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

NSMutableAttributedString and NSTextTable
I'm using NSTextTable to format panels of stand-out text within body text. Paragraphs within the panel are handled as individual NSTextTableBlocks within the table. Each block is added to the NSMutableParagraphStyle that is part of the attributed string within the block's attributes. That's all fine and it works. But... Occasionally I see undrawn lines within the panel. These disappear (or sometimes appear) when the parent window (and thus the NSTextView holding the rendered attributed string) is resized. Lines do not always appear, and when they do they are not always in the same place. The height of the gap varies. I see this behaviour with these panels and with tables. What's common to both cases is not only the use of NSTextTable and NSTextTableBlock etc., but crucially (I think) the use of block margins. If I disable margins (which looks OK for the panels, but isn't right for tables), the problem disappears. So, a bug or (more likely) I'm missing a key part of view rendering or margin set up. But what? Code here: https://github.com/smittytone/PreviewMarkdown/blob/930f5f32aa0b3b77ec3f4f53436a79e10bb26f18/Markdown%20Previewer/Styler.swift#L882 Running 14.6.1 on an M3. I'm using TextKit 1 because I'm using an NSLayoutManager subclass to override certain text underlines (not used in panels as outlined above, or tables).
Topic: UI Frameworks SubTopic: AppKit
3
0
439
Sep ’24
Swift Charts - weak scrolling performance
Hello there! I wanted to give a native scrolling mechanism for the Swift Charts Graph a try and experiment a bit if the scenario that we try to achieve might be possible, but it seems that the Swift Charts scrolling performance is very poor. The graph was created as follows: X-axis is created based on a date range, Y-axis is created based on an integer values between moreless 0-320 value. the graph is scrollable horizontally only (x-axis), The time range (x-axis) for the scrolling content was set to one year from now date (so the user can scroll one year into the past as a minimum visible date (.chartXScale). The X-axis shows 3 hours of data per screen width (.chartXVisibleDomain). The data points for the graph are generated once when screen is about to appear so that the Charts engine can use it (no lazy loading implemented yet). The line data points (LineMark views) consist of 2880 data points distributed every 5 minutes which simulates - two days of continuous data stream that we want to present. The rest of the graph displays no data at all. The performance result: The graph on the initial loading phase is frozen for about 10-15 seconds until the data appears on the graph. Scrolling is very laggy - the CPU usage is 100% and is unacceptable for the end users. If we show no data at all on the graph (so no LineMark views are created at all) - the result is similar - the empty graph scrolling is also very laggy. Below I am sharing a test code: @main struct ChartsTestApp: App { var body: some Scene { WindowGroup { ContentView() Spacer() } } } struct LineDataPoint: Identifiable, Equatable { var id: Int let date: Date let value: Int } actor TestData { func generate(startDate: Date) async -> [LineDataPoint] { var values: [LineDataPoint] = [] for i in 0..<(1440 * 2) { values.append( LineDataPoint( id: i, date: startDate.addingTimeInterval( TimeInterval(60 * 5 * i) // Every 5 minutes ), value: Int.random(in: 1...100) ) ) } return values } } struct ContentView: View { var startDate: Date { return endDate.addingTimeInterval(-3600*24*30*12) // one year into the past from now } let endDate = Date() @State var dataPoints: [LineDataPoint] = [] var body: some View { Chart { ForEach(dataPoints) { item in LineMark( x: .value("Date", item.date), y: .value("Value", item.value), series: .value("Series", "Test") ) } } .frame(height: 200) .chartScrollableAxes(.horizontal) .chartYAxis(.hidden) .chartXScale(domain: startDate...endDate) // one year possibility to scroll back .chartXVisibleDomain(length: 3600 * 3) // 3 hours visible on screen .onAppear { Task { dataPoints = await TestData().generate(startDate: startDate) } } } } I would be grateful for any insights or suggestions on how to improve it or if it's planned to be improved in the future. Currently, I use UIKit CollectionView where we split the graph into smaller chunks of the graph and we present the SwiftUI Chart content in the cells, so we use the scrolling offered there. I wonder if it's possible to use native SwiftUI for such a scenario so that later on we could also implement some kind of lazy loading of the data as the user scrolls into the past.
4
2
1.5k
Sep ’24
Widget link broken by `.desaturated` image rendering mode
Using desaturated mode on an image in a widget will break any links or buttons that use the image as their 'label'. Using the following will just open the app as if there was no link at all - therefore just using the fallback userActivity handler, or any .widgetURL() urls provided. Link(destination: URL(string: "bug://never-works")!) { Image("puppy") .widgetAccentedRenderingMode(.desaturated) } The same goes for buttons: Button(intent: MyDemoIntent()) { Image("puppy") .widgetAccentedRenderingMode(.desaturated) } I've tried hacky solutions like putting the link behind the image using a ZStack, and disabling hit testing on the image, but they don't work. Anything else to try? Logged as Feedback #15152620.
8
5
933
Sep ’24
SwiftUI toolbar in MacOS 15 bug ?
struct ContentView: View { var body: some View { NavigationSplitView { List { Text("row 1") Text("row 2") Text("row 3") } .toolbar(content: { ToolbarItem { Button("aa", action: onToolbar) } }) } detail: { HSplitView { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .toolbar(id: "toolbar", content: { ToolbarItem(id: "toolbar-1") { Button("bb", action: onToolbar) } }) .padding() Text("right") } }.navigationTitle("") } func onToolbar() {} } Run & Crash NSToolbar 0x6000005665b0 already contains an item with the identifier com.apple.SwiftUI.splitViewSeparator-0. Duplicate items of this type are not allowed.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
13
2
1.7k
Sep ’24
[macOS Sequoia] Using RegisterEventHotkey with option and shift modifiers doesn't working anymore
Hello. In my app, I use RegisterEventHotkey to implement global keyboard shortcuts to trigger actions. Up until macOS Sequoia, I was able to use a keyboard shortcut with option and shift as the modifiers, like option shift 2 (⌥ ⇧ 2). Now, on macOS Sequoia, using RegisterEventHotkey to register a hotkey with those exact modifiers (option and shift), regardless of the key, fails with the error -9868 (eventInternalErr). Is this a documented and wanted change, or is this a bug? Other modifier keys (just command, command option, command shift, command control, control shift, etc), all work. Any insight into this would be appreciated. (Feedback filed: FB15163561) Thank you, Matthias
22
10
14k
Sep ’24
SwiftUI: AlignmentGuide in Overlay not working when in if block
Scenario A SwiftUI view has an overlay with alignment: .top, the content uses .alignmentGuide(.top) {} to adjust the placement. Issue When the content of the overlay is in an if-block, the alignment guide is not adjusted. Example code The example shows 2 views. Not working example, where the content is an if-block. Working example, where the content is not in an if-block Screenshot: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/blob/main/screenshot.png Tested on - Xcode Version 16.0 RC (16A242) on iOS 18.0 Code // Not working .overlay(alignment: .top) { if true { // This line causes .alignmentGuide() to fail Text("Test") .alignmentGuide(.top, computeValue: { dimension in dimension[.bottom] }) } } // Working .overlay(alignment: .top) { Text("Test") .alignmentGuide(.top, computeValue: { dimension in dimension[.bottom] }) } Also created a Feedback: FB15248296 Example Project is here: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/tree/main
2
2
513
Sep ’24
Tinted widgets have an inset background in the widget gallery only
When the home screen is in tinted mode in iOS 18, the widget gallery seems to display widgets with the background inset from the edge of the widget (i.e. negative padding). You can see this behavior in the Apple News widget too, where the full-bleed content outside of the inset is very light. This only happens in the sample gallery, not when the widgets are placed on the home screen. For my widgets, this outer edge contains important content and the clipping behavior makes the widgets look poorly designed when viewed in the gallery. Is there any way to turn this behavior off and just show the widget normally in the gallery with no weird inset — the way it will actually display when added to the home screen? If it matters, my widgets are currently configured with: .contentMarginsDisabled() .containerBackground(for: .widget) { // ... (background color) */ }
3
0
621
Sep ’24
UIDocumentPickerViewController provides corrupt copy of file when user taps multiple times on file
We're trying to implement a backup/restore data feature in our business productivity iPad app using UIDocumentPickerViewController and AppleArchive, but discovered odd behavior of [UIDocumentPickerViewController initForOpeningContentTypes: asCopy:YES] when reading large archive files from a USB drive. We've duplicated this behavior with iPadOS 16.6.1 and 17.7 when building our app with Xcode 15.4 targeting minimum deployment of iPadOS 16. We haven't tested this with bleeding edge iPadOS 18. Here's our Objective-C code which presents the picker: NSArray* contentTypeArray = @[UTTypeAppleArchive]; UIDocumentPickerViewController* docPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:contentTypeArray asCopy:YES]; docPickerVC.delegate = self; docPickerVC.allowsMultipleSelection = NO; docPickerVC.shouldShowFileExtensions = YES; docPickerVC.modalPresentationStyle = UIModalPresentationPopover; docPickerVC.popoverPresentationController.sourceView = self.view; [self presentViewController:docPickerVC animated:YES completion:nil]; The UIDocumentPickerViewController remains visible until the selected external archive file has been copied from the USB drive to the app's local tmp sandbox. This may take several seconds due to the slow access speed of the USB drive. During this time the UIDocumentPickerViewController does NOT disable its tableview rows displaying files found on the USB drive. Even the most patient user will tap the desired filename a second (or third or fourth) time since the user's initial tap appears to have been ignored by UIDocumentPickerViewController, which lacks sufficient UI feedback showing it's busy copying the selected file. When the user taps the file a second time, UIDocumentPickerViewController apparently begins to copy the archive file once again. The end result is a truncated copy of the selected file based on the time between taps. For instance, a 788 MB source archive may be copied as a 56 MB file. Here, the UIDocumentPickerDelegate receives a 56 MB file instead of the original 788 MB of data. Not surprisingly, AppleArchive fails to decrypt the local copy of the archive because it's missing data. Instead of failing gracefully, AppleArchive crashes in AAArchiveStreamClose() (see forums post 765102 for details). Does anyone know if there's a workaround for this strange behavior of UIDocumentPickerViewController?
9
0
1.1k
Sep ’24
Navigation Bar Elements Disappear When Using UIPageViewController in SwiftUI Under Low Power Mode
Problem Description: In a SwiftUI application, I've wrapped UIKit's UIPageViewController using UIViewControllerRepresentable, naming the wrapped class PagedInfiniteScrollView. This component causes navigation bar elements (title and buttons) to disappear. This issue only occurs in Low Power Mode on a physical device. Steps to Reproduce: Enable Low Power Mode on a physical device and open the app's home page. From the home page, open a detail sheet containing PagedInfiniteScrollView. This detail page include a navigation title and a toolbar button in the top-right corner. PagedInfiniteScrollView supports horizontal swiping to switch pages. Tap the toolbar button in the top-right corner of the detail page to open an edit sheet. Without making any changes, close the edit sheet and return to the detail page. On the detail page, swipe left and right on the PagedInfiniteScrollView. Expected Result: When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page should remain visible. Actual Result: When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page disappear. import SwiftUI @main struct CalendarApp: App { var body: some Scene { WindowGroup { ContentView() } } } import SwiftUI struct ContentView: View { @State private var showDetailSheet = false @State private var currentPage: Int = 0 var body: some View { NavigationStack { Button { showDetailSheet = true } label: { Text("show Calendar sheet") } .sheet(isPresented: $showDetailSheet) { DetailSheet(currentPage: $currentPage) } } } } struct DetailSheet: View { @Binding var currentPage: Int @State private var showEditSheet = false var body: some View { NavigationStack { PagedInfiniteScrollView(content: { pageIndex in Text("\(pageIndex)") .frame(width: 200, height: 200) .background(Color.blue) }, currentPage: $currentPage) .sheet(isPresented: $showEditSheet, content: { Text("edit") }) .navigationTitle("Detail") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { Button { showEditSheet = true } label: { Text("Edit") } } } } } } import SwiftUI import UIKit struct PagedInfiniteScrollView<Content: View>: UIViewControllerRepresentable { typealias UIViewControllerType = UIPageViewController let content: (Int) -> Content @Binding var currentPage: Int func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> UIPageViewController { let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) pageViewController.dataSource = context.coordinator pageViewController.delegate = context.coordinator let initialViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) })) pageViewController.setViewControllers([initialViewController], direction: .forward, animated: false, completion: nil) return pageViewController } func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) { let currentViewController = uiViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>> let currentIndex = currentViewController?.rootView.index ?? 0 if currentPage != currentIndex { let direction: UIPageViewController.NavigationDirection = currentPage > currentIndex ? .forward : .reverse let newViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) })) uiViewController.setViewControllers([newViewController], direction: direction, animated: true, completion: nil) } } class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var parent: PagedInfiniteScrollView init(_ parent: PagedInfiniteScrollView) { self.parent = parent } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else { return nil } let previousIndex = currentIndex - 1 return UIHostingController(rootView: IdentifiableContent(index: previousIndex, content: { parent.content(previousIndex) })) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else { return nil } let nextIndex = currentIndex + 1 return UIHostingController(rootView: IdentifiableContent(index: nextIndex, content: { parent.content(nextIndex) })) } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed, let currentView = pageViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? { parent.currentPage = currentIndex } } } } extension PagedInfiniteScrollView { struct IdentifiableContent<Content: View>: View { let index: Int let content: Content init(index: Int, @ViewBuilder content: () -> Content) { self.index = index self.content = content() } var body: some View { content } } }
5
1
902
Oct ’24
AppLaunchTimeoutError
Hi. I tried to launch SwiftUI preview. But I got an error "AppLaunchTimeoutError" I attach the diagnostics. Does anyone know how to fix this problem? memo.txt
Topic: UI Frameworks SubTopic: SwiftUI
1
1
427
Oct ’24
Navigation Bar animation upon Tab change
Hi. Since Xcode 16 and/or iOS 18.0 (I upgraded at the same time), I have an strange effect in the lower (let's say) 20% section of the Navigation Bar when changing to another tab, and this independently if large titles are used or not. Mentioned section is brighter or darker than the rest of the Navigation Bar background, depending on which background tint is used. This effect lasts about 0.3 seconds, but is clearly visible, quite disturbing and new as of Xcode 16 and/or iOS 18.0. I use the code below in AppDelegate to get a gradient coloured Navigation Bar background. let appearance = UINavigationBarAppearance() UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().compactAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().compactScrollEdgeAppearance = appearance If I don't use above code., the background color is filled and without gradient. Subject effect doesn't show in this case. The effect basically looks like when changing tab, the new Navigation Bar background doesn't clear right away, and keeps the background from the previous Navigation Bar for 0.3 seconds before new one Navigation Bar background is rendered. I spent quite some time on changing every possible setting, in code as well as storyboard ... no success so far. Any ideas how to disable this undesired animation?
1
0
800
Oct ’24
SwiftUI List insertion changes aren't animated on macOS 15
I've been struggling with this issue since the release of macOS 15 Sequoia. I'm wondering if anyone else has encountered it or if anyone has a workaround to fix it. Inserting a new element into the array that acts as data source for a SwiftUI List with a ForEach is never animated even if the insertion is wrapped in a withAnimation() call. It seems that some other changes can be automated though: e.g. calls to shuffle() on the array successfully animate the changes. This used to work fine on macOS 14, but stopped working on macOS 15. I created a very simple project to reproduce the issue: import SwiftUI @main struct TestApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct IdentifiableItem: Identifiable { let id = UUID() var name: String { "Item \(id)" } } struct ContentView: View { @State var items: [IdentifiableItem] = [ IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), ] var body: some View { List { ForEach(items) { item in Text(item.name) } } Button("Add Item") { withAnimation { items.insert(IdentifiableItem(), at: 0) } } Button("Shuffle Items") { withAnimation { items.shuffle() } } } } How to reproduce Copy the code below in an Xcode project. Run it on macOS 15. Hit the "Add Item" button Expected: A new item is inserted with animation. Result: A new item is inserted without animation. How to prove this is a regression Follow the same steps above but run on macOS 14. A new item is inserted with animation.
3
1
686
Oct ’24
UICollectionView Move Item Method Not Called in iOS 18
Summary In iOS 18, the UICollectionViewDelegate method collectionView(_:targetIndexPathForMoveOfItemFromOriginalIndexPath:atCurrentIndexPath:toProposedIndexPath:) is not being called when moving items in a UICollectionView. This method works as expected in iOS 17.5 and earlier versions. Steps to Reproduce Create a UICollectionView with drag and drop enabled. Implement the UICollectionViewDelegate method: func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveOfItemFromOriginalIndexPath originalIndexPath: IndexPath, atCurrentIndexPath currentIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath { print("🐸 Move") return proposedIndexPath } Run the app on iOS 18. Attempt to drag and drop items within the collection view. Expected Behavior The method should be called during the drag and drop operation, and "🐸 Move" should be printed to the console. Actual Behavior The method is not called, and nothing is printed to the console. The drag and drop operation still occurs, but without invoking this delegate method. Configuration iOS Version: 18 Xcode Version: Xcode 16.0.0
Topic: UI Frameworks SubTopic: UIKit Tags:
4
3
750
Oct ’24
Using `compactTabIdentifiers` and other iOS 18+ tab APIs
Hello I'm working on implementing some changes to my app's tab bar, particularly to support some features in the new iPad floating tab bar, which has required me to adopt some of the new tab APIs from iOS 18. The issue I've encountered is that I would like the tabs visible in regular (floating tab bar) and compact (bottom tab bar) to be slightly different. The compact variant should show a subset of the tabs visible on iPad. For example: Floating tab bar: Use tabs A, B, C Bottom tab bar: Use tabs A, B I'm finding this quite difficult, especially in the scenario of split view on iPad, where the size class and tab bar location can change as the user interacts with the app. I went down the route of changing my tab bar controller's tabs property when the trait collection changed tabBarController.tabs = eligibleTabs where eligibleTabs for compact passes tabs AB, and when in regular - ABC. This throws an exception UIViewController cannot be shared between multiple UITab *** Assertion failure in -[UITab viewController], UITab.m:173 I'm not sharing view controllers between tabs. I can only assume that the system doesn't like me passing the same values (always at least tabs A & B) multiple times to the tabs property. I then later noticed a new iOS 18 property on UITabBarController - compactTabIdentifiers. This seemed like exactly what I was looking for: An optional filter to display only select root-level tabs when in a compact appearance. Default is nil, which would make all tabs available. So I changed my implementation: tabBarController.tabs = [tabA, tabB, tabC] tabBarController.compactTabIdentifiers = [tabAIdentifier, tabBIdentifier] and don't make any explicit updates when split view is enabled. Unfortunately this doesn't seem to work, at least not how I would expect it to. When enabling split view on iPad, the bottom tab bar appears, but it doesn't respect the tab identifiers I passed in tabBarController.compactTabIdentifiers. I'm not sure if this a bug or that I'm not really understanding how to use tabBarController.compactTabIdentifiers. Does anyone have any insight on this?
Topic: UI Frameworks SubTopic: UIKit
2
2
912
Oct ’24
Slow rendering List backed by SwiftData @Query
Hello, I've a question about performance when trying to render lots of items coming from SwiftData via a @Query on a SwiftUI List. Here's my setup: // Item.swift: @Model final class Item: Identifiable { var timestamp: Date var isOptionA: Bool init() { self.timestamp = Date() self.isOptionA = Bool.random() } } // Menu.swift enum Menu: String, CaseIterable, Hashable, Identifiable { var id: String { rawValue } case optionA case optionB case all var predicate: Predicate<Item> { switch self { case .optionA: return #Predicate { $0.isOptionA } case .optionB: return #Predicate { !$0.isOptionA } case .all: return #Predicate { _ in true } } } } // SlowData.swift @main struct SlowDataApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([Item.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) return try! ModelContainer(for: schema, configurations: [modelConfiguration]) }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } // ContentView.swift struct ContentView: View { @Environment(\.modelContext) private var modelContext @State var selection: Menu? = .optionA var body: some View { NavigationSplitView { List(Menu.allCases, selection: $selection) { menu in Text(menu.rawValue).tag(menu) } } detail: { DemoListView(selectedMenu: $selection) }.onAppear { // Do this just once // (0..<15_000).forEach { index in // let item = Item() // modelContext.insert(item) // } } } } // DemoListView.swift struct DemoListView: View { @Binding var selectedMenu: Menu? @Query private var items: [Item] init(selectedMenu: Binding<Menu?>) { self._selectedMenu = selectedMenu self._items = Query(filter: selectedMenu.wrappedValue?.predicate, sort: \.timestamp) } var body: some View { // Option 1: touching `items` = slow! List(items) { item in Text(item.timestamp.description) } // Option 2: Not touching `items` = fast! // List { // Text("Not accessing `items` here") // } .navigationTitle(selectedMenu?.rawValue ?? "N/A") } } When I use Option 1 on DemoListView, there's a noticeable delay on the navigation. If I use Option 2, there's none. This happens both on Debug builds and Release builds, just FYI because on Xcode 16 Debug builds seem to be slower than expected: https://indieweb.social/@curtclifton/113273571392595819 I've profiled it and the SwiftData fetches seem blazing fast, the Hang occurs when accessing the items property from the List. Is there anything I'm overlooking or it's just as fast as it can be right now?
10
4
1.9k
Oct ’24
NavigationStack $path cleared on dealloc?
Hello, This code has a NavigationSplitView, whose sidebar is a List and its detail contains a NavigationStack. It is controlled by two @Observable properties, a selection and a path. The path shown in the detail depends upon the selection in the List. If I programmatically change the selection and path, the path will be set, but via SwiftUI backtraces (pasted below), it will clear out my path. This makes my code lose state. What am I doing wrong? Is this a bug? I'm using Xcode16 running against the iOS 18 simulator (although this also happens with iOS17). To reproduce, Launch the App, Note that you are on the "first" selection. Tap "Nav Path: Path: second-100". You'll go to the "second" selection, but the path will be empty. If you place a breakpoint when the $path is cleared, you'll see it is being cleared by SwiftUI. Backtrace when the path is emptied: (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 * frame #0: 0x00000001043295c0 DoubleNav.debug.dylib`Navigation.path.setter(newValue=0 values) at ContentView.swift:86:22 frame #1: 0x00000001043296d0 DoubleNav.debug.dylib`key path setter for Navigation.path : Navigation at <compiler-generated>:0 frame #2: 0x000000019485b500 libswiftCore.dylib`Swift.NonmutatingWritebackBuffer.__deallocating_deinit + 132 frame #3: 0x0000000194a351c4 libswiftCore.dylib`_swift_release_dealloc + 28 frame #4: 0x0000000194a35bd4 libswiftCore.dylib`bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 156 frame #5: 0x00000001d2f5f584 SwiftUICore`closure #1 () -> () in SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 132 frame #6: 0x00000001d2f5f6b0 SwiftUICore`partial apply forwarder for closure #1 () -> () in SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 28 frame #7: 0x00000001d2801da8 SwiftUICore`generic specialization <()> of closure #1 () throws -> τ_0_0 in SwiftUI.withTransaction<τ_0_0>(SwiftUI.Transaction, () throws -> τ_0_0) throws -> τ_0_0 + 296 frame #8: 0x00000001d2f5f4d8 SwiftUICore`SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 336 frame #9: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #10: 0x00000001d2c42c84 SwiftUICore`protocol witness for SwiftUI.Location.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () in conformance SwiftUI.LocationBox<τ_0_0> : SwiftUI.Location in SwiftUI + 20 frame #11: 0x00000001d2c42e90 SwiftUICore`SwiftUI.ProjectedLocation.set(_: τ_0_1.Projected, transaction: SwiftUI.Transaction) -> () + 196 frame #12: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #13: 0x00000001d2cf2fe8 SwiftUICore`SwiftUI.Binding.ScopedLocation.set(_: τ_0_0, transaction: SwiftUI.Transaction) -> () + 28 frame #14: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #15: 0x00000001d2cf3190 SwiftUICore`function signature specialization <Arg[0] = Owned To Guaranteed> of SwiftUI.Binding.wrappedValue.setter : τ_0_0 + 32 frame #16: 0x00000001d2cf12c0 SwiftUICore`SwiftUI.Binding.wrappedValue.setter : τ_0_0 + 28 frame #17: 0x00000001d1d0e538 SwiftUI`closure #1 () -> () in SwiftUI.NavigationColumnState.popAllForSelectionChange(popReplacedRoots: Swift.Bool) -> SwiftUI.NavigationState.RequestResults + 524 frame #18: 0x00000001d281be58 SwiftUICore`partial apply forwarder for reabstraction thunk helper from @callee_guaranteed (@guaranteed Swift.Dictionary<__C.NSAttributedStringKey, Any>, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () to @escaping @callee_guaranteed (@guaranteed Swift.Dictionary<__C.NSAttributedStringKey, Any>, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () + 20 frame #19: 0x00000001d2b3b64c SwiftUICore`static SwiftUI.Update.dispatchActions() -> () + 1080 frame #20: 0x00000001d2b3ac4c SwiftUICore`static SwiftUI.Update.end() -> () + 108 frame #21: 0x00000001d18daf44 SwiftUI`closure #1 (Swift.Optional<Swift.UnsafeMutableRawPointer>, Swift.Double, Swift.UnsafePointer<__C._UIUpdateTiming>) -> () in static SwiftUI.UIKitUpdateCycle.addPreCommitObserver(() -> ()) -> () + 168 frame #22: 0x00000001d18dafbc SwiftUI`reabstraction thunk helper from @escaping @callee_guaranteed (@unowned Swift.Optional<Swift.UnsafeMutableRawPointer>, @unowned Swift.Double, @unowned Swift.UnsafePointer<__C._UIUpdateTiming>) -> () to @escaping @callee_unowned @convention(block) (@unowned Swift.Optional<Swift.UnsafeMutableRawPointer>, @unowned Swift.Double, @unowned Swift.UnsafePointer<__C._UIUpdateTiming>) -> () + 64 frame #23: 0x0000000185030388 UIKitCore`_UIUpdateSequenceRun + 76 frame #24: 0x00000001859d22e8 UIKitCore`schedulerStepScheduledMainSection + 168 frame #25: 0x00000001859d1720 UIKitCore`runloopSourceCallback + 80 frame #26: 0x000000018041b324 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #27: 0x000000018041b26c CoreFoundation`__CFRunLoopDoSource0 + 172 frame #28: 0x000000018041a9d0 CoreFoundation`__CFRunLoopDoSources0 + 232 frame #29: 0x00000001804150b0 CoreFoundation`__CFRunLoopRun + 788 frame #30: 0x0000000180414960 CoreFoundation`CFRunLoopRunSpecific + 536 frame #31: 0x0000000190183b10 GraphicsServices`GSEventRunModal + 160 frame #32: 0x0000000185aa2b40 UIKitCore`-[UIApplication _run] + 796 frame #33: 0x0000000185aa6d38 UIKitCore`UIApplicationMain + 124 frame #34: 0x00000001d1e2eab4 SwiftUI`closure #1 (Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Never in SwiftUI.KitRendererCommon(Swift.AnyObject.Type) -> Swift.Never + 164 frame #35: 0x00000001d1e2e7dc SwiftUI`SwiftUI.runApp<τ_0_0 where τ_0_0: SwiftUI.App>(τ_0_0) -> Swift.Never + 84 frame #36: 0x00000001d1b70c8c SwiftUI`static SwiftUI.App.main() -> () + 148 frame #37: 0x0000000104333df0 DoubleNav.debug.dylib`static DoubleNavApp.$main() at <compiler-generated>:0 frame #38: 0x0000000104333ea0 DoubleNav.debug.dylib`main at DoubleNavApp.swift:11:8 frame #39: 0x00000001048f9410 dyld_sim`start_sim + 20 frame #40: 0x000000010440e154 dyld`start + 2476 Thanks for any tips! Here's the code.
Topic: UI Frameworks SubTopic: SwiftUI
2
0
507
Oct ’24
Quick Look Extension does not load MapKit map properly anymore, after macOS Sequoia
It appears that starting with macOS Sequoia, Quick Look Preview extension no longer loads MapKit maps correctly anymore. Map tiles do not appear, leaving users with a beige background. Users report that polylines do render correctly, but annotations appears black. This was previously working fine in prior macOS versions including Sonoma. STEPS TO REPRODUCE Create a macOS app project, with an associated document. Ensure project has a Quick Look preview extension, with necessary basic setups. Ensure that the extension mentioned in (2) must have a MKMapView. Any other cosmetic changes, etc, does not need to be implemented to observe the base issue. Do note that it has been reported that in addition to the map tiles not loading, annotations don't render correctly as well.
5
2
1.1k
Oct ’24
NSMutableAttributedString and NSTextTable
I'm using NSTextTable to format panels of stand-out text within body text. Paragraphs within the panel are handled as individual NSTextTableBlocks within the table. Each block is added to the NSMutableParagraphStyle that is part of the attributed string within the block's attributes. That's all fine and it works. But... Occasionally I see undrawn lines within the panel. These disappear (or sometimes appear) when the parent window (and thus the NSTextView holding the rendered attributed string) is resized. Lines do not always appear, and when they do they are not always in the same place. The height of the gap varies. I see this behaviour with these panels and with tables. What's common to both cases is not only the use of NSTextTable and NSTextTableBlock etc., but crucially (I think) the use of block margins. If I disable margins (which looks OK for the panels, but isn't right for tables), the problem disappears. So, a bug or (more likely) I'm missing a key part of view rendering or margin set up. But what? Code here: https://github.com/smittytone/PreviewMarkdown/blob/930f5f32aa0b3b77ec3f4f53436a79e10bb26f18/Markdown%20Previewer/Styler.swift#L882 Running 14.6.1 on an M3. I'm using TextKit 1 because I'm using an NSLayoutManager subclass to override certain text underlines (not used in panels as outlined above, or tables).
Topic: UI Frameworks SubTopic: AppKit
Replies
3
Boosts
0
Views
439
Activity
Sep ’24
Swift Charts - weak scrolling performance
Hello there! I wanted to give a native scrolling mechanism for the Swift Charts Graph a try and experiment a bit if the scenario that we try to achieve might be possible, but it seems that the Swift Charts scrolling performance is very poor. The graph was created as follows: X-axis is created based on a date range, Y-axis is created based on an integer values between moreless 0-320 value. the graph is scrollable horizontally only (x-axis), The time range (x-axis) for the scrolling content was set to one year from now date (so the user can scroll one year into the past as a minimum visible date (.chartXScale). The X-axis shows 3 hours of data per screen width (.chartXVisibleDomain). The data points for the graph are generated once when screen is about to appear so that the Charts engine can use it (no lazy loading implemented yet). The line data points (LineMark views) consist of 2880 data points distributed every 5 minutes which simulates - two days of continuous data stream that we want to present. The rest of the graph displays no data at all. The performance result: The graph on the initial loading phase is frozen for about 10-15 seconds until the data appears on the graph. Scrolling is very laggy - the CPU usage is 100% and is unacceptable for the end users. If we show no data at all on the graph (so no LineMark views are created at all) - the result is similar - the empty graph scrolling is also very laggy. Below I am sharing a test code: @main struct ChartsTestApp: App { var body: some Scene { WindowGroup { ContentView() Spacer() } } } struct LineDataPoint: Identifiable, Equatable { var id: Int let date: Date let value: Int } actor TestData { func generate(startDate: Date) async -> [LineDataPoint] { var values: [LineDataPoint] = [] for i in 0..<(1440 * 2) { values.append( LineDataPoint( id: i, date: startDate.addingTimeInterval( TimeInterval(60 * 5 * i) // Every 5 minutes ), value: Int.random(in: 1...100) ) ) } return values } } struct ContentView: View { var startDate: Date { return endDate.addingTimeInterval(-3600*24*30*12) // one year into the past from now } let endDate = Date() @State var dataPoints: [LineDataPoint] = [] var body: some View { Chart { ForEach(dataPoints) { item in LineMark( x: .value("Date", item.date), y: .value("Value", item.value), series: .value("Series", "Test") ) } } .frame(height: 200) .chartScrollableAxes(.horizontal) .chartYAxis(.hidden) .chartXScale(domain: startDate...endDate) // one year possibility to scroll back .chartXVisibleDomain(length: 3600 * 3) // 3 hours visible on screen .onAppear { Task { dataPoints = await TestData().generate(startDate: startDate) } } } } I would be grateful for any insights or suggestions on how to improve it or if it's planned to be improved in the future. Currently, I use UIKit CollectionView where we split the graph into smaller chunks of the graph and we present the SwiftUI Chart content in the cells, so we use the scrolling offered there. I wonder if it's possible to use native SwiftUI for such a scenario so that later on we could also implement some kind of lazy loading of the data as the user scrolls into the past.
Replies
4
Boosts
2
Views
1.5k
Activity
Sep ’24
Widget link broken by `.desaturated` image rendering mode
Using desaturated mode on an image in a widget will break any links or buttons that use the image as their 'label'. Using the following will just open the app as if there was no link at all - therefore just using the fallback userActivity handler, or any .widgetURL() urls provided. Link(destination: URL(string: "bug://never-works")!) { Image("puppy") .widgetAccentedRenderingMode(.desaturated) } The same goes for buttons: Button(intent: MyDemoIntent()) { Image("puppy") .widgetAccentedRenderingMode(.desaturated) } I've tried hacky solutions like putting the link behind the image using a ZStack, and disabling hit testing on the image, but they don't work. Anything else to try? Logged as Feedback #15152620.
Replies
8
Boosts
5
Views
933
Activity
Sep ’24
SwiftUI toolbar in MacOS 15 bug ?
struct ContentView: View { var body: some View { NavigationSplitView { List { Text("row 1") Text("row 2") Text("row 3") } .toolbar(content: { ToolbarItem { Button("aa", action: onToolbar) } }) } detail: { HSplitView { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .toolbar(id: "toolbar", content: { ToolbarItem(id: "toolbar-1") { Button("bb", action: onToolbar) } }) .padding() Text("right") } }.navigationTitle("") } func onToolbar() {} } Run & Crash NSToolbar 0x6000005665b0 already contains an item with the identifier com.apple.SwiftUI.splitViewSeparator-0. Duplicate items of this type are not allowed.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
13
Boosts
2
Views
1.7k
Activity
Sep ’24
[macOS Sequoia] Using RegisterEventHotkey with option and shift modifiers doesn't working anymore
Hello. In my app, I use RegisterEventHotkey to implement global keyboard shortcuts to trigger actions. Up until macOS Sequoia, I was able to use a keyboard shortcut with option and shift as the modifiers, like option shift 2 (⌥ ⇧ 2). Now, on macOS Sequoia, using RegisterEventHotkey to register a hotkey with those exact modifiers (option and shift), regardless of the key, fails with the error -9868 (eventInternalErr). Is this a documented and wanted change, or is this a bug? Other modifier keys (just command, command option, command shift, command control, control shift, etc), all work. Any insight into this would be appreciated. (Feedback filed: FB15163561) Thank you, Matthias
Replies
22
Boosts
10
Views
14k
Activity
Sep ’24
SwiftUI: AlignmentGuide in Overlay not working when in if block
Scenario A SwiftUI view has an overlay with alignment: .top, the content uses .alignmentGuide(.top) {} to adjust the placement. Issue When the content of the overlay is in an if-block, the alignment guide is not adjusted. Example code The example shows 2 views. Not working example, where the content is an if-block. Working example, where the content is not in an if-block Screenshot: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/blob/main/screenshot.png Tested on - Xcode Version 16.0 RC (16A242) on iOS 18.0 Code // Not working .overlay(alignment: .top) { if true { // This line causes .alignmentGuide() to fail Text("Test") .alignmentGuide(.top, computeValue: { dimension in dimension[.bottom] }) } } // Working .overlay(alignment: .top) { Text("Test") .alignmentGuide(.top, computeValue: { dimension in dimension[.bottom] }) } Also created a Feedback: FB15248296 Example Project is here: https://github.com/simonnickel/FB15248296-SwiftUIAlignmentGuideInOverlayConditional/tree/main
Replies
2
Boosts
2
Views
513
Activity
Sep ’24
allowsExpansionToolTips in SwiftUI?
Is there a SwiftUI version of NSControl.allowsExpansionToolTips? That is, showing a tool tip with the full text when (and only when) a text item is truncated? Or do I need to use a hosting view to get that behavior?
Replies
2
Boosts
0
Views
521
Activity
Sep ’24
Tinted widgets have an inset background in the widget gallery only
When the home screen is in tinted mode in iOS 18, the widget gallery seems to display widgets with the background inset from the edge of the widget (i.e. negative padding). You can see this behavior in the Apple News widget too, where the full-bleed content outside of the inset is very light. This only happens in the sample gallery, not when the widgets are placed on the home screen. For my widgets, this outer edge contains important content and the clipping behavior makes the widgets look poorly designed when viewed in the gallery. Is there any way to turn this behavior off and just show the widget normally in the gallery with no weird inset — the way it will actually display when added to the home screen? If it matters, my widgets are currently configured with: .contentMarginsDisabled() .containerBackground(for: .widget) { // ... (background color) */ }
Replies
3
Boosts
0
Views
621
Activity
Sep ’24
UIDocumentPickerViewController provides corrupt copy of file when user taps multiple times on file
We're trying to implement a backup/restore data feature in our business productivity iPad app using UIDocumentPickerViewController and AppleArchive, but discovered odd behavior of [UIDocumentPickerViewController initForOpeningContentTypes: asCopy:YES] when reading large archive files from a USB drive. We've duplicated this behavior with iPadOS 16.6.1 and 17.7 when building our app with Xcode 15.4 targeting minimum deployment of iPadOS 16. We haven't tested this with bleeding edge iPadOS 18. Here's our Objective-C code which presents the picker: NSArray* contentTypeArray = @[UTTypeAppleArchive]; UIDocumentPickerViewController* docPickerVC = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:contentTypeArray asCopy:YES]; docPickerVC.delegate = self; docPickerVC.allowsMultipleSelection = NO; docPickerVC.shouldShowFileExtensions = YES; docPickerVC.modalPresentationStyle = UIModalPresentationPopover; docPickerVC.popoverPresentationController.sourceView = self.view; [self presentViewController:docPickerVC animated:YES completion:nil]; The UIDocumentPickerViewController remains visible until the selected external archive file has been copied from the USB drive to the app's local tmp sandbox. This may take several seconds due to the slow access speed of the USB drive. During this time the UIDocumentPickerViewController does NOT disable its tableview rows displaying files found on the USB drive. Even the most patient user will tap the desired filename a second (or third or fourth) time since the user's initial tap appears to have been ignored by UIDocumentPickerViewController, which lacks sufficient UI feedback showing it's busy copying the selected file. When the user taps the file a second time, UIDocumentPickerViewController apparently begins to copy the archive file once again. The end result is a truncated copy of the selected file based on the time between taps. For instance, a 788 MB source archive may be copied as a 56 MB file. Here, the UIDocumentPickerDelegate receives a 56 MB file instead of the original 788 MB of data. Not surprisingly, AppleArchive fails to decrypt the local copy of the archive because it's missing data. Instead of failing gracefully, AppleArchive crashes in AAArchiveStreamClose() (see forums post 765102 for details). Does anyone know if there's a workaround for this strange behavior of UIDocumentPickerViewController?
Replies
9
Boosts
0
Views
1.1k
Activity
Sep ’24
Navigation Bar Elements Disappear When Using UIPageViewController in SwiftUI Under Low Power Mode
Problem Description: In a SwiftUI application, I've wrapped UIKit's UIPageViewController using UIViewControllerRepresentable, naming the wrapped class PagedInfiniteScrollView. This component causes navigation bar elements (title and buttons) to disappear. This issue only occurs in Low Power Mode on a physical device. Steps to Reproduce: Enable Low Power Mode on a physical device and open the app's home page. From the home page, open a detail sheet containing PagedInfiniteScrollView. This detail page include a navigation title and a toolbar button in the top-right corner. PagedInfiniteScrollView supports horizontal swiping to switch pages. Tap the toolbar button in the top-right corner of the detail page to open an edit sheet. Without making any changes, close the edit sheet and return to the detail page. On the detail page, swipe left and right on the PagedInfiniteScrollView. Expected Result: When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page should remain visible. Actual Result: When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page disappear. import SwiftUI @main struct CalendarApp: App { var body: some Scene { WindowGroup { ContentView() } } } import SwiftUI struct ContentView: View { @State private var showDetailSheet = false @State private var currentPage: Int = 0 var body: some View { NavigationStack { Button { showDetailSheet = true } label: { Text("show Calendar sheet") } .sheet(isPresented: $showDetailSheet) { DetailSheet(currentPage: $currentPage) } } } } struct DetailSheet: View { @Binding var currentPage: Int @State private var showEditSheet = false var body: some View { NavigationStack { PagedInfiniteScrollView(content: { pageIndex in Text("\(pageIndex)") .frame(width: 200, height: 200) .background(Color.blue) }, currentPage: $currentPage) .sheet(isPresented: $showEditSheet, content: { Text("edit") }) .navigationTitle("Detail") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItemGroup(placement: .topBarTrailing) { Button { showEditSheet = true } label: { Text("Edit") } } } } } } import SwiftUI import UIKit struct PagedInfiniteScrollView<Content: View>: UIViewControllerRepresentable { typealias UIViewControllerType = UIPageViewController let content: (Int) -> Content @Binding var currentPage: Int func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> UIPageViewController { let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) pageViewController.dataSource = context.coordinator pageViewController.delegate = context.coordinator let initialViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) })) pageViewController.setViewControllers([initialViewController], direction: .forward, animated: false, completion: nil) return pageViewController } func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) { let currentViewController = uiViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>> let currentIndex = currentViewController?.rootView.index ?? 0 if currentPage != currentIndex { let direction: UIPageViewController.NavigationDirection = currentPage > currentIndex ? .forward : .reverse let newViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) })) uiViewController.setViewControllers([newViewController], direction: direction, animated: true, completion: nil) } } class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var parent: PagedInfiniteScrollView init(_ parent: PagedInfiniteScrollView) { self.parent = parent } func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else { return nil } let previousIndex = currentIndex - 1 return UIHostingController(rootView: IdentifiableContent(index: previousIndex, content: { parent.content(previousIndex) })) } func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else { return nil } let nextIndex = currentIndex + 1 return UIHostingController(rootView: IdentifiableContent(index: nextIndex, content: { parent.content(nextIndex) })) } func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { if completed, let currentView = pageViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? { parent.currentPage = currentIndex } } } } extension PagedInfiniteScrollView { struct IdentifiableContent<Content: View>: View { let index: Int let content: Content init(index: Int, @ViewBuilder content: () -> Content) { self.index = index self.content = content() } var body: some View { content } } }
Replies
5
Boosts
1
Views
902
Activity
Oct ’24
Swipe left/right only works when iPhone is vertical enough?
If I put the phone flat on a table, the left and right swipe gestures is not working but up and down gestures works. Only when I put the iPhone to some vertical degree, the left and right swipe works. Tested on 2 iPhone 7 Plus and 2 iPhone 13. Anyone has similar experience? If yes, why?
Replies
2
Boosts
0
Views
737
Activity
Oct ’24
AppLaunchTimeoutError
Hi. I tried to launch SwiftUI preview. But I got an error "AppLaunchTimeoutError" I attach the diagnostics. Does anyone know how to fix this problem? memo.txt
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
1
Views
427
Activity
Oct ’24
Navigation Bar animation upon Tab change
Hi. Since Xcode 16 and/or iOS 18.0 (I upgraded at the same time), I have an strange effect in the lower (let's say) 20% section of the Navigation Bar when changing to another tab, and this independently if large titles are used or not. Mentioned section is brighter or darker than the rest of the Navigation Bar background, depending on which background tint is used. This effect lasts about 0.3 seconds, but is clearly visible, quite disturbing and new as of Xcode 16 and/or iOS 18.0. I use the code below in AppDelegate to get a gradient coloured Navigation Bar background. let appearance = UINavigationBarAppearance() UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().compactAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().compactScrollEdgeAppearance = appearance If I don't use above code., the background color is filled and without gradient. Subject effect doesn't show in this case. The effect basically looks like when changing tab, the new Navigation Bar background doesn't clear right away, and keeps the background from the previous Navigation Bar for 0.3 seconds before new one Navigation Bar background is rendered. I spent quite some time on changing every possible setting, in code as well as storyboard ... no success so far. Any ideas how to disable this undesired animation?
Replies
1
Boosts
0
Views
800
Activity
Oct ’24
SwiftUI List insertion changes aren't animated on macOS 15
I've been struggling with this issue since the release of macOS 15 Sequoia. I'm wondering if anyone else has encountered it or if anyone has a workaround to fix it. Inserting a new element into the array that acts as data source for a SwiftUI List with a ForEach is never animated even if the insertion is wrapped in a withAnimation() call. It seems that some other changes can be automated though: e.g. calls to shuffle() on the array successfully animate the changes. This used to work fine on macOS 14, but stopped working on macOS 15. I created a very simple project to reproduce the issue: import SwiftUI @main struct TestApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct IdentifiableItem: Identifiable { let id = UUID() var name: String { "Item \(id)" } } struct ContentView: View { @State var items: [IdentifiableItem] = [ IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), IdentifiableItem(), ] var body: some View { List { ForEach(items) { item in Text(item.name) } } Button("Add Item") { withAnimation { items.insert(IdentifiableItem(), at: 0) } } Button("Shuffle Items") { withAnimation { items.shuffle() } } } } How to reproduce Copy the code below in an Xcode project. Run it on macOS 15. Hit the "Add Item" button Expected: A new item is inserted with animation. Result: A new item is inserted without animation. How to prove this is a regression Follow the same steps above but run on macOS 14. A new item is inserted with animation.
Replies
3
Boosts
1
Views
686
Activity
Oct ’24
UICollectionView Move Item Method Not Called in iOS 18
Summary In iOS 18, the UICollectionViewDelegate method collectionView(_:targetIndexPathForMoveOfItemFromOriginalIndexPath:atCurrentIndexPath:toProposedIndexPath:) is not being called when moving items in a UICollectionView. This method works as expected in iOS 17.5 and earlier versions. Steps to Reproduce Create a UICollectionView with drag and drop enabled. Implement the UICollectionViewDelegate method: func collectionView(_ collectionView: UICollectionView, targetIndexPathForMoveOfItemFromOriginalIndexPath originalIndexPath: IndexPath, atCurrentIndexPath currentIndexPath: IndexPath, toProposedIndexPath proposedIndexPath: IndexPath) -> IndexPath { print("🐸 Move") return proposedIndexPath } Run the app on iOS 18. Attempt to drag and drop items within the collection view. Expected Behavior The method should be called during the drag and drop operation, and "🐸 Move" should be printed to the console. Actual Behavior The method is not called, and nothing is printed to the console. The drag and drop operation still occurs, but without invoking this delegate method. Configuration iOS Version: 18 Xcode Version: Xcode 16.0.0
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
4
Boosts
3
Views
750
Activity
Oct ’24
Using `compactTabIdentifiers` and other iOS 18+ tab APIs
Hello I'm working on implementing some changes to my app's tab bar, particularly to support some features in the new iPad floating tab bar, which has required me to adopt some of the new tab APIs from iOS 18. The issue I've encountered is that I would like the tabs visible in regular (floating tab bar) and compact (bottom tab bar) to be slightly different. The compact variant should show a subset of the tabs visible on iPad. For example: Floating tab bar: Use tabs A, B, C Bottom tab bar: Use tabs A, B I'm finding this quite difficult, especially in the scenario of split view on iPad, where the size class and tab bar location can change as the user interacts with the app. I went down the route of changing my tab bar controller's tabs property when the trait collection changed tabBarController.tabs = eligibleTabs where eligibleTabs for compact passes tabs AB, and when in regular - ABC. This throws an exception UIViewController cannot be shared between multiple UITab *** Assertion failure in -[UITab viewController], UITab.m:173 I'm not sharing view controllers between tabs. I can only assume that the system doesn't like me passing the same values (always at least tabs A & B) multiple times to the tabs property. I then later noticed a new iOS 18 property on UITabBarController - compactTabIdentifiers. This seemed like exactly what I was looking for: An optional filter to display only select root-level tabs when in a compact appearance. Default is nil, which would make all tabs available. So I changed my implementation: tabBarController.tabs = [tabA, tabB, tabC] tabBarController.compactTabIdentifiers = [tabAIdentifier, tabBIdentifier] and don't make any explicit updates when split view is enabled. Unfortunately this doesn't seem to work, at least not how I would expect it to. When enabling split view on iPad, the bottom tab bar appears, but it doesn't respect the tab identifiers I passed in tabBarController.compactTabIdentifiers. I'm not sure if this a bug or that I'm not really understanding how to use tabBarController.compactTabIdentifiers. Does anyone have any insight on this?
Topic: UI Frameworks SubTopic: UIKit
Replies
2
Boosts
2
Views
912
Activity
Oct ’24
Slow rendering List backed by SwiftData @Query
Hello, I've a question about performance when trying to render lots of items coming from SwiftData via a @Query on a SwiftUI List. Here's my setup: // Item.swift: @Model final class Item: Identifiable { var timestamp: Date var isOptionA: Bool init() { self.timestamp = Date() self.isOptionA = Bool.random() } } // Menu.swift enum Menu: String, CaseIterable, Hashable, Identifiable { var id: String { rawValue } case optionA case optionB case all var predicate: Predicate<Item> { switch self { case .optionA: return #Predicate { $0.isOptionA } case .optionB: return #Predicate { !$0.isOptionA } case .all: return #Predicate { _ in true } } } } // SlowData.swift @main struct SlowDataApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([Item.self]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) return try! ModelContainer(for: schema, configurations: [modelConfiguration]) }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } // ContentView.swift struct ContentView: View { @Environment(\.modelContext) private var modelContext @State var selection: Menu? = .optionA var body: some View { NavigationSplitView { List(Menu.allCases, selection: $selection) { menu in Text(menu.rawValue).tag(menu) } } detail: { DemoListView(selectedMenu: $selection) }.onAppear { // Do this just once // (0..<15_000).forEach { index in // let item = Item() // modelContext.insert(item) // } } } } // DemoListView.swift struct DemoListView: View { @Binding var selectedMenu: Menu? @Query private var items: [Item] init(selectedMenu: Binding<Menu?>) { self._selectedMenu = selectedMenu self._items = Query(filter: selectedMenu.wrappedValue?.predicate, sort: \.timestamp) } var body: some View { // Option 1: touching `items` = slow! List(items) { item in Text(item.timestamp.description) } // Option 2: Not touching `items` = fast! // List { // Text("Not accessing `items` here") // } .navigationTitle(selectedMenu?.rawValue ?? "N/A") } } When I use Option 1 on DemoListView, there's a noticeable delay on the navigation. If I use Option 2, there's none. This happens both on Debug builds and Release builds, just FYI because on Xcode 16 Debug builds seem to be slower than expected: https://indieweb.social/@curtclifton/113273571392595819 I've profiled it and the SwiftData fetches seem blazing fast, the Hang occurs when accessing the items property from the List. Is there anything I'm overlooking or it's just as fast as it can be right now?
Replies
10
Boosts
4
Views
1.9k
Activity
Oct ’24
NavigationStack $path cleared on dealloc?
Hello, This code has a NavigationSplitView, whose sidebar is a List and its detail contains a NavigationStack. It is controlled by two @Observable properties, a selection and a path. The path shown in the detail depends upon the selection in the List. If I programmatically change the selection and path, the path will be set, but via SwiftUI backtraces (pasted below), it will clear out my path. This makes my code lose state. What am I doing wrong? Is this a bug? I'm using Xcode16 running against the iOS 18 simulator (although this also happens with iOS17). To reproduce, Launch the App, Note that you are on the "first" selection. Tap "Nav Path: Path: second-100". You'll go to the "second" selection, but the path will be empty. If you place a breakpoint when the $path is cleared, you'll see it is being cleared by SwiftUI. Backtrace when the path is emptied: (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1 * frame #0: 0x00000001043295c0 DoubleNav.debug.dylib`Navigation.path.setter(newValue=0 values) at ContentView.swift:86:22 frame #1: 0x00000001043296d0 DoubleNav.debug.dylib`key path setter for Navigation.path : Navigation at <compiler-generated>:0 frame #2: 0x000000019485b500 libswiftCore.dylib`Swift.NonmutatingWritebackBuffer.__deallocating_deinit + 132 frame #3: 0x0000000194a351c4 libswiftCore.dylib`_swift_release_dealloc + 28 frame #4: 0x0000000194a35bd4 libswiftCore.dylib`bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 156 frame #5: 0x00000001d2f5f584 SwiftUICore`closure #1 () -> () in SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 132 frame #6: 0x00000001d2f5f6b0 SwiftUICore`partial apply forwarder for closure #1 () -> () in SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 28 frame #7: 0x00000001d2801da8 SwiftUICore`generic specialization <()> of closure #1 () throws -> τ_0_0 in SwiftUI.withTransaction<τ_0_0>(SwiftUI.Transaction, () throws -> τ_0_0) throws -> τ_0_0 + 296 frame #8: 0x00000001d2f5f4d8 SwiftUICore`SwiftUI.ObjectLocation.set(_: τ_0_1, transaction: SwiftUI.Transaction) -> () + 336 frame #9: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #10: 0x00000001d2c42c84 SwiftUICore`protocol witness for SwiftUI.Location.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () in conformance SwiftUI.LocationBox<τ_0_0> : SwiftUI.Location in SwiftUI + 20 frame #11: 0x00000001d2c42e90 SwiftUICore`SwiftUI.ProjectedLocation.set(_: τ_0_1.Projected, transaction: SwiftUI.Transaction) -> () + 196 frame #12: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #13: 0x00000001d2cf2fe8 SwiftUICore`SwiftUI.Binding.ScopedLocation.set(_: τ_0_0, transaction: SwiftUI.Transaction) -> () + 28 frame #14: 0x00000001d2c42468 SwiftUICore`SwiftUI.LocationBox.set(_: τ_0_0.Value, transaction: SwiftUI.Transaction) -> () + 148 frame #15: 0x00000001d2cf3190 SwiftUICore`function signature specialization <Arg[0] = Owned To Guaranteed> of SwiftUI.Binding.wrappedValue.setter : τ_0_0 + 32 frame #16: 0x00000001d2cf12c0 SwiftUICore`SwiftUI.Binding.wrappedValue.setter : τ_0_0 + 28 frame #17: 0x00000001d1d0e538 SwiftUI`closure #1 () -> () in SwiftUI.NavigationColumnState.popAllForSelectionChange(popReplacedRoots: Swift.Bool) -> SwiftUI.NavigationState.RequestResults + 524 frame #18: 0x00000001d281be58 SwiftUICore`partial apply forwarder for reabstraction thunk helper from @callee_guaranteed (@guaranteed Swift.Dictionary<__C.NSAttributedStringKey, Any>, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () to @escaping @callee_guaranteed (@guaranteed Swift.Dictionary<__C.NSAttributedStringKey, Any>, @unowned __C._NSRange, @unowned Swift.UnsafeMutablePointer<ObjectiveC.ObjCBool>) -> () + 20 frame #19: 0x00000001d2b3b64c SwiftUICore`static SwiftUI.Update.dispatchActions() -> () + 1080 frame #20: 0x00000001d2b3ac4c SwiftUICore`static SwiftUI.Update.end() -> () + 108 frame #21: 0x00000001d18daf44 SwiftUI`closure #1 (Swift.Optional<Swift.UnsafeMutableRawPointer>, Swift.Double, Swift.UnsafePointer<__C._UIUpdateTiming>) -> () in static SwiftUI.UIKitUpdateCycle.addPreCommitObserver(() -> ()) -> () + 168 frame #22: 0x00000001d18dafbc SwiftUI`reabstraction thunk helper from @escaping @callee_guaranteed (@unowned Swift.Optional<Swift.UnsafeMutableRawPointer>, @unowned Swift.Double, @unowned Swift.UnsafePointer<__C._UIUpdateTiming>) -> () to @escaping @callee_unowned @convention(block) (@unowned Swift.Optional<Swift.UnsafeMutableRawPointer>, @unowned Swift.Double, @unowned Swift.UnsafePointer<__C._UIUpdateTiming>) -> () + 64 frame #23: 0x0000000185030388 UIKitCore`_UIUpdateSequenceRun + 76 frame #24: 0x00000001859d22e8 UIKitCore`schedulerStepScheduledMainSection + 168 frame #25: 0x00000001859d1720 UIKitCore`runloopSourceCallback + 80 frame #26: 0x000000018041b324 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #27: 0x000000018041b26c CoreFoundation`__CFRunLoopDoSource0 + 172 frame #28: 0x000000018041a9d0 CoreFoundation`__CFRunLoopDoSources0 + 232 frame #29: 0x00000001804150b0 CoreFoundation`__CFRunLoopRun + 788 frame #30: 0x0000000180414960 CoreFoundation`CFRunLoopRunSpecific + 536 frame #31: 0x0000000190183b10 GraphicsServices`GSEventRunModal + 160 frame #32: 0x0000000185aa2b40 UIKitCore`-[UIApplication _run] + 796 frame #33: 0x0000000185aa6d38 UIKitCore`UIApplicationMain + 124 frame #34: 0x00000001d1e2eab4 SwiftUI`closure #1 (Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Never in SwiftUI.KitRendererCommon(Swift.AnyObject.Type) -> Swift.Never + 164 frame #35: 0x00000001d1e2e7dc SwiftUI`SwiftUI.runApp<τ_0_0 where τ_0_0: SwiftUI.App>(τ_0_0) -> Swift.Never + 84 frame #36: 0x00000001d1b70c8c SwiftUI`static SwiftUI.App.main() -> () + 148 frame #37: 0x0000000104333df0 DoubleNav.debug.dylib`static DoubleNavApp.$main() at <compiler-generated>:0 frame #38: 0x0000000104333ea0 DoubleNav.debug.dylib`main at DoubleNavApp.swift:11:8 frame #39: 0x00000001048f9410 dyld_sim`start_sim + 20 frame #40: 0x000000010440e154 dyld`start + 2476 Thanks for any tips! Here's the code.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
2
Boosts
0
Views
507
Activity
Oct ’24
Is there a way to open another view from the `.contextMenu` preview?
Hey there, Is there a way to launch another view by tapping on the preview of a context menu? Something like the behavior of the Photos app where tapping on the preview navigates to the details view. Tap gesture handlers on the preview don't seem to get called, even as high priority gestures. Thanks for the help! Gab
Replies
1
Boosts
1
Views
347
Activity
Oct ’24
Quick Look Extension does not load MapKit map properly anymore, after macOS Sequoia
It appears that starting with macOS Sequoia, Quick Look Preview extension no longer loads MapKit maps correctly anymore. Map tiles do not appear, leaving users with a beige background. Users report that polylines do render correctly, but annotations appears black. This was previously working fine in prior macOS versions including Sonoma. STEPS TO REPRODUCE Create a macOS app project, with an associated document. Ensure project has a Quick Look preview extension, with necessary basic setups. Ensure that the extension mentioned in (2) must have a MKMapView. Any other cosmetic changes, etc, does not need to be implemented to observe the base issue. Do note that it has been reported that in addition to the map tiles not loading, annotations don't render correctly as well.
Replies
5
Boosts
2
Views
1.1k
Activity
Oct ’24