Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Activity

[iPadOS 18] A series of TabBar questions
Hello, I am currently implementing the new iPadOS 18 TabBar with the new Tab API, but there are several things that don't look very good or aren't operating quite the way I'd hope and would like to know if there is a way around them or to change them. Here is what my sidebar for the TabBar looks like: My questions: The tabViewSidebarBottomBar isn't actually at the "bottom". Instead there is a gap below which you can see the sidebar scrolling. Is there a way to get the tabViewSidebarBottomBar actually at the bottom? Is there a way to make it so that the sections themselves are also reorderable? Can we turn scrollIndicators to .never or are we stuck with them being on? Is there any way at all to make the SF Symbols in the sidebar render in another color mode, particularly .palette? Is it possible to allow sections in between plain tabs? Like I want Dashboard, then the whole Events section, then the rest can continue. On iPhone why are sections not honored at all, in any way? Like even if they weren't going to be expandable menus, at least treating it like a list section would allow some visual hierarchy?! Bonus Question: Any way to make the tabViewSidebarHeader sticky so it's always at the top? Thank you in advance!
1
0
765
Oct ’24
In a Form's Section, how to automatically dismiss the keyboard when clicking a NavigationLink to enter a child view, and ensure the keyboard does not pop up again when returning to the main view.
The main view contains a Form with multiple Sections, including TextFields and NavigationLinks. I used an @FocusState variable to automatically pop up the keyboard when entering the main view, making it easier for users to input content directly in the TextField. However, when I keep the keyboard open and click a NavigationLink in the Section to enter a child view, I can't find a way to automatically dismiss the keyboard upon entering the child view. I've seen several official Apple apps, like Reminders, achieve this. Not dismissing the keyboard causes it to automatically pop up again when returning to the main view, along with triggering a "Unable to simultaneously satisfy constraints" error in the console. I asked GPT for help, but the suggestions, like using simultaneousGesture or adding isActive to the NavigationLink (which might be deprecated), haven't worked. Has anyone in iOS development encountered this issue?
1
0
353
Oct ’24
How to permanently move Command+A focus from SideBar to DetailView?
My NavigationSplitView is very simple. The DetailView contains Table populated with data. SideBar populated with items that act as a filter for Table content. If I'm on the SideBar and hit Command-A, I never want to select everything in the sidebar. I always want to select all the content for a detail view. This is how Finder works. I tried to set List(...) { ... } .focusable(false) When I launch the application, Command-A works exactly as I would like. But when I select another "filter" in sidebar with the mouse, the List becomes focusable.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
0
0
261
Oct ’24
UITextView changed sequence of views in iOS 18
UITextView changed the sequence of views and on the top UIView. As a result in the custom gesture class in "touchesBegan" method, this code brings me "let touchedView = touches.first?.view" type of view UIView but it must be my own custom class type. How can I get a tapped type of view? Help please override func touchesBegan(_ touches: Set, with event: UIEvent) { super.touchesBegan(touches, with: event) let touchedView = touches.first?.view }
1
0
388
Sep ’24
SwiftUI - WatchKit TextField Keyboard closed immediately before entering text
The TextField Shows and When selected opens the keyboard very slowly and before entering text closes immediately with these messages. <PUICQuickboardController: 0x6000029111f0> [0x6000029111f0-1] presentation watchdog expired! <PUICQuickboardController: 0x6000029111f0> [0x6000029111f0-1] is no longer the current presentation, we must have timed out... This App was working fine in Simulator and test devices until recently.
0
0
361
Oct ’24
dismissWindow alternative for macOS 13?
Currently for my SwiftUI application i'm using dismissWindow() to close my windows. However, I want to make my app compatible on macOS 13 to enable a wider audience. My current usage of this function is as follows: func reloadContentViewAfterDelete() { @Environment(\.openWindow) var openWindow @Environment(\.dismissWindow) var dismissWindow dismissWindow(id: "content") openWindow(id: "content") }
1
0
358
Sep ’24
macOS Sequoia SwiftUI crash
I encountered a crash with my SwiftUI app for macOS. NSInvalidArgumentException: -[MTLIGAccelRenderCommandEncoder setVertexBuffer:offset:attributeStride:atIndex:]: unrecognized selector sent to instance struct ContentView: View { @State var text = "" var body: some View { TextEditor(text: $text) } } When I run macOS app, console print NSBundle file:///System/Library/PrivateFrameworks/MetalTools.framework/ principal class is nil because all fallbacks have failed and I found it crash with TextEditor. Can somebody help me?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
3
0
990
Oct ’24
MapkitJS - not able to click on PointOfInterest and use it for actions
Hi, Have been trying to work with MapkitJS for a website, but I'm stumped on once basic capability: I want to be able to click on a point of interest, and perform some actions such as: Get its coordinates Attach an annotation to it (e.g. a callout) In my code, PointOfInterest's are selectable: map.selectableMapFeatures = [ mapkit.MapFeatureType.PointOfInterest, ]; But when I click on one, I do see the marker pop up but nothing else (which is not much help since there is no additional information in the marker itself). I see no event getting triggered that I can do something with. I am using an event listener as follows: map.addEventListener('single-tap', (event) => { const coordinate = map.convertPointOnPageToCoordinate(event.pointOnPage); console.log('Map tapped at:', coordinate); console.log('Map tapped event:', event); ... I guess I have to grab the Place ID somehow but I don't know how to. Thanks for any help.
0
0
541
Nov ’24
DeviceActivityReport - Slow Synchronization. Screen time api, Family Controls
Hello! I am developing an app using the Screen Time API. Everything is good, but I have a problem with DeviceActivityReport. On the child’s device, stats are synced to the app in about 1-5 minutes. However, on the parent’s device, it can take around an hour or more. How can I make the stats sync faster between the child’s device and the parent’s device? How I Implemented It @Published var context: DeviceActivityReport.Context = .init("Time Limit") let filter = DeviceActivityFilter( segment: .daily( during: Calendar.current.dateInterval(of: .day, for: .now)! ), users: .children, devices: .all, applications: applicationTokens ) DeviceActivityReport(viewModel.context, filter: viewModel.filter) .frame(maxHeight: viewModel.maxReportHeight) In the Report Extension (Test Code) for await d in data { result += "device.name=\(d.device.name ?? "")" result += "\nuser.appleID=\(d.user.appleID ?? "")" result += "\ngivenName=\(d.user.nameComponents?.givenName ?? "")" result += "\nrole=\(d.user.role.rawValue)" for await activity in d.activitySegments { result += "\nactivitySegments hashValue=\(activity.hashValue)" result += "\ntotalActivityDuration=\(activity.totalActivityDuration)" result += "\ndateInterval=\(activity.dateInterval)" for await category in activity.categories { result += "\ncategory=\(category.category.localizedDisplayName ?? "")" for await app in category.applications { result += "\napp=\(app.application.bundleIdentifier ?? ""), time=\(app.totalActivityDuration)" } } } } Problems: activity.categories can be empty for a long time app.totalActivityDuration contains outdated information I need to wait about 1+ hours to get “actual” information.
1
0
491
Oct ’24
Identify what is being dragged globally
I would like to implement the same kind of behavior as the Dropoverapp application, specifically being able to perform a specific action when files are dragged (such as opening a window, for example). I have written the code below to capture the mouse coordinates, drag, drop, and click globally. However, I don't know how to determine the nature of what is being dropped. Do you have any ideas on how to detect the nature of what is being dragged outside the application's scope? Here is my current code: import SwiftUI import CoreGraphics struct ContentView: View { @State private var mouseX: CGFloat = 0.0 @State private var mouseY: CGFloat = 0.0 @State private var isClicked: Bool = false @State private var isDragging: Bool = false private var mouseTracker = MouseTracker.shared var body: some View { VStack { Text("Mouse coordinates: \(mouseX, specifier: "%.2f"), \(mouseY, specifier: "%.2f")") .padding() Text(isClicked ? "Mouse is clicked" : "Mouse is not clicked") .padding() Text(isDragging ? "Mouse is dragging" : "Mouse is not dragging") .padding() } .frame(width: 400, height: 200) .onAppear { mouseTracker.startTracking { newMouseX, newMouseY, newIsClicked, newIsDragging in self.mouseX = newMouseX self.mouseY = newMouseY self.isClicked = newIsClicked self.isDragging = newIsDragging } } } } class MouseTracker { static let shared = MouseTracker() private var eventTap: CFMachPort? private var runLoopSource: CFRunLoopSource? private var isClicked: Bool = false private var isDragging: Bool = false private var callback: ((CGFloat, CGFloat, Bool, Bool) -> Void)? func startTracking(callback: @escaping (CGFloat, CGFloat, Bool, Bool) -> Void) { self.callback = callback let mask: CGEventMask = (1 << CGEventType.mouseMoved.rawValue) | (1 << CGEventType.leftMouseDown.rawValue) | (1 << CGEventType.leftMouseUp.rawValue) | (1 << CGEventType.leftMouseDragged.rawValue) // Pass 'self' via 'userInfo' let selfPointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) guard let eventTap = CGEvent.tapCreate( tap: .cghidEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: mask, callback: MouseTracker.mouseEventCallback, userInfo: selfPointer ) else { print("Failed to create event tap") return } self.eventTap = eventTap runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0) CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) CGEvent.tapEnable(tap: eventTap, enable: true) } // Static callback function private static let mouseEventCallback: CGEventTapCallBack = { _, eventType, event, userInfo in guard let userInfo = userInfo else { return Unmanaged.passUnretained(event) } // Retrieve the instance of MouseTracker from userInfo let tracker = Unmanaged<MouseTracker>.fromOpaque(userInfo).takeUnretainedValue() let location = NSEvent.mouseLocation // Update the click and drag state switch eventType { case .leftMouseDown: tracker.isClicked = true tracker.isDragging = false case .leftMouseUp: tracker.isClicked = false tracker.isDragging = false case .leftMouseDragged: tracker.isDragging = true default: break } // Call the callback on the main thread DispatchQueue.main.async { tracker.callback?(location.x, location.y, tracker.isClicked, tracker.isDragging) } return Unmanaged.passUnretained(event) } }
0
0
487
Oct ’24
iOS 18 SwiftUI sheet resizing from page to form presentation sizing
I'm presenting a UIKit view controller from SwiftUI in a sheet using UIViewControllerRepresentable. The size of the sheet is being set to PagePresentationSizing using the new iOS 18 presentationSizing method. When Split View is used and the size class changes from regular to compact, the sheet resizes as expected to fit in the smaller window. When the app returns to full screen and the size class changes back to regular, the sheet is now displayed using FormPresentationSizing instead of PagePresentationSizing. This all worked as expected in iOS 17 with the view controller modalPresentationStyle being specified in the UIViewControllerRepresentable implementation, but the behaviour has now changed with iOS 18. How do I preserve the desired presentation sizing of the sheet? Thanks for any help.
2
0
2.3k
Sep ’24
Cocoa Binding for custom class
So I was trying to use an NSArrayController to bind the contents of a property , first I tried using NSDictionary and it worked great, here's what I did: @interface ViewController : NSViewController @property IBOutlet ArrayController * tableCities; @end ... @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSString* filePath = @"/tmp/city_test.jpeg"; NSDictionary *obj = @{@"image": [[NSImage alloc] initByReferencingFile:filePath], @"name": @"NYC", @"filePath": filePath}; NSDictionary *obj2 = @{@"image": [[NSImage alloc] initByReferencingFile:filePath], @"name": @"Chicago", @"filePath": filePath}; NSDictionary *obj3 = @{@"image": [[NSImage alloc] initByReferencingFile:filePath], @"name": @"Little Rock", @"filePath": filePath}; [_tableCities addObjects:@[obj, obj2, obj3]]; } @end Now for an NSPopUpButton, binding the Controller Key to the ArrayController and the ModelKeyPath to "name" works perfectly and the popupbutton will show the cities as I expected. But now, instead of using an NSDictionary I wanted to use a custom class for these cities along with an NSMutableArray which holds the objects of this custom class. I'm having some trouble going about this.
0
0
419
Oct ’24
How to transition & resize a view from one view controller to another using UIKit & Snapshots?
I am trying to seamlessly transition a view from one view controller and have it "jump" into a sheet. Apple does this in their widget picker for example when you add one of the top widgets. Based on all the documentation I've read, the most common approach to this is to snapshot the area/view that you are trying to seamlessly transition in the presented view controller (the place where the item is coming from. And then have that snapshot translate across into where it should be in the layout of the presenting view controller. func makeAnimatorIfNeeded( using transitionContext: UIViewControllerContextTransitioning ) -> UIViewPropertyAnimator { if let animator = animator { return animator } // Presentation animation let animator = UIViewPropertyAnimator( duration: 0.5, timingParameters: UISpringTimingParameters(dampingRatio: 1.0) ) guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else { transitionContext.completeTransition(false) return animator } /// !IMPORTANT: For some weird reason, accessing the views of the view controller /// directly, like `fromVC.view` or `toVC.view` will break the sheet transition and gestures. /// Instead, use the `view(forKey:)` method of the `transitionContext` to get the views. let fromView = transitionContext.view(forKey: .from) let toView = transitionContext.view(forKey: .to) let containerView = transitionContext.containerView let fromFrame = transitionContext.viewController(forKey: .from).map { transitionContext.finalFrame(for: $0) } ?? containerView.bounds let toFrame = transitionContext.viewController(forKey: .to).map { transitionContext.finalFrame(for: $0) } ?? containerView.bounds // Calculate the frame of sourceView relative to fromVC.view to capture the correct snapshot area. let snapshotFrameInFromVC = sourceView?.convert(sourceView?.frame ?? .zero, to: fromVC.view) ?? containerView.frame // Calculate the frame of sourceView relative to containerView to position the snapshot correctly. let snapshotFrameInContainer = sourceView?.convert(sourceView?.frame ?? .zero, to: containerView) ?? containerView.frame let snapshot: UIView? if isPresenting { // Create a snapshot of fromVC.view from the defined area (snapshotFrameInFromVC). let originalColor = fromVC.view.backgroundColor fromVC.view.backgroundColor = .clear snapshot = fromVC.view.resizableSnapshotView(from: snapshotFrameInFromVC, afterScreenUpdates: true, withCapInsets: .zero) fromVC.view.backgroundColor = originalColor // Position the snapshot correctly within the snapshot container snapshot?.frame = snapshotFrameInContainer toView?.frame = toFrame toView?.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height) toView?.layoutIfNeeded() if let fromView { containerView.addSubview(fromView) } if let toView { containerView.addSubview(toView) containerView.addSubview(snapshot ?? UIView()) } let toViewCenter = CGPoint( x: toVC.view.bounds.midX, y: toVC.view.bounds.midY + 55 ) let gestureVelocity = CGPoint(x: 0, y: -5000) animator.addAnimations { Wave.animate( withSpring: self.animatedSpring, mode: .animated, gestureVelocity: gestureVelocity ) { snapshot?.animator.frame.size = CGSize(width: 204, height: 204) // Important to animate first snapshot?.animator.center = toViewCenter } completion: { finished, retargeted in print("finished: \(finished), retargeted: \(retargeted)") } toView?.transform = CGAffineTransform.identity } animator.addCompletion { animatingPosition in switch animatingPosition { case .end: snapshot?.removeFromSuperview() transitionContext.completeTransition(true) default: transitionContext.completeTransition(false) } } } else { // Transitioning view is fromView if let toView { containerView.addSubview(toView) } if let fromView { containerView.addSubview(fromView) } animator.addAnimations { fromView?.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height) } animator.addCompletion { animatingPosition in switch animatingPosition { case .end: transitionContext.completeTransition(true) default: transitionContext.completeTransition(false) } } } self.animator = animator return animator } I can pull this off seamlessly if the animation involves a simple movement from A to B. But if I try to resize the snapshot as well, the snapshot becomes pixelated and low quality (I assume this is because the snapshot is literally a small screenshot and I'm stretching it from 56x56 to 204x204). Is there another way that I'm overlooking to pull this off without a snapshot? Or is there a way I can resize the snapshot without losing quality? Here is the animator code, it works great without the sizing so this should help future people looking to replicate the same effect anyways.
Topic: UI Frameworks SubTopic: UIKit Tags:
0
0
290
Sep ’24
After interacting with a button in the live activity, the Control widget and home screen widget did not update.
I have a button on a live activity, and I want to end the live activity and update the Control Center widgets and home screen widgets after clicking this button. So, in the perform() method of the button's LiveActivityIntent, I called the methods ControlCenter.shared.reloadAllControls() and WidgetCenter.shared.reloadAllTimelines(), but they didn't work. Home screen widgets and Control Center widgets do not refresh, resulting in a poor user experience.
1
0
415
Sep ’24
Scale contextMenu preview size to containing content
Hi! I have a multi-line string I'd like to display as text in a long-press preview (using .contextMenu). However, text after 3rd or 4th linebreak (\n) gets clipped. The intended effect is to have the minimum preview size that can fit all of the text. Tried .frame(maxWidth: .infinity, maxHeight: .infinity) on Text() but it didn't have any effect. The only modifier that somewhat works is .containerRelativeFrame([.horizontal, .vertical]) but this gives the maximum preview size, instead of minimum. Any suggestions? TIA. struct RedditView: View { @State private var text = "AAAAAAAAAAAAAAAAAAAAAA?\n\nBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\nCCCCCCCCCCCCCCCCCCCCCCCCCC" var body: some View { Text("Long press this") .frame(width: 300, height: 100) .contentShape(.rect) .border(Color.blue) .contextMenu(menuItems: { Button { // do something } label: { Label { Text("Edit") } icon: { Image(systemName: "pencil") } } }, preview: { Text(text) // .frame(maxWidth: .infinity, maxHeight: .infinity) .multilineTextAlignment(.center) .padding() //.containerRelativeFrame([.horizontal, .vertical]) } ) } }
0
0
633
Sep ’24
Share Sheet Behavior on iPhone
Hello! I hope you are all doing well. The reason for this post is to ask about the Share Sheet behavior, as I am experiencing a double Share Sheet behavior where in iOS 14.6 I give the order to download 2 files which open 2 share pop-ups (2 Share Sheets), but in iOS 17.6 it only opens once to download each file. Do you know if this changed at some point between iOS versions and why? I leave an example image of the behavior in iOS 14.6:
Topic: UI Frameworks SubTopic: General
1
0
403
Oct ’24
Bug and crash when changing tabs in SwiftUI on the mac.
I like how the TabView control looks on the mac and ipad and decided to see if my current code can show the tabs in my multi-platform app just to realize that whenever I click on one of those tabs my macOS app currently crashes every time I press any other tab with the error: Thread 1: "NSToolbar 0x600003de33c0 already contains an item with the identifier com.apple.SwiftUI.navigationSplitView.toggleSidebar. Duplicate items of this type are not allowed." While trying to troubleshoot I noticed several other people have had a similar issue with differing reasons (toolbars and searchers mentioned) all in macOS since upgrading to 15.0: https://forums.developer.apple.com/forums/thread/763829 Minimal Viable Project: to show the issue I commented out most of my code calls hoping to create a project that worked so I could bring my code back in and see if it broke. I still had this issue so I next created a minimal viable example. Here it is: import SwiftUI import SwiftData @Model class Issue { var name: String init(name: String) { self.name = name } } @main struct TestMultiplatformApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([ Issue.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } struct ContentView: View { @State var isCompact: Bool = true var body: some View { VStack { if isCompact { EntryTab() } else { EntrySidebar() } Toggle(isOn: $isCompact, label: { Text(isCompact ? "TabView" : "Sidebar") }) .padding(.horizontal, 20) .padding(.bottom, 20) } } } public struct tabControl: Identifiable, Hashable, Sendable { public static func == (lhs: tabControl, rhs: tabControl) -> Bool { lhs.id < rhs.id } public var id: Int // Tab Number public var displayName: String public init(id: Int, displayName: String) { self.id = id self.displayName = displayName } } struct EntryTab: View { let entryTabs = [ tabControl(id: 0, displayName: "row 0"), tabControl(id: 1, displayName: "row 1"), tabControl(id: 2, displayName: "row 2"), tabControl(id: 3, displayName: "row 3") ] @State private var selectedTab: Int = 0 var body: some View { TabView(selection: $selectedTab) { ForEach(entryTabs) { tabCtrl in NavigationSplitView { Text("Selected tab is \(selectedTab)") } detail: { Text("Choose item from sidebar... in future this would be content") } .tabItem { Text(tabCtrl.displayName) } .tag(tabCtrl.id) } } } } struct EntrySidebar: View { @State private var selectedTabID: Int? let entryTabs = [ tabControl(id: 0, displayName: "row 0"), tabControl(id: 1, displayName: "row 1"), tabControl(id: 2, displayName: "row 2"), tabControl(id: 3, displayName: "row 3") ] var body: some View { NavigationSplitView(sidebar: { List(entryTabs, id:\.id, selection: $selectedTabID) { thisItem in Text(thisItem.displayName) } }, content: { Text("Hi selected tab: \(String(describing: selectedTabID))") }, detail: { Text("Choose item from sidebar... in future this would be content") }) .onAppear() { // Set the selected tab selectedTabID = 1 } } }
0
0
446
Sep ’24