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

Created

Regression in UITabBarController on Catalyst when building under Xcode 16
I am running into an issue with UITabBarController in a Catalyst app when building under Xcode 16 and running on macOS 15. If a UITabBarController is used, the tabs are presented in an unwanted title/toolbar at the top of the window. If you have an app where your views run to the top of the window, this can obscure the content and controls that are near the top. I created a sample application that is attached to the Feedback record (FB14293963). When building under Xcode 15, this is what the app looks like: Under Xcode 16, it looks like this: Beyond this simple example, using UITabBarController in a presented view controller can result in the tabs not showing at all. Also, If you switch the view in the main window to something that isn't a UITabBarController, the tabs still remain at the top. This seems to stem from the tab bar/sidebar changes for iPadOS 18. While this approach can work for simpler apps, it may not work well at all for more complex apps and there really needs to be a way to opt out of this behavior so apps where it is not suited for can still take advantage of features in iPadOS/Catalyst 18. Has anyone discovered a workaround or way to disable the new tab bar behavior with having to write their own version of UITabBarController?
2
0
1.4k
Jul ’24
Combining NavigationSplitView and TabView in iOS 18
Hi folks, I've used a NavigationSplitView within one of the tabs of my app since iOS 16, but with the new styling in iOS 18 the toolbar region looks odd. In other tabs using e.g. simple stacks, the toolbar buttons are horizontally in line with the new tab picker, but with NavigationSplitView, the toolbar leaves a lot of empty space at the top (see below). Is there anything I can do to adjust this, or alternatively, continue to use the old style? Thanks!
12
3
2.9k
Jul ’24
iOS 18 beta bug: NavigationStack pushes the same view twice
Hello, community and Apple engineers. I need your help. Our app has the following issue: NavigationStack pushes a view twice if the NavigationStack is inside TabView and NavigationStack uses a navigation path of custom Hashable elements. Our app works with issues in Xcode 18 Beta 13 + iOS 18.0. The same issue happened on previous beta versions of Xcode 18. The issue isn’t represented in iOS 17.x and everything worked well before iOS 18.0 beta releases. I was able to represent the same issue in a clear project with two simple views. I will paste the code below. Several notes: We use a centralised routing system in our app where all possible routes for navigation path are implemented in a View extension called withAppRouter(). We have a enum RouterDestination that contains all possible routes and is resolved in withAppRouter() extension. We use Router class that contains @Published var path: [RouterDestination] = [] and this @Published property is bound to NavigationStack. In the real app, we need to have an access to this path property for programmatic navigation purposes. Our app uses @ObservableObject / @StateObject approach. import SwiftUI struct ContentView: View { @StateObject private var router = Router() var body: some View { TabView { NavigationStack(path: $router.path) { NavigationLink(value: RouterDestination.next, label: { Label("Next", systemImage: "plus.circle.fill") }) .withAppRouter() } } } } enum RouterDestination: Hashable { case next } struct SecondView: View { var body: some View { Text("Screen 2") } } class Router: ObservableObject { @Published var path: [RouterDestination] = [] } extension View { func withAppRouter() -> some View { navigationDestination(for: RouterDestination.self) { destination in switch destination { case .next: return SecondView() } } } } Below you can see the GIF with the issue: What I tried to do: Use iOS 17+ @Observable approach. It didn’t help. Using @State var path: [RouterDestination] = [] directly inside View seems to help. But it is not what we want as we need this property to be @Published and located inside Router class where we can get an access to it, and use for programmatic navigation if needed. I ask Apple engineers to help with that, please, and if it is a bug of iOS 18 beta, then please fix it in the next versions of iOS 18.0
12
4
2k
Jul ’24
SwiftUI Gestures prevent scrolling with iOS 18
I added gesture support to my app that supports iOS 16 and 17 and have never had issues with it. However, when I compiled my app with Xcode 16 I immediately noticed a problem with the app when I ran it in the simulator. I couldn't scroll up or down. I figured out it’s because of my gesture support. My gesture support is pretty simple. let myDragGesture = DragGesture() .onChanged { gesture in self.offset = gesture.translation } .onEnded { _ in if self.offset.width > threshold { ...some logic } else if self.offset.width < -threshold { ...some other logic } logitUI.debug("drag gesture width was \(self.offset.width)") self.offset = .zero } If I pass nil to .gesture instead of myDragGesture then scrolling starts working again. Here’s some example output when I’m trying to scroll down. These messages do NOT appear when I run my app on an iOS 16/17 simulator with Xcode 15. drag gesture width was 5.333328 drag gesture width was -15.333344 drag gesture width was -3.000000 drag gesture width was -24.333328 drag gesture width was -30.666656 I opened FB14205678 about this.
17
21
10k
Jul ’24
CoreText' CTRunDraw can't draw underline attribute in iOS18 with Xcode 16 beta
demo code : - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); // Flip the coordinate system CGContextSetTextMatrix(context, CGAffineTransformIdentity); CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0); NSDictionary *attrs = @{NSFontAttributeName: [UIFont systemFontOfSize:20], NSForegroundColorAttributeName: [UIColor blueColor], NSUnderlineStyleAttributeName: @(NSUnderlineStyleThick), }; // Make an attributed string NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"Hello CoreText!" attributes:attrs]; CFAttributedStringRef attributedStringRef = (__bridge CFAttributedStringRef)attributedString; // Simple CoreText with CTFrameDraw CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedStringRef); CGPathRef path = CGPathCreateWithRect(self.bounds,NULL); CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0, 0),path,NULL); //CTFrameDraw(frame, context); // You can comment the line 'CTFrameDraw' and use the following lines // draw with CTLineDraw CFArrayRef lines = CTFrameGetLines(frame); CGPoint lineOrigins[CFArrayGetCount(lines)]; CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins); for (int i = 0; i < CFArrayGetCount(lines); i++) { CTLineRef line = CFArrayGetValueAtIndex(lines, i); CGContextSetTextPosition(context, lineOrigins[i].x, lineOrigins[i].y); // CTLineDraw(line, context); // You can comment the line 'CTLineDraw' and use the following lines // draw with CTRunDraw // use CTRunDraw will lost some attributes like NSUnderlineStyleAttributeName, // so you need draw it by yourself CFArrayRef runs = CTLineGetGlyphRuns(line); for (int j = 0; j < CFArrayGetCount(runs); j++) { CTRunRef run = CFArrayGetValueAtIndex(runs, j); CTRunDraw(run, context, CFRangeMake(0, 0)); } } } this code will use CTRunDraw to draw the content , and the underline will draw and show normally in iOS17 & Xcode 15 , But when you build it with XCode16 & iOS18 beta . the underline will be missing .
2
4
762
Jul ’24
SF Symbols 6 app does not export 'Can Rotate' property of a layer
The SF Symbols app 6.0 (99) does not export the ‘Can Rotate’ property of layers when exporting a symbol via File > Export Symbol. Without this, all the new fantastic edit functions in the app related to rotating in SF Symbols is completely useless. This issue with the SF Symbols 6 app can be reproduced by exporting a rotatable symbol like fan.desk, and then by importing the result as custom symbol. When inspecting ‘Group 1’ of the imported symbol, it is no longer marked as rotatable. SF Symbols app 6.0 is still in beta, but hasn't been updated since 10 June. Hopefully this bug will be solved in the release version, or earlier. Does anyone know how to manually add the missing rotation info to the exported SVG file? In case an Apple engineer reads this: FB13916635
4
2
796
Jul ’24
onContinueUserActivity(CSSearchableItemActionType, perform) does not work on a SwiftUI macOS app
onContinueUserActivity(CSSearchableItemActionType, perform) works as expected on iOS when we search and select an item from Spotlight, but nothing happens when we do the same on a SwiftUI macOS app. var body: some Scene { WindowGroup { MyView() .onContinueUserActivity(CSSearchableItemActionType, perform: handleSpotlight) } } func handleSpotlight(_ userActivity: NSUserActivity) { // Is not called... } How can we respond to a user clicking a Spotlight result from our apps on macOS?
3
1
918
Jul ’24
@Observable state class reinitializes every time view is updated
With the new @Observable macro, it looks like every time the struct of a view is reinitialized, any observable class marked as @State in the struct also gets reinitialized. Moreover, the result of the reinitialization immediately gets discarded. This is in contrast to @StateObject and ObservableObject, where the class would only be initialized at the first creation of the view. The initialization method of the class would never be called again between view updates. Is this a bug or an expected behavior? This redundant reinitialization causes performance issues when the init method of the observable class does anything slightly heavyweight. Feedback ID: FB13697724
3
1
1k
Jul ’24
Control Center widget won't show snippet view
Has anyone been able to create a Control Center widget that opens a snippet view? There are stock Control Center widgets that do this, but I haven't been able to get it to work. Here's what I tried: struct SnippetButton: ControlWidget { var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind: "xxx.xxx.snippetWidget" ) { ControlWidgetButton(action: SnippetIntent()) { Label("Show Snippet", systemImage: "map.fill") } } .displayName(LocalizedStringResource("Show Snippet")) .description("Show a snippet.") } } struct SnippetIntent: ControlConfigurationIntent { static var title: LocalizedStringResource = "Show a snippet" static var description = IntentDescription("Show a snippet with some text.") @MainActor func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView { return .result(dialog: IntentDialog("Hello!"), view: SnippetView()) } } struct SnippetView: View { var body: some View { Text("Hello!") } }
4
2
877
Jul ’24
NSSavePanel accessory view doesn't react to enter keypress
I have a textfield in accessory view of NSSavePanel. For user convenience there are default actions supported natively by macOS (such as pressing Enter, keyEquivalent). However this doesn't work for enter under Sonoma. Escape key works. Is enter keypress dangerous for malicious actors so it's not supported? I have workaround below but I am not confident if I am not violating sandbox (future proof). Original code demonstrating the issue: class ViewController: NSViewController, NSTextFieldDelegate, NSControlTextEditingDelegate { let savePanel = NSSavePanel() override func viewDidLoad() { super.viewDidLoad() let customView = NSView() let textField = NSTextField(string: "11111111") textField.delegate = self // to get focus using tab keypress savePanel.accessoryView = textField } override func viewWillAppear() { savePanel.runModal() } } Workaround: // variable set to true in delegate method controlTextDidEndEditing var didUseTextFieldWithEnterPressed = false override func performKeyEquivalent(with event: NSEvent) -> Bool { if #unavailable(macOS 14) { return super.performKeyEquivalent(with: event) } guard let panel, didUseTextFieldWithEnterPressed == true, event.type == .keyDown && (event.keyCode == UInt16(kVK_Return) || event.keyCode == UInt16(kVK_ANSI_KeypadEnter)) else { return super.performKeyEquivalent(with: event) } return panel.performKeyEquivalent(with: event) }
1
0
616
Aug ’24
How can we performantly scroll to a target location using TextKit 2?
How can we performantly scroll to a target location using TextKit 2? Hi everyone, I'm building a custom text editor using TextKit 2 and would like to scroll to a target location efficiently. For instance, I would like to move to the end of a document seamlessly, similar to how users can do in standard text editors by using CMD + Down. Background: NSTextView and TextEdit on macOS can navigate to the end of large documents in milliseconds. However, after reading the documentation and experimenting with various ideas using TextKit 2's APIs, it's not clear how third-party developers are supposed to achieve this. My Code: Here's the code I use to move the selection to the end of the document and scroll the viewport to reveal the selection. override func moveToEndOfDocument(_ sender: Any?) { textLayoutManager.ensureLayout(for: textLayoutManager.documentRange) let targetLocation = textLayoutManager.documentRange.endLocation let beforeTargetLocation = textLayoutManager.location(targetLocation, offsetBy: -1)! textLayoutManager.textViewportLayoutController.layoutViewport() guard let textLayoutFragment = textLayoutManager.textLayoutFragment(for: beforeTargetLocation) else { return } guard let textLineFragment = textLayoutFragment.textLineFragment(for: targetLocation, isUpstreamAffinity: true) else { return } let lineFrame = textLayoutFragment.layoutFragmentFrame let lineFragmentFrame = textLineFragment.typographicBounds.offsetBy(dx: 0, dy: lineFrame.minY) scrollToVisible(lineFragmentFrame) } While this code works as intended, it is very inefficient because ensureLayout(_:) is incredibly expensive and can take seconds for large documents. Issues Encountered: In my attempts, I have come across the following two issues. Estimated Frames: The frames of NSTextLayoutFragment and NSTextLineFragment are approximate and not precise enough for scrolling unless the text layout fragment has been fully laid out. Laying out all text is expensive: The frames become accurate once NSTextLayoutManager's ensureLayout(for:) method has been called with a range covering the entire document. However, ensureLayout(for:) is resource-intensive and can take seconds for large documents. NSTextView, on the other hand, accomplishes the same scrolling to the end of a document in milliseconds. I've tried using NSTextViewportLayoutController's relocateViewport(to:) without success. It's unclear to me whether this function is intended for a use case like mine. If it is, I would appreciate some guidance on its proper usage. Configuration: I'm testing on macOS Sonoma 14.5 (23F79), Swift (AppKit), Xcode 15.4 (15F31d). I'm working on a multi-platform project written in AppKit and UIKit, so I'm looking for either a single solution that works in both AppKit and UIKit or two solutions, one for each UI framework. Question: How can third-party developers scroll to a target location, specifically the end of a document, performantly using TextKit 2? Steps to Reproduce: The issue can be reproduced using the example project (download from link below) by following these steps: Open the example project. Run the example app on a Mac. The example app shows an uneditable text view in a scroll view. The text view displays a long text. Press the "Move to End of Document" toolbar item. Notice that the text view has scrolled to the bottom, but this took several seconds (~3 seconds on my MacBook Pro 16-inch, 2021). The duration will be shown in Xcode's log. You can open the ExampleTextView.swift file and find the implementation of moveToEndOfDocument(_:). Comment out line 84 where the ensureLayout(_:) is called, rerun the app, and then select "Move to End of Document" again. This time, you will notice that the text view moves fast but does not end up at the bottom of the document. You may also open the large-file.json in the project, the same file that the example app displays, in TextEdit, and press CMD+Down to move to the end of the document. Notice that TextEdit does this in mere milliseconds. Example Project: The example project is located on GitHub: https://github.com/simonbs/apple-developer-forums/tree/main/how-can-we-performantly-scroll-to-a-target-location-using-textkit-2 Any advice or guidance on how to achieve this with TextKit 2 would be greatly appreciated. Thanks in advance! Best regards, Simon
10
9
2.8k
Aug ’24
UITextView's pressesBegan isn't triggered by the software keyboard
I'm building a SwiftUI app with a UITextView subclass, and it seems that the software keyboard doesn't trigger the pressesBegan or pressesEnded functions of UITextView. With a hardware keyboard, pressesBegan works as expected, allowing us to intercept key presses in our subclass. I can't find any documentation about this, or any other forum posts (here or on Stack Overflow) that talk about a discrepancy between software and hardware keyboard behaviors, and I can't believe this is an intended behavior. Our app is a SwiftUI app, in case that's relevant. Does anyone have any guidance? Is this a bug or am I not understanding this API? Any information or work arounds would be greatly appreciated. I've made a sample project that demonstrates this issue, which you can grab from GitHub at https://github.com/nyousefi/KeyPressSample. To see this in action, run the sample project and start pressing keys. The hardware keyboard will print the key press at the top of the screen (above the text view), while the software keyboard won't.
3
0
736
Aug ’24
iOS18 Control Widget custom symbol preview failed
i export apple SF as custom sf for test. code is simple: var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind:"ControlWidgetConfiguration" ) { ControlWidgetButton(action: DynamicWidgetIntent()) { Text("test") Image("custom_like") } }.displayName("test") } as we can see, it can't show image in the preview. but it can show image in the Control widget center. am i do some thing wrong?
5
7
1.1k
Aug ’24
tvOS 18.0 Siri back button behavior bug
On testing my app with tvOS 18, I have noticed the Siri Remote back button no longer provides system-provided behavior when interacting with tab bar controller pages. Instead of moving focus back to the tab bar when pressed, the back button will close the app, as if the Home button was pressed. This occurs both on device and in the Simulator. Create tvOS project with a tab bar controller. Create pages/tabs which contain focusable items (ie. buttons) Scroll down to any focusable item (ie. a button or UICollectionView cell) Hit the Siri Remote back button. See expect behavior below: Expected behavior: System-provided behavior should move focus back to the tab bar at the top of the screen. Actual results: App is closed and user is taken back to the Home Screen. Has anyone else noticed this behavior?
8
3
1.3k
Aug ’24
iOS 18 hit testing functionality differs from iOS 17
I created a Radar for this FB14766095, but thought I would add it here for extra visibility, or if anyone else had any thoughts on the issue. Basic Information Please provide a descriptive title for your feedback: iOS 18 hit testing functionality differs from iOS 17 What type of feedback are you reporting? Incorrect/Unexpected Behavior Description: Please describe the issue and what steps we can take to reproduce it: We have an issue in iOS 18 Beta 6 where hit testing functionality differs from the expected functionality in iOS 17.5.1 and previous versions of iOS. iOS 17: When a sheet is presented, the hit-testing logic considers subviews of the root view, meaning the rootView itself is rarely the hit view. iOS 18: When a sheet is presented, the hit-testing logic changes, sometimes considering the rootView itself as the hit view. Code: import SwiftUI struct ContentView: View { @State var isPresentingView: Bool = false var body: some View { VStack { Text("View One") Button { isPresentingView.toggle() } label: { Text("Present View Two") } } .padding() .sheet(isPresented: $isPresentingView) { ContentViewTwo() } } } #Preview { ContentView() } struct ContentViewTwo: View { @State var isPresentingView: Bool = false var body: some View { VStack { Text("View Two") } .padding() } } extension UIWindow { public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { /// Get view from superclass. guard let hitView = super.hitTest(point, with: event) else { return nil } print("RPTEST rootViewController = ", rootViewController.hashValue) print("RPTEST rootViewController?.view = ", rootViewController?.view.hashValue) print("RPTEST hitView = ", hitView.hashValue) if let rootView = rootViewController?.view { print("RPTEST rootViewController's view memory address: \(Unmanaged.passUnretained(rootView).toOpaque())") print("RPTEST hitView memory address: \(Unmanaged.passUnretained(hitView).toOpaque())") print("RPTEST Are they equal? \(rootView == hitView)") } /// If the returned view is the `UIHostingController`'s view, ignore. print("MTEST: hitTest rootViewController?.view == hitView", rootViewController?.view == hitView) print("MTEST: -") return hitView } } Looking at the print statements from the provided sample project:
 iOS 17 presenting a sheet from a button tap on the ContentView(): RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fd25000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fd25000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false iOS 17 dismiss from presented view: RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fe04080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fe04080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false iOS 18 presenting a sheet from a button tap on the ContentView(): RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103342080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x000000010333e3c0 RPTEST Are they equal? true MTEST: hitTest rootViewController?.view == hitView true You can see here ☝️ that in iOS 18 the views have the same memory address on the second call and are evaluated to be the same. This differs from iOS 17. iOS 18 dismiss RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103e80000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103e80000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false The question I want to ask: Is this an intended change, meaning the current functionality in iOS 18 is expected? Or is this a bug and it's something that needs to be fixed? As a user, I would expect that the hit testing functionality would remain the same from iOS 17 to iOS 18. Thank you for your time.
14
13
4.8k
Aug ’24
Can SwiftUI View Receive a Call When Window Will Close?
I have an NSStatusBar application. This is my first in SwiftUI. And I need to know when the window is closed so that I can disable some of menu commands. I can use NSWindowDelegate with AppDelegate as follows. import SwiftUI @main struct SomeApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var menuViewModel = MenuViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(menuViewModel) } } } class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { private var menuViewModel = MenuViewModel() func applicationDidFinishLaunching(_ notification: Notification) { if let window = NSApplication.shared.windows.first { window.setIsVisible(false) window.delegate = self } } func windowWillClose(_ notification: Notification) { menuViewModel.windowClosed = true } } When the window will close, MenuViewModel (ObservableObject) will receive a call, which I want my ContentView to receive. But, so far, it won't. import SwiftUI struct ContentView: View { var body: some View { ZStack { ... ... } .onReceive(statusBarViewModel.$windowClosed) { result in // never called... } } } Can a SwiftUI View receive a call somehow when its window closes? Muchos thankos.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
3
1
613
Aug ’24
Cannot remove an observer <TUINSCursorUIController 0x600001844340> for the key path "visible" from <NSSavePanel 0x10ff05200> because it is not registered as an observer.
I have two NSTextField as an accessoryView of NSSavePanel. When I try to change focus in between them with a caps lock on the whole panel will crash. This will also happen when NSTextField if focused with caps lock on and i press cmd+tab (app switching). This happens on Sonoma + Sequoia beta. On top I have noticed editing NSTextField in accessoryView is completely broken on Sonoma and I can only edit it ONLY when the textfiled is using bindings. I am trying to find a workaround for the caps lock indicator being displayed. The only idea I have ATM is to observe NSApp.windows and look for TUINSWindow and force close it when it's visible. Is there any other workaround to prevent this crash? https://youtu.be/BCVjZH7684U Sample code: import Cocoa class ViewController: NSViewController { let savePanel = NSSavePanel() override func viewDidLoad() { super.viewDidLoad() let view = NSGridView(views: [[NSTextField(string: "111111")], [NSTextField(string: "22222222")]]) savePanel.accessoryView = view } override func viewWillAppear() { savePanel.runModal() } } Crash report: Cannot remove an observer <TUINSCursorUIController 0x600001844340> for the key path "visible" from <NSSavePanel 0x10ff05200> because it is not registered as an observer. ( 0 CoreFoundation 0x000000019a2522ec __exceptionPreprocess + 176 1 libobjc.A.dylib 0x0000000199d36158 objc_exception_throw + 60 2 Foundation 0x000000019b30436c -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 628 3 Foundation 0x000000019b3040a4 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 136 4 TextInputUIMacHelper 0x0000000253d9e598 -[TUINSCursorUIController deactivate:] + 416 5 AppKit 0x000000019dbda3e4 -[NSTextInputContext deactivate] + 288 6 AppKit 0x000000019da3fff4 +[NSTextInputContext currentInputContext_withFirstResponderSync:] + 228 7 AppKit 0x000000019da4f084 -[NSView _setWindow:] + 692 8 AppKit 0x000000019db7d880 -[NSTextView(NSPrivate) _setWindow:] + 216 9 AppKit 0x000000019e4da778 __21-[NSView _setWindow:]_block_invoke.146 + 268 10 CoreAutoLayout 0x00000001a2aba588 -[NSISEngine withBehaviors:performModifications:] + 88 11 AppKit 0x000000019da4f4b4 -[NSView _setWindow:] + 1764 12 AppKit 0x000000019da7712c -[NSView removeFromSuperview] + 168 13 AppKit 0x000000019dc7c0e8 -[_NSKeyboardFocusClipView removeFromSuperview] + 56 14 AppKit 0x000000019dbc5474 -[NSWindow endEditingFor:] + 368 15 AppKit 0x000000019da770d0 -[NSView removeFromSuperview] + 76 16 AppKit 0x000000019dc7c0e8 -[_NSKeyboardFocusClipView removeFromSuperview] + 56 17 AppKit 0x000000019dc7be00 -[NSCell endEditing:] + 452 18 AppKit 0x000000019dc7b994 -[NSTextField textDidEndEditing:] + 264 19 CoreFoundation 0x000000019a1d2144 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 148 20 CoreFoundation 0x000000019a2663d8 ___CFXRegistrationPost_block_invoke + 88 21 CoreFoundation 0x000000019a266320 _CFXRegistrationPost + 440 22 CoreFoundation 0x000000019a1a0678 _CFXNotificationPost + 768 23 Foundation 0x000000019b2bd2c4 -[NSNotificationCenter postNotificationName:object:userInfo:] + 88 24 AppKit 0x000000019dc7b5fc -[NSTextView(NSSharing) resignFirstResponder] + 668 25 AppKit 0x000000019db2ca80 -[NSWindow _realMakeFirstResponder:] + 196 26 AppKit 0x000000019dcc1764 -[NSWindow _makeParentWindowHaveFirstResponder:] + 76 27 ViewBridge 0x00000001a296c8c0 -[NSAccessoryViewWindow makeFirstResponder:] + 80 28 AppKit 0x000000019dbdbb9c -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3148 29 AppKit 0x000000019db67504 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 288 30 AppKit 0x000000019db67210 -[NSWindow(NSEventRouting) sendEvent:] + 284 31 ViewBridge 0x00000001a296cecc -[NSAccessoryViewWindow sendEvent:] + 64 32 AppKit 0x000000019e2304f0 -[NSApplication(NSEventRouting) sendEvent:] + 1604 33 AppKit 0x000000019dc6df6c -[NSApplication _doModalLoop:peek:] + 276 34 AppKit 0x000000019dc6ce38 __35-[NSApplication runModalForWindow:]_block_invoke_2 + 56 35 AppKit 0x000000019dc6cde4 __35-[NSApplication runModalForWindow:]_block_invoke + 108 36 AppKit 0x000000019dc6c6b0 _NSTryRunModal + 100 37 AppKit 0x000000019dc6c570 -[NSApplication runModalForWindow:] + 292 38 AppKit 0x000000019e690490 -[NSSavePanel runModal] + 340 39 SavePanelAccessory 0x0000000100435ad4 $s18SavePanelAccessory14ViewControllerC14viewWillAppearyyF + 60 40 SavePanelAccessory 0x0000000100435b0c $s18SavePanelAccessory14ViewControllerC14viewWillAppearyyFTo + 36 41 AppKit 0x000000019db387f4 -[NSViewController _sendViewWillAppear] + 32 42 AppKit 0x000000019db386bc -[NSViewController _windowWillOrderOnScreen] + 80 43 AppKit 0x000000019e4e7b38 -[NSView _windowWillOrderOnScreen] + 56 44 AppKit 0x000000019e4e7ba4 -[NSView _windowWillOrderOnScreen] + 164 45 AppKit 0x000000019db38570 -[NSWindow _doWindowWillBeVisibleAsSheet:] + 40 46 AppKit 0x000000019e4fc418 -[NSWindow _reallyDoOrderWindowAboveOrBelow:] + 1028 47 AppKit 0x000000019e4fcfec -[NSWindow _reallyDoOrderWindow:] + 64 SHORTENED
Topic: UI Frameworks SubTopic: AppKit
1
0
657
Aug ’24
Migrating SwiftUI lifecycle to UIKit's
Hi, everyone I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes. Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app. I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application. Thank you very much.
7
2
1.9k
Aug ’24
Button Touch Not Canceled in ScrollView on Modal in SwiftUI for iOS 18
When displaying a view with a Button inside a ScrollView using the sheet modifier, if you try to close the sheet by swiping and your finger is touching the Button, the touch is not canceled. This issue occurs when building with Xcode 16 but does not occur when building with Xcode 15. Here is screen cast. https://drive.google.com/file/d/1GaOjggWxvjDY38My4JEl-URyik928iBT/view?usp=sharing Code struct ContentView: View { @State var isModalPresented: Bool = false var body: some View { ScrollView { Button { debugPrint("Hello") isModalPresented.toggle() } label: { Text("Hello") .frame(height: 44) } Button { debugPrint("World") } label: { Text("World") .frame(height: 44) } Text("Hoge") .frame(height: 44) .contentShape(Rectangle()) .onTapGesture { debugPrint("Hoge") } } .sheet(isPresented: $isModalPresented) { ContentView() } } }
15
19
2.9k
Sep ’24
Live Activity Blanks out when lock screen sleeps.
I have a live activity, that works fine when the Lock Screen showing, but as soon as it "sleeps" dims down for always on display, everything in the widget disappears and an Activity Indicator(spinner), displays in its place, but non-animating.
Replies
4
Boosts
0
Views
944
Activity
Jul ’24
Regression in UITabBarController on Catalyst when building under Xcode 16
I am running into an issue with UITabBarController in a Catalyst app when building under Xcode 16 and running on macOS 15. If a UITabBarController is used, the tabs are presented in an unwanted title/toolbar at the top of the window. If you have an app where your views run to the top of the window, this can obscure the content and controls that are near the top. I created a sample application that is attached to the Feedback record (FB14293963). When building under Xcode 15, this is what the app looks like: Under Xcode 16, it looks like this: Beyond this simple example, using UITabBarController in a presented view controller can result in the tabs not showing at all. Also, If you switch the view in the main window to something that isn't a UITabBarController, the tabs still remain at the top. This seems to stem from the tab bar/sidebar changes for iPadOS 18. While this approach can work for simpler apps, it may not work well at all for more complex apps and there really needs to be a way to opt out of this behavior so apps where it is not suited for can still take advantage of features in iPadOS/Catalyst 18. Has anyone discovered a workaround or way to disable the new tab bar behavior with having to write their own version of UITabBarController?
Replies
2
Boosts
0
Views
1.4k
Activity
Jul ’24
Combining NavigationSplitView and TabView in iOS 18
Hi folks, I've used a NavigationSplitView within one of the tabs of my app since iOS 16, but with the new styling in iOS 18 the toolbar region looks odd. In other tabs using e.g. simple stacks, the toolbar buttons are horizontally in line with the new tab picker, but with NavigationSplitView, the toolbar leaves a lot of empty space at the top (see below). Is there anything I can do to adjust this, or alternatively, continue to use the old style? Thanks!
Replies
12
Boosts
3
Views
2.9k
Activity
Jul ’24
iOS 18 beta bug: NavigationStack pushes the same view twice
Hello, community and Apple engineers. I need your help. Our app has the following issue: NavigationStack pushes a view twice if the NavigationStack is inside TabView and NavigationStack uses a navigation path of custom Hashable elements. Our app works with issues in Xcode 18 Beta 13 + iOS 18.0. The same issue happened on previous beta versions of Xcode 18. The issue isn’t represented in iOS 17.x and everything worked well before iOS 18.0 beta releases. I was able to represent the same issue in a clear project with two simple views. I will paste the code below. Several notes: We use a centralised routing system in our app where all possible routes for navigation path are implemented in a View extension called withAppRouter(). We have a enum RouterDestination that contains all possible routes and is resolved in withAppRouter() extension. We use Router class that contains @Published var path: [RouterDestination] = [] and this @Published property is bound to NavigationStack. In the real app, we need to have an access to this path property for programmatic navigation purposes. Our app uses @ObservableObject / @StateObject approach. import SwiftUI struct ContentView: View { @StateObject private var router = Router() var body: some View { TabView { NavigationStack(path: $router.path) { NavigationLink(value: RouterDestination.next, label: { Label("Next", systemImage: "plus.circle.fill") }) .withAppRouter() } } } } enum RouterDestination: Hashable { case next } struct SecondView: View { var body: some View { Text("Screen 2") } } class Router: ObservableObject { @Published var path: [RouterDestination] = [] } extension View { func withAppRouter() -> some View { navigationDestination(for: RouterDestination.self) { destination in switch destination { case .next: return SecondView() } } } } Below you can see the GIF with the issue: What I tried to do: Use iOS 17+ @Observable approach. It didn’t help. Using @State var path: [RouterDestination] = [] directly inside View seems to help. But it is not what we want as we need this property to be @Published and located inside Router class where we can get an access to it, and use for programmatic navigation if needed. I ask Apple engineers to help with that, please, and if it is a bug of iOS 18 beta, then please fix it in the next versions of iOS 18.0
Replies
12
Boosts
4
Views
2k
Activity
Jul ’24
SwiftUI Gestures prevent scrolling with iOS 18
I added gesture support to my app that supports iOS 16 and 17 and have never had issues with it. However, when I compiled my app with Xcode 16 I immediately noticed a problem with the app when I ran it in the simulator. I couldn't scroll up or down. I figured out it’s because of my gesture support. My gesture support is pretty simple. let myDragGesture = DragGesture() .onChanged { gesture in self.offset = gesture.translation } .onEnded { _ in if self.offset.width > threshold { ...some logic } else if self.offset.width < -threshold { ...some other logic } logitUI.debug("drag gesture width was \(self.offset.width)") self.offset = .zero } If I pass nil to .gesture instead of myDragGesture then scrolling starts working again. Here’s some example output when I’m trying to scroll down. These messages do NOT appear when I run my app on an iOS 16/17 simulator with Xcode 15. drag gesture width was 5.333328 drag gesture width was -15.333344 drag gesture width was -3.000000 drag gesture width was -24.333328 drag gesture width was -30.666656 I opened FB14205678 about this.
Replies
17
Boosts
21
Views
10k
Activity
Jul ’24
CoreText' CTRunDraw can't draw underline attribute in iOS18 with Xcode 16 beta
demo code : - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); // Flip the coordinate system CGContextSetTextMatrix(context, CGAffineTransformIdentity); CGContextTranslateCTM(context, 0, self.bounds.size.height); CGContextScaleCTM(context, 1.0, -1.0); NSDictionary *attrs = @{NSFontAttributeName: [UIFont systemFontOfSize:20], NSForegroundColorAttributeName: [UIColor blueColor], NSUnderlineStyleAttributeName: @(NSUnderlineStyleThick), }; // Make an attributed string NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"Hello CoreText!" attributes:attrs]; CFAttributedStringRef attributedStringRef = (__bridge CFAttributedStringRef)attributedString; // Simple CoreText with CTFrameDraw CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attributedStringRef); CGPathRef path = CGPathCreateWithRect(self.bounds,NULL); CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0, 0),path,NULL); //CTFrameDraw(frame, context); // You can comment the line 'CTFrameDraw' and use the following lines // draw with CTLineDraw CFArrayRef lines = CTFrameGetLines(frame); CGPoint lineOrigins[CFArrayGetCount(lines)]; CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins); for (int i = 0; i < CFArrayGetCount(lines); i++) { CTLineRef line = CFArrayGetValueAtIndex(lines, i); CGContextSetTextPosition(context, lineOrigins[i].x, lineOrigins[i].y); // CTLineDraw(line, context); // You can comment the line 'CTLineDraw' and use the following lines // draw with CTRunDraw // use CTRunDraw will lost some attributes like NSUnderlineStyleAttributeName, // so you need draw it by yourself CFArrayRef runs = CTLineGetGlyphRuns(line); for (int j = 0; j < CFArrayGetCount(runs); j++) { CTRunRef run = CFArrayGetValueAtIndex(runs, j); CTRunDraw(run, context, CFRangeMake(0, 0)); } } } this code will use CTRunDraw to draw the content , and the underline will draw and show normally in iOS17 & Xcode 15 , But when you build it with XCode16 & iOS18 beta . the underline will be missing .
Replies
2
Boosts
4
Views
762
Activity
Jul ’24
SF Symbols 6 app does not export 'Can Rotate' property of a layer
The SF Symbols app 6.0 (99) does not export the ‘Can Rotate’ property of layers when exporting a symbol via File > Export Symbol. Without this, all the new fantastic edit functions in the app related to rotating in SF Symbols is completely useless. This issue with the SF Symbols 6 app can be reproduced by exporting a rotatable symbol like fan.desk, and then by importing the result as custom symbol. When inspecting ‘Group 1’ of the imported symbol, it is no longer marked as rotatable. SF Symbols app 6.0 is still in beta, but hasn't been updated since 10 June. Hopefully this bug will be solved in the release version, or earlier. Does anyone know how to manually add the missing rotation info to the exported SVG file? In case an Apple engineer reads this: FB13916635
Replies
4
Boosts
2
Views
796
Activity
Jul ’24
onContinueUserActivity(CSSearchableItemActionType, perform) does not work on a SwiftUI macOS app
onContinueUserActivity(CSSearchableItemActionType, perform) works as expected on iOS when we search and select an item from Spotlight, but nothing happens when we do the same on a SwiftUI macOS app. var body: some Scene { WindowGroup { MyView() .onContinueUserActivity(CSSearchableItemActionType, perform: handleSpotlight) } } func handleSpotlight(_ userActivity: NSUserActivity) { // Is not called... } How can we respond to a user clicking a Spotlight result from our apps on macOS?
Replies
3
Boosts
1
Views
918
Activity
Jul ’24
@Observable state class reinitializes every time view is updated
With the new @Observable macro, it looks like every time the struct of a view is reinitialized, any observable class marked as @State in the struct also gets reinitialized. Moreover, the result of the reinitialization immediately gets discarded. This is in contrast to @StateObject and ObservableObject, where the class would only be initialized at the first creation of the view. The initialization method of the class would never be called again between view updates. Is this a bug or an expected behavior? This redundant reinitialization causes performance issues when the init method of the observable class does anything slightly heavyweight. Feedback ID: FB13697724
Replies
3
Boosts
1
Views
1k
Activity
Jul ’24
Control Center widget won't show snippet view
Has anyone been able to create a Control Center widget that opens a snippet view? There are stock Control Center widgets that do this, but I haven't been able to get it to work. Here's what I tried: struct SnippetButton: ControlWidget { var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind: "xxx.xxx.snippetWidget" ) { ControlWidgetButton(action: SnippetIntent()) { Label("Show Snippet", systemImage: "map.fill") } } .displayName(LocalizedStringResource("Show Snippet")) .description("Show a snippet.") } } struct SnippetIntent: ControlConfigurationIntent { static var title: LocalizedStringResource = "Show a snippet" static var description = IntentDescription("Show a snippet with some text.") @MainActor func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView { return .result(dialog: IntentDialog("Hello!"), view: SnippetView()) } } struct SnippetView: View { var body: some View { Text("Hello!") } }
Replies
4
Boosts
2
Views
877
Activity
Jul ’24
NSSavePanel accessory view doesn't react to enter keypress
I have a textfield in accessory view of NSSavePanel. For user convenience there are default actions supported natively by macOS (such as pressing Enter, keyEquivalent). However this doesn't work for enter under Sonoma. Escape key works. Is enter keypress dangerous for malicious actors so it's not supported? I have workaround below but I am not confident if I am not violating sandbox (future proof). Original code demonstrating the issue: class ViewController: NSViewController, NSTextFieldDelegate, NSControlTextEditingDelegate { let savePanel = NSSavePanel() override func viewDidLoad() { super.viewDidLoad() let customView = NSView() let textField = NSTextField(string: "11111111") textField.delegate = self // to get focus using tab keypress savePanel.accessoryView = textField } override func viewWillAppear() { savePanel.runModal() } } Workaround: // variable set to true in delegate method controlTextDidEndEditing var didUseTextFieldWithEnterPressed = false override func performKeyEquivalent(with event: NSEvent) -> Bool { if #unavailable(macOS 14) { return super.performKeyEquivalent(with: event) } guard let panel, didUseTextFieldWithEnterPressed == true, event.type == .keyDown && (event.keyCode == UInt16(kVK_Return) || event.keyCode == UInt16(kVK_ANSI_KeypadEnter)) else { return super.performKeyEquivalent(with: event) } return panel.performKeyEquivalent(with: event) }
Replies
1
Boosts
0
Views
616
Activity
Aug ’24
How can we performantly scroll to a target location using TextKit 2?
How can we performantly scroll to a target location using TextKit 2? Hi everyone, I'm building a custom text editor using TextKit 2 and would like to scroll to a target location efficiently. For instance, I would like to move to the end of a document seamlessly, similar to how users can do in standard text editors by using CMD + Down. Background: NSTextView and TextEdit on macOS can navigate to the end of large documents in milliseconds. However, after reading the documentation and experimenting with various ideas using TextKit 2's APIs, it's not clear how third-party developers are supposed to achieve this. My Code: Here's the code I use to move the selection to the end of the document and scroll the viewport to reveal the selection. override func moveToEndOfDocument(_ sender: Any?) { textLayoutManager.ensureLayout(for: textLayoutManager.documentRange) let targetLocation = textLayoutManager.documentRange.endLocation let beforeTargetLocation = textLayoutManager.location(targetLocation, offsetBy: -1)! textLayoutManager.textViewportLayoutController.layoutViewport() guard let textLayoutFragment = textLayoutManager.textLayoutFragment(for: beforeTargetLocation) else { return } guard let textLineFragment = textLayoutFragment.textLineFragment(for: targetLocation, isUpstreamAffinity: true) else { return } let lineFrame = textLayoutFragment.layoutFragmentFrame let lineFragmentFrame = textLineFragment.typographicBounds.offsetBy(dx: 0, dy: lineFrame.minY) scrollToVisible(lineFragmentFrame) } While this code works as intended, it is very inefficient because ensureLayout(_:) is incredibly expensive and can take seconds for large documents. Issues Encountered: In my attempts, I have come across the following two issues. Estimated Frames: The frames of NSTextLayoutFragment and NSTextLineFragment are approximate and not precise enough for scrolling unless the text layout fragment has been fully laid out. Laying out all text is expensive: The frames become accurate once NSTextLayoutManager's ensureLayout(for:) method has been called with a range covering the entire document. However, ensureLayout(for:) is resource-intensive and can take seconds for large documents. NSTextView, on the other hand, accomplishes the same scrolling to the end of a document in milliseconds. I've tried using NSTextViewportLayoutController's relocateViewport(to:) without success. It's unclear to me whether this function is intended for a use case like mine. If it is, I would appreciate some guidance on its proper usage. Configuration: I'm testing on macOS Sonoma 14.5 (23F79), Swift (AppKit), Xcode 15.4 (15F31d). I'm working on a multi-platform project written in AppKit and UIKit, so I'm looking for either a single solution that works in both AppKit and UIKit or two solutions, one for each UI framework. Question: How can third-party developers scroll to a target location, specifically the end of a document, performantly using TextKit 2? Steps to Reproduce: The issue can be reproduced using the example project (download from link below) by following these steps: Open the example project. Run the example app on a Mac. The example app shows an uneditable text view in a scroll view. The text view displays a long text. Press the "Move to End of Document" toolbar item. Notice that the text view has scrolled to the bottom, but this took several seconds (~3 seconds on my MacBook Pro 16-inch, 2021). The duration will be shown in Xcode's log. You can open the ExampleTextView.swift file and find the implementation of moveToEndOfDocument(_:). Comment out line 84 where the ensureLayout(_:) is called, rerun the app, and then select "Move to End of Document" again. This time, you will notice that the text view moves fast but does not end up at the bottom of the document. You may also open the large-file.json in the project, the same file that the example app displays, in TextEdit, and press CMD+Down to move to the end of the document. Notice that TextEdit does this in mere milliseconds. Example Project: The example project is located on GitHub: https://github.com/simonbs/apple-developer-forums/tree/main/how-can-we-performantly-scroll-to-a-target-location-using-textkit-2 Any advice or guidance on how to achieve this with TextKit 2 would be greatly appreciated. Thanks in advance! Best regards, Simon
Replies
10
Boosts
9
Views
2.8k
Activity
Aug ’24
UITextView's pressesBegan isn't triggered by the software keyboard
I'm building a SwiftUI app with a UITextView subclass, and it seems that the software keyboard doesn't trigger the pressesBegan or pressesEnded functions of UITextView. With a hardware keyboard, pressesBegan works as expected, allowing us to intercept key presses in our subclass. I can't find any documentation about this, or any other forum posts (here or on Stack Overflow) that talk about a discrepancy between software and hardware keyboard behaviors, and I can't believe this is an intended behavior. Our app is a SwiftUI app, in case that's relevant. Does anyone have any guidance? Is this a bug or am I not understanding this API? Any information or work arounds would be greatly appreciated. I've made a sample project that demonstrates this issue, which you can grab from GitHub at https://github.com/nyousefi/KeyPressSample. To see this in action, run the sample project and start pressing keys. The hardware keyboard will print the key press at the top of the screen (above the text view), while the software keyboard won't.
Replies
3
Boosts
0
Views
736
Activity
Aug ’24
iOS18 Control Widget custom symbol preview failed
i export apple SF as custom sf for test. code is simple: var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind:"ControlWidgetConfiguration" ) { ControlWidgetButton(action: DynamicWidgetIntent()) { Text("test") Image("custom_like") } }.displayName("test") } as we can see, it can't show image in the preview. but it can show image in the Control widget center. am i do some thing wrong?
Replies
5
Boosts
7
Views
1.1k
Activity
Aug ’24
tvOS 18.0 Siri back button behavior bug
On testing my app with tvOS 18, I have noticed the Siri Remote back button no longer provides system-provided behavior when interacting with tab bar controller pages. Instead of moving focus back to the tab bar when pressed, the back button will close the app, as if the Home button was pressed. This occurs both on device and in the Simulator. Create tvOS project with a tab bar controller. Create pages/tabs which contain focusable items (ie. buttons) Scroll down to any focusable item (ie. a button or UICollectionView cell) Hit the Siri Remote back button. See expect behavior below: Expected behavior: System-provided behavior should move focus back to the tab bar at the top of the screen. Actual results: App is closed and user is taken back to the Home Screen. Has anyone else noticed this behavior?
Replies
8
Boosts
3
Views
1.3k
Activity
Aug ’24
iOS 18 hit testing functionality differs from iOS 17
I created a Radar for this FB14766095, but thought I would add it here for extra visibility, or if anyone else had any thoughts on the issue. Basic Information Please provide a descriptive title for your feedback: iOS 18 hit testing functionality differs from iOS 17 What type of feedback are you reporting? Incorrect/Unexpected Behavior Description: Please describe the issue and what steps we can take to reproduce it: We have an issue in iOS 18 Beta 6 where hit testing functionality differs from the expected functionality in iOS 17.5.1 and previous versions of iOS. iOS 17: When a sheet is presented, the hit-testing logic considers subviews of the root view, meaning the rootView itself is rarely the hit view. iOS 18: When a sheet is presented, the hit-testing logic changes, sometimes considering the rootView itself as the hit view. Code: import SwiftUI struct ContentView: View { @State var isPresentingView: Bool = false var body: some View { VStack { Text("View One") Button { isPresentingView.toggle() } label: { Text("Present View Two") } } .padding() .sheet(isPresented: $isPresentingView) { ContentViewTwo() } } } #Preview { ContentView() } struct ContentViewTwo: View { @State var isPresentingView: Bool = false var body: some View { VStack { Text("View Two") } .padding() } } extension UIWindow { public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { /// Get view from superclass. guard let hitView = super.hitTest(point, with: event) else { return nil } print("RPTEST rootViewController = ", rootViewController.hashValue) print("RPTEST rootViewController?.view = ", rootViewController?.view.hashValue) print("RPTEST hitView = ", hitView.hashValue) if let rootView = rootViewController?.view { print("RPTEST rootViewController's view memory address: \(Unmanaged.passUnretained(rootView).toOpaque())") print("RPTEST hitView memory address: \(Unmanaged.passUnretained(hitView).toOpaque())") print("RPTEST Are they equal? \(rootView == hitView)") } /// If the returned view is the `UIHostingController`'s view, ignore. print("MTEST: hitTest rootViewController?.view == hitView", rootViewController?.view == hitView) print("MTEST: -") return hitView } } Looking at the print statements from the provided sample project:
 iOS 17 presenting a sheet from a button tap on the ContentView(): RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fd25000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fd25000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false iOS 17 dismiss from presented view: RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fe04080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x0000000120009200 RPTEST hitView memory address: 0x000000011fe04080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false iOS 18 presenting a sheet from a button tap on the ContentView(): RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103342080 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x000000010333e3c0 RPTEST Are they equal? true MTEST: hitTest rootViewController?.view == hitView true You can see here ☝️ that in iOS 18 the views have the same memory address on the second call and are evaluated to be the same. This differs from iOS 17. iOS 18 dismiss RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103e80000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false RPTEST rootViewController's view memory address: 0x000000010333e3c0 RPTEST hitView memory address: 0x0000000103e80000 RPTEST Are they equal? false MTEST: hitTest rootViewController?.view == hitView false The question I want to ask: Is this an intended change, meaning the current functionality in iOS 18 is expected? Or is this a bug and it's something that needs to be fixed? As a user, I would expect that the hit testing functionality would remain the same from iOS 17 to iOS 18. Thank you for your time.
Replies
14
Boosts
13
Views
4.8k
Activity
Aug ’24
Can SwiftUI View Receive a Call When Window Will Close?
I have an NSStatusBar application. This is my first in SwiftUI. And I need to know when the window is closed so that I can disable some of menu commands. I can use NSWindowDelegate with AppDelegate as follows. import SwiftUI @main struct SomeApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @StateObject private var menuViewModel = MenuViewModel() var body: some Scene { WindowGroup { ContentView() .environmentObject(menuViewModel) } } } class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { private var menuViewModel = MenuViewModel() func applicationDidFinishLaunching(_ notification: Notification) { if let window = NSApplication.shared.windows.first { window.setIsVisible(false) window.delegate = self } } func windowWillClose(_ notification: Notification) { menuViewModel.windowClosed = true } } When the window will close, MenuViewModel (ObservableObject) will receive a call, which I want my ContentView to receive. But, so far, it won't. import SwiftUI struct ContentView: View { var body: some View { ZStack { ... ... } .onReceive(statusBarViewModel.$windowClosed) { result in // never called... } } } Can a SwiftUI View receive a call somehow when its window closes? Muchos thankos.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
3
Boosts
1
Views
613
Activity
Aug ’24
Cannot remove an observer <TUINSCursorUIController 0x600001844340> for the key path "visible" from <NSSavePanel 0x10ff05200> because it is not registered as an observer.
I have two NSTextField as an accessoryView of NSSavePanel. When I try to change focus in between them with a caps lock on the whole panel will crash. This will also happen when NSTextField if focused with caps lock on and i press cmd+tab (app switching). This happens on Sonoma + Sequoia beta. On top I have noticed editing NSTextField in accessoryView is completely broken on Sonoma and I can only edit it ONLY when the textfiled is using bindings. I am trying to find a workaround for the caps lock indicator being displayed. The only idea I have ATM is to observe NSApp.windows and look for TUINSWindow and force close it when it's visible. Is there any other workaround to prevent this crash? https://youtu.be/BCVjZH7684U Sample code: import Cocoa class ViewController: NSViewController { let savePanel = NSSavePanel() override func viewDidLoad() { super.viewDidLoad() let view = NSGridView(views: [[NSTextField(string: "111111")], [NSTextField(string: "22222222")]]) savePanel.accessoryView = view } override func viewWillAppear() { savePanel.runModal() } } Crash report: Cannot remove an observer <TUINSCursorUIController 0x600001844340> for the key path "visible" from <NSSavePanel 0x10ff05200> because it is not registered as an observer. ( 0 CoreFoundation 0x000000019a2522ec __exceptionPreprocess + 176 1 libobjc.A.dylib 0x0000000199d36158 objc_exception_throw + 60 2 Foundation 0x000000019b30436c -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 628 3 Foundation 0x000000019b3040a4 -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 136 4 TextInputUIMacHelper 0x0000000253d9e598 -[TUINSCursorUIController deactivate:] + 416 5 AppKit 0x000000019dbda3e4 -[NSTextInputContext deactivate] + 288 6 AppKit 0x000000019da3fff4 +[NSTextInputContext currentInputContext_withFirstResponderSync:] + 228 7 AppKit 0x000000019da4f084 -[NSView _setWindow:] + 692 8 AppKit 0x000000019db7d880 -[NSTextView(NSPrivate) _setWindow:] + 216 9 AppKit 0x000000019e4da778 __21-[NSView _setWindow:]_block_invoke.146 + 268 10 CoreAutoLayout 0x00000001a2aba588 -[NSISEngine withBehaviors:performModifications:] + 88 11 AppKit 0x000000019da4f4b4 -[NSView _setWindow:] + 1764 12 AppKit 0x000000019da7712c -[NSView removeFromSuperview] + 168 13 AppKit 0x000000019dc7c0e8 -[_NSKeyboardFocusClipView removeFromSuperview] + 56 14 AppKit 0x000000019dbc5474 -[NSWindow endEditingFor:] + 368 15 AppKit 0x000000019da770d0 -[NSView removeFromSuperview] + 76 16 AppKit 0x000000019dc7c0e8 -[_NSKeyboardFocusClipView removeFromSuperview] + 56 17 AppKit 0x000000019dc7be00 -[NSCell endEditing:] + 452 18 AppKit 0x000000019dc7b994 -[NSTextField textDidEndEditing:] + 264 19 CoreFoundation 0x000000019a1d2144 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 148 20 CoreFoundation 0x000000019a2663d8 ___CFXRegistrationPost_block_invoke + 88 21 CoreFoundation 0x000000019a266320 _CFXRegistrationPost + 440 22 CoreFoundation 0x000000019a1a0678 _CFXNotificationPost + 768 23 Foundation 0x000000019b2bd2c4 -[NSNotificationCenter postNotificationName:object:userInfo:] + 88 24 AppKit 0x000000019dc7b5fc -[NSTextView(NSSharing) resignFirstResponder] + 668 25 AppKit 0x000000019db2ca80 -[NSWindow _realMakeFirstResponder:] + 196 26 AppKit 0x000000019dcc1764 -[NSWindow _makeParentWindowHaveFirstResponder:] + 76 27 ViewBridge 0x00000001a296c8c0 -[NSAccessoryViewWindow makeFirstResponder:] + 80 28 AppKit 0x000000019dbdbb9c -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3148 29 AppKit 0x000000019db67504 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 288 30 AppKit 0x000000019db67210 -[NSWindow(NSEventRouting) sendEvent:] + 284 31 ViewBridge 0x00000001a296cecc -[NSAccessoryViewWindow sendEvent:] + 64 32 AppKit 0x000000019e2304f0 -[NSApplication(NSEventRouting) sendEvent:] + 1604 33 AppKit 0x000000019dc6df6c -[NSApplication _doModalLoop:peek:] + 276 34 AppKit 0x000000019dc6ce38 __35-[NSApplication runModalForWindow:]_block_invoke_2 + 56 35 AppKit 0x000000019dc6cde4 __35-[NSApplication runModalForWindow:]_block_invoke + 108 36 AppKit 0x000000019dc6c6b0 _NSTryRunModal + 100 37 AppKit 0x000000019dc6c570 -[NSApplication runModalForWindow:] + 292 38 AppKit 0x000000019e690490 -[NSSavePanel runModal] + 340 39 SavePanelAccessory 0x0000000100435ad4 $s18SavePanelAccessory14ViewControllerC14viewWillAppearyyF + 60 40 SavePanelAccessory 0x0000000100435b0c $s18SavePanelAccessory14ViewControllerC14viewWillAppearyyFTo + 36 41 AppKit 0x000000019db387f4 -[NSViewController _sendViewWillAppear] + 32 42 AppKit 0x000000019db386bc -[NSViewController _windowWillOrderOnScreen] + 80 43 AppKit 0x000000019e4e7b38 -[NSView _windowWillOrderOnScreen] + 56 44 AppKit 0x000000019e4e7ba4 -[NSView _windowWillOrderOnScreen] + 164 45 AppKit 0x000000019db38570 -[NSWindow _doWindowWillBeVisibleAsSheet:] + 40 46 AppKit 0x000000019e4fc418 -[NSWindow _reallyDoOrderWindowAboveOrBelow:] + 1028 47 AppKit 0x000000019e4fcfec -[NSWindow _reallyDoOrderWindow:] + 64 SHORTENED
Topic: UI Frameworks SubTopic: AppKit
Replies
1
Boosts
0
Views
657
Activity
Aug ’24
Migrating SwiftUI lifecycle to UIKit's
Hi, everyone I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes. Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app. I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application. Thank you very much.
Replies
7
Boosts
2
Views
1.9k
Activity
Aug ’24
Button Touch Not Canceled in ScrollView on Modal in SwiftUI for iOS 18
When displaying a view with a Button inside a ScrollView using the sheet modifier, if you try to close the sheet by swiping and your finger is touching the Button, the touch is not canceled. This issue occurs when building with Xcode 16 but does not occur when building with Xcode 15. Here is screen cast. https://drive.google.com/file/d/1GaOjggWxvjDY38My4JEl-URyik928iBT/view?usp=sharing Code struct ContentView: View { @State var isModalPresented: Bool = false var body: some View { ScrollView { Button { debugPrint("Hello") isModalPresented.toggle() } label: { Text("Hello") .frame(height: 44) } Button { debugPrint("World") } label: { Text("World") .frame(height: 44) } Text("Hoge") .frame(height: 44) .contentShape(Rectangle()) .onTapGesture { debugPrint("Hoge") } } .sheet(isPresented: $isModalPresented) { ContentView() } } }
Replies
15
Boosts
19
Views
2.9k
Activity
Sep ’24