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

All subtopics
Posts under UI Frameworks topic

Post

Replies

Boosts

Views

Activity

A Summary of the WWDC25 Group Lab - UI Frameworks
At WWDC25 we launched a new type of Lab event for the developer community - Group Labs. A Group Lab is a panel Q&A designed for a large audience of developers. Group Labs are a unique opportunity for the community to submit questions directly to a panel of Apple engineers and designers. Here are the highlights from the WWDC25 Group Lab for UI Frameworks. How would you recommend developers start adopting the new design? Start by focusing on the foundational structural elements of your application, working from the "top down" or "bottom up" based on your application's hierarchy. These structural changes, like edge-to-edge content and updated navigation and controls, often require corresponding code modifications. As a first step, recompile your application with the new SDK to see what updates are automatically applied, especially if you've been using standard controls. Then, carefully analyze where the new design elements can be applied to your UI, paying particular attention to custom controls or UI that could benefit from a refresh. Address the large structural items first then focus on smaller details is recommended. Will we need to migrate our UI code to Swift and SwiftUI to adopt the new design? No, you will not need to migrate your UI code to Swift and SwiftUI to adopt the new design. The UI frameworks fully support the new design, allowing you to migrate your app with as little effort as possible, especially if you've been using standard controls. The goal is to make it easy to adopt the new design, regardless of your current UI framework, to achieve a cohesive look across the operating system. What was the reason for choosing Liquid Glass over frosted glass, as used in visionOS? The choice of Liquid Glass was driven by the desire to bring content to life. The see-through nature of Liquid Glass enhances this effect. The appearance of Liquid Glass adapts based on its size; larger glass elements look more frosted, which aligns with the design of visionOS, where everything feels larger and benefits from the frosted look. What are best practices for apps that use customized navigation bars? The new design emphasizes behavior and transitions as much as static appearance. Consider whether you truly need a custom navigation bar, or if the system-provided controls can meet your needs. Explore new APIs for subtitles and custom views in navigation bars, designed to support common use cases. If you still require a custom solution, ensure you're respecting safe areas using APIs like SwiftUI's safeAreaInset. When working with Liquid Glass, group related buttons in shared containers to maintain design consistency. Finally, mark glass containers as interactive. For branding, instead of coloring the navigation bar directly, consider incorporating branding colors into the content area behind the Liquid Glass controls. This creates a dynamic effect where the color is visible through the glass and moves with the content as the user scrolls. I want to know why new UI Framework APIs aren’t backward compatible, specifically in SwiftUI? It leads to code with lots of if-else statements. Existing APIs have been updated to work with the new design where possible, ensuring that apps using those APIs will adopt the new design and function on both older and newer operating systems. However, new APIs often depend on deep integration across the framework and graphics stack, making backward compatibility impractical. When using these new APIs, it's important to consider how they fit within the context of the latest OS. The use of if-else statements allows you to maintain compatibility with older systems while taking full advantage of the new APIs and design features on newer systems. If you are using new APIs, it likely means you are implementing something very specific to the new design language. Using conditional code allows you to intentionally create different code paths for the new design versus older operating systems. Prefer to use if #available where appropriate to intentionally adopt new design elements. Are there any Liquid Glass materials in iOS or macOS that are only available as part of dedicated components? Or are all those materials available through new UIKit and AppKit views? Yes, some variations of the Liquid Glass material are exclusively available through dedicated components like sliders, segmented controls, and tab bars. However, the "regular" and "clear" glass materials should satisfy most application requirements. If you encounter situations where these options are insufficient, please file feedback. If I were to create an app today, how should I design it to make it future proof using Liquid Glass? The best approach to future-proof your app is to utilize standard system controls and design your UI to align with the standard system look and feel. Using the framework-provided declarative API generally leads to easier adoption of future design changes, as you're expressing intent rather than specifying pixel-perfect visuals. Pay close attention to the design sessions offered this year, which cover the design motivation behind the Liquid Glass material and best practices for its use. Is it possible to implement your own sidebar on macOS without NSSplitViewController, but still provide the Liquid Glass appearance? While technically possible to create a custom sidebar that approximates the Liquid Glass appearance without using NSSplitViewController, it is not recommended. The system implementation of the sidebar involves significant unseen complexity, including interlayering with scroll edge effects and fullscreen behaviors. NSSplitViewController provides the necessary level of abstraction for the framework to handle these details correctly. Regarding the SceneDelagate and scene based life-cycle, I would like to confirm that AppDelegate is not going away. Also if the above is a correct understanding, is there any advice as to what should, and should not, be moved to the SceneDelegate? UIApplicationDelegate is not going away and still serves a purpose for application-level interactions with the system and managing scenes at a higher level. Move code related to your app's scene or UI into the UISceneDelegate. Remember that adopting scenes doesn't necessarily mean supporting multiple scenes; an app can be scene-based but still support only one scene. Refer to the tech note Migrating to the UIKit scene-based life cycle and the Make your UIKit app more flexible WWDC25 session for more information.
Topic: UI Frameworks SubTopic: General
0
0
659
Jun ’25
UIDocumentPickerViewController Dismisses Automatically Twice When Selecting Multiple Files Quickly
Description: When using UIDocumentPickerViewController in iOS, if the user taps two or more files in quick succession, the picker view controller dismisses itself automatically twice. This results in an unintended behavior, leading to inconsistent screen states or redundant dismiss animations. Steps to Reproduce: Initialize a UIDocumentPickerViewController instance in UIDocumentPickerModeImport. UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[(NSString *)kUTTypeData] inMode:UIDocumentPickerModeImport]; documentPicker.delegate = self; documentPicker.modalPresentationStyle = UIModalPresentationFullScreen; documentPicker.allowsMultipleSelection = NO; [self presentViewController:documentPicker animated:YES completion:nil]; Launch the document picker. Quickly tap two or more files in succession. Expected Behavior: The UIDocumentPickerViewController should dismiss once and call the delegate method documentPicker:didPickDocumentsAtURLs: with the selected file(s). Actual Behavior: The UIDocumentPickerViewController automatically dismisses twice. This issue occurs even when no explicit call to dismissViewControllerAnimated: is made in the delegate method. Observed Results: The didPickDocumentsAtURLs: callback is invoked after the picker is already dismissed. During this process, unintended behavior such as multiple dismiss animations, view controller state corruption, or UI inconsistencies may occur. Impact: This issue disrupts the user experience and causes UI glitches when selecting documents. It prevents developers from having full control over dismiss behavior or mitigating the problem programmatically. Environment: iOS Version: iOS 15.0 and later (also reproduced on iOS 17.4) Device: Reproducible on multiple iPhone and iPad models Framework: UIKit Conclusion: This appears to be a system-level issue with UIDocumentPickerViewController. The automatic dismiss behavior is not correctlyhandling multiple taps, causing unintended dismiss events. Developers have no explicit way to prevent this behavior or gain full control over the dismissal process. We kindly request Apple to investigate and resolve this issue to ensure UIDocumentPickerViewController behaves as expected when interacting with multiple taps.
Topic: UI Frameworks SubTopic: UIKit Tags:
0
1
377
Dec ’24
iOS 18 crash issue for unnecessary dequeuing
My code extension MyViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "CollectionViewCellID", for: indexPath ) as? CollectionViewCell { cell.setup() return cell } return UICollectionViewCell() } func collectionView( _ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath ) { // Unnecessary dequeue guard collectionView.dequeueReusableCell( withReuseIdentifier: "CollectionViewCellID", for: indexPath ) is CollectionViewCell else { return } // My action for selecting cell print("Cell Selected") } } Error: *** Assertion failure in -[UICollectionView _updateVisibleCellsNow:], UICollectionView.m:5673 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Expected dequeued view to be returned to the collection view in preparation for display. When the collection view's data source is asked to provide a view for a given index path, ensure that a single view is dequeued and returned to the collection view. Avoid dequeuing views without a request from the collection view. For retrieving an existing view in the collection view, use -[UICollectionView cellForItemAtIndexPath:] or -[UICollectionView supplementaryViewForElementKind:atIndexPath:]. Solution: The problem was doing unnecessary dequeuing in didSelectItemAt when selecting the cell. In previous iOS like 17 or 16 or lower, it was allowed to dequeue where it is not really needed but from iOS 18, it may restricted to unnecessary dequeuing. So better to remove dequeue and use cellForItem(at) if we need to get cell from collection view. Example extension MyViewController: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { if let cell = collectionView.dequeueReusableCell( withReuseIdentifier: "CollectionViewCellID", for: indexPath ) as? CollectionViewCell { cell.setup() return cell } return UICollectionViewCell() } func collectionView( _ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath ) { guard collectionView.cellForItem(at: indexPath) is CollectionViewCell else { return } // My action for selecting cell print("Cell Selected") } }
0
0
3.6k
Dec ’24
iOS 18 iPad Toolbar .principal Placement
Hi, I am having some spacing issues with the new TabViewStyle.sidebarAdaptable. My app uses @ToolBarContentBuilder for navigationBar elements as there are some custom design requirements. One of these is title text that is set in the principal position. Simplified example: var body: some View { Text("Body") .toolbar { toolbar() } } @ToolbarContentBuilder private func toolbar() -> some ToolbarContent { ToolbarItem(placement: placement) { Text("Title") } } Everything with this setup works fine till I use an iPad with iOS 18 where the new toggleable sidebar is present. Upon switching to the sidebar layout the title does not move to the space adjacent to the navigation button (where the tab bar just was) and instead remains in its own bar, below the rest of the navigation. I've noticed that when navigationTitle is set the the title set in toolbar() does appear in the right place. var body: some View { Text("Body") .toolbar { toolbar() } .navigationTitle("anything") } Is this expected behaviour? How can I achieve a single line nav bar with a title set this way? Happy to provide a sandbox app to reproduce this issue. Many thanks, Matt
0
1
624
Dec ’24
SwiftUI Toolbar Item buttons not registering taps
Before I updated to iOS 18 everything worked fine. I pushed out an update to my application on the App Store and I had no issues. After updating to the latest OS many of my touch events are no longer working and I have no idea why. Sometimes when the app runs the touch events work fine and other times I can't click on half of my views & buttons. I am completely lost as to what might be happening. I am having issues all over the application but let's focus on the navigation stack and the toolbar item buttons. I will post some code snippets, I have been unable to replicate this in a small playground project. This is my setup, I have two buttons but lets focus on the home & notifications view. The custom Router import SwiftUI import Foundation @Observable class HomeRouter { var navPath = NavigationPath() @MainActor func navigate(to destination: HOME_ROUTES) { navPath.append(destination) } @MainActor func navigateBack() { navPath.removeLast() } @MainActor func navigateToRoot() { navPath.removeLast(navPath.count) } } Home View import os import SwiftUI import CoreLocation import NotificationCenter struct Home: View { @State public var router: HomeRouter @State private var showDetail = false @State private var showMoreFields = false @EnvironmentObject private var session: SessionStore private var log = Logger(subsystem: "com.olympsis.client", category: "home_view") init(router: HomeRouter = HomeRouter()) { self._router = State(initialValue: router) } var body: some View { NavigationStack(path: $router.navPath) { ScrollView(.vertical) { //MARK: - Welcome message WelcomeCard() .padding(.top, 25) .environmentObject(session) // MARK: - Announcements AnnouncementsView() .environmentObject(session) // MARK: - Next Events NextEvents() .environmentObject(session) // MARK: - Hot Events HotEvents() .environmentObject(session) // MARK: - Nearby Venues NearbyVenues() .environmentObject(session) Spacer(minLength: 100) } .toolbar { ToolbarItem(placement: .topBarLeading) { Text("Olympsis") .italic() .font(.largeTitle) .fontWeight(.black) } ToolbarItemGroup(placement: .topBarTrailing) { Button(action: { router.navigate(to: .messages) }) { ZStack(alignment: .topTrailing) { Image(systemName: "bubble.left.and.bubble.right") .foregroundStyle(Color.foreground) if session.invitations.count > 0 { NotificationCountView(value: $session.invitations.count) } } } Button(action: { router.navigate(to: .notifications) }) { ZStack(alignment: .topTrailing) { Image(systemName: "bell") .foregroundStyle(Color.foreground) if session.invitations.count > 0 { NotificationCountView(value: $session.invitations.count) } } } } } .background(Color("background-color/primary")) .navigationDestination(for: HOME_ROUTES.self, destination: { route in switch route { case .notifications: NotificationsView() .id(HOME_ROUTES.notifications) .environment(router) .environmentObject(session) .navigationBarBackButtonHidden() case .messages: HomeMessagesView() .id(HOME_ROUTES.messages) .environment(router) .environmentObject(session) .navigationBarBackButtonHidden() case .full_post_view(let id): AsyncPostView(postId: id) .id(HOME_ROUTES.full_post_view(id)) .environmentObject(session) .navigationBarBackButtonHidden() } }) } } } #Preview { Home() .environmentObject(SessionStore()) } The Notifications View import SwiftUI struct NotificationsView: View { @State private var notifications: [NotificationModel] = [] @Environment(HomeRouter.self) private var router @EnvironmentObject private var session: SessionStore var body: some View { ScrollView { if notifications.count > 0 { ForEach(notifications, id: \.id){ note in NotificationModelView(notification: note) } } else { VStack { Text("No new notifications") HStack { Spacer() } }.padding(.top, 50) } } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button(action:{ router.navigateBack() }) { Image(systemName: "chevron.left") .foregroundStyle(Color.foreground) } } ToolbarItem(placement: .principal) { Text("Notifications") } } .task { notifications = session.invitations.map({ i in NotificationModel(id: UUID().uuidString, type: "invitation", invite: i, body: "") }) } } } #Preview { NavigationStack { NotificationsView() .environment(HomeRouter()) .environmentObject(SessionStore()) } }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
482
Dec ’24
what it that func in CoreText ?
I can use CGContextShowText to display character. and what I should use in CoreText? I find there is NSAttributedString but there is lack of setShouldAntialias and setAllowsAntialiasing(Bool) in NSAttributedString I DO NOT want to use Antialiase which means I want to set Antialiase to FALSE when I display characters
1
0
332
Dec ’24
SwiftUI.Stepper bug from `onIncrement` and `onDecrement`?
Ok… I'm baffled here… this is very strange. Here is a SwiftUI app: import SwiftUI @main struct StepperDemoApp: App { func onIncrement() { print(#function) } func onDecrement() { print(#function) } var body: some Scene { WindowGroup { Stepper { Text("Stepper") } onIncrement: { self.onIncrement() } onDecrement: { self.onDecrement() } } } } When I run in the app in macOS (Xcode 16.0 (16A242) and macOS 14.6.1 (23G93)), I see some weird behavior from these buttons. My experiment is tapping + + + - - -. Here is what I see printed: onIncrement() onIncrement() onIncrement() onIncrement() onDecrement() What I expected was: onIncrement() onIncrement() onIncrement() onDecrement() onDecrement() onDecrement() Why is an extra onIncrement being called? And why is one onDecrement dropping on the floor? Deploying the app to iPhone Simulator does not repro this behavior (I see the six "correct" logs from iPhone Simulator).
9
0
751
Dec ’24
Image Playground API
Hi, WWDC24 videos have a lot of references to an "Image Playground" API, and the "What's New in AppKit" session even shows it in action, with a "ImagePlaygroundViewController". However, there doesn't seem to be any access to the new API, even with Xcode 16.2 beta. Am I missing something, or is that 'coming later'?
5
1
1.3k
Dec ’24
How to draw emojis like the Lock Screen customisation?
On iOS you can create a new Lock Screen that contains a bunch of emoji, and they'll get put on the screen in a repeating pattern, like this: When you have two or more emoji they're placed in alternating patterns, like this: How do I write something like that? I need to handle up to three emoji, and I need the canvas as large as the device's screen, and it needs to be saved as an image. Thanks! (I've already written an emojiToImage() extension on String, but it just plonks one massive emoji in the middle of an image, so I'm doing something wrong there.)
1
0
605
Dec ’24
DocumentGroup Fails to Display Document Content in Mac Catalyst App
When running a Mac Catalyst app that uses DocumentGroup, the app fails to display the document content. The document picker works as expected, but creating a new document or opening an existing one results in an empty window. This issue occurs regardless of whether “Optimize for Mac” or “Scale iPad” is selected. Steps to Reproduce: 1. Download the sample project provided by Apple for building a document-based app in SwiftUI. 2. Delete the macOS version of the project. 3. Add a Mac Catalyst version of the app. 4. In the Mac Catalyst settings, select “Optimize for Mac” (the bug also appears if it is “Scale iPad”). 5. Run the project on macOS. Expected Result: The app should correctly display the content of the document when creating or opening it. Actual Result: The app opens an empty window when a new document is created or an existing one is opened. Impact: We have received multiple 1-star reviews, and our retention has dropped by two-thirds due to this issue. Environment: Xcode 16.1; macOS 15.1 & 15.2 (on 15.0 it works fine) Has anyone experienced the same issue? I filed multiple reports so far.
3
1
957
Dec ’24
Why first View on my NavigationStack appears again when I switch branch?
Hello, In my app, I have an onboarding made of multiple steps in a NavigationStack. I also have a state variable that controls an if else root branch to show either the onboarding NavigationStack or the app content if the onboarding is finished. I noticed that when I end the onboarding (i.e. I switch to the other part of the if else root branch), the onAppear of the first View in the NavigationStack of the onboarding is called again. I don’t understand why. Is this a bug? Thanks, Axel enum Step { case one case two case three case four } struct ContentView: View { @State private var isFinished: Bool = false @State private var steps: [Step] = [] var body: some View { if isFinished { Button("Restart") { steps = [] isFinished = false } } else { NavigationStack(path: $steps) { VStack { Text("Start") .onAppear { print("onAppear: start") } Button("Go to step 1") { steps.append(.one) } } .navigationDestination(for: Step.self) { step in switch step { case .one: Button("Go to step 2") { steps.append(.two) } .onAppear { print("onAppear: step 1") } case .two: Button("Go to step 3") { steps.append(.three) } .onAppear { print("onAppear: step 2") } case .three: Button("Go to step 4") { steps.append(.four) } .onAppear { print("onAppear: step 3") } case .four: Button("End") { isFinished = true } .onAppear { print("onAppear: end") } } } } .onAppear { print("onAppear: NavigationStack") } } } }
6
0
777
Dec ’24
SwiftUI bug, when removing a TabView all other tabs have `onAppear` and `onDisappear` triggered
Xcode 14.1 Running on iPhone 14 Pro max simulator 16.1 Code... import SwiftUI struct ContentView: View { @State var loggedIn: Bool = false var body: some View { switch loggedIn { case false: Button("Login") { loggedIn = true } .onAppear { print("🍏 Login on appear") } .onDisappear { print("🍎 Login on disappear") } case true: TabView { NavigationView { Text("Home") .navigationBarTitle("Home") .onAppear { print("🍏 Home on appear") } .onDisappear { print("🍎 Home on disappear") } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Logout") { loggedIn = false } } } } .tabItem { Image(systemName: "house") Text("Home") } NavigationView { Text("Savings") .navigationBarTitle("Savings") .onAppear { print("🍏 Savings on appear") } .onDisappear { print("🍎 Savings on disappear") } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Logout") { loggedIn = false } } } } .tabItem { Image(systemName: "dollarsign.circle") Text("Savings") } NavigationView { Text("Profile") .navigationBarTitle("Profile") .onAppear { print("🍏 Profile on appear") } .onDisappear { print("🍎 Profile on disappear") } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button("Logout") { loggedIn = false } } } } .tabItem { Image(systemName: "person") Text("Profile") } } .onAppear { print("🍏 Tabview on appear") } .onDisappear { print("🍎 Tabview on disappear") } } } } Video of bug... https://youtu.be/oLKjRGL2lX0 Example steps... Launch app Tap Login Tap Savings tab Tap Home tab Tap Logout Expected Logs... 🍏 Login on appear 🍏 Tabview on appear 🍏 Home on appear 🍎 Login on disappear 🍏 Savings on appear 🍎 Home on disappear 🍏 Home on appear 🍎 Savings on disappear 🍏 Login on appear 🍎 Home on disappear 🍎 Tabview on disappear Actual logs... 🍏 Login on appear 🍏 Tabview on appear 🍏 Home on appear 🍎 Login on disappear 🍏 Savings on appear 🍎 Home on disappear 🍏 Home on appear 🍎 Savings on disappear 🍏 Login on appear 🍏 Savings on appear 🍎 Home on disappear 🍎 Savings on disappear 🍎 Tabview on disappear Error... 10 and 12 in the actual logs should not be there at all. For each tab that you have visited (that is not the current tab) it will call onAppear and onDisappear for it when the tab view is removed.
1
4
1.2k
Dec ’24
Why is ScrollView / LazyVStack retaining Views which causes memory leaks in the end?
Recently I noticed how my ViewModels aren't deallocating and they end up as a memory leaks. I found something similar in this thread but this is also happening without using @Observation. Check the source code below: class CellViewModel: Identifiable { let id = UUID() var color: Color = Color.red init() { print("init") } deinit { print("deinit") } } struct CellView: View { let viewModel: CellViewModel var body: some View { ZStack { Color(viewModel.color) Text(viewModel.id.uuidString) } } } @main struct LeakApp: App { @State var list = [CellViewModel]() var body: some Scene { WindowGroup { Button("Add") { list.append(CellViewModel()) } Button("Remove") { list = list.dropLast() } ScrollView { LazyVStack { ForEach(list) { model in CellView(viewModel: model) } } } } } } When I tap the Add button twice in the console I will see "init" message twice. So far so good. But then I click the Remove button twice and I don't see any "deinit" messages. I used the Debug Memory Graph in Xcode and it showed me that two CellViewModel objects are in the memory and they are owned by the CellView and some other objects that I don't know where are they coming from (I assume from SwiftUI internally). I tried using VStack instead of LazyVStack and that did worked a bit better but still not 100% "deinits" were in the Console. I tried using weak var struct CellView: View { weak var viewModel: CellViewModel? .... } but this also helped only partially. The only way to fully fix this is to have a separate class that holds the list of items and to use weak var viewModel: CellViewModel?. Something like this: class CellViewModel: Identifiable { let id = UUID() var color: Color = Color.red init() { print("init") } deinit { print("deinit") } } struct CellView: View { var viewModel: CellViewModel? var body: some View { ZStack { if let viewModel = viewModel { Color(viewModel.color) Text(viewModel.id.uuidString) } } } } @Observable class ListViewModel { var list = [CellViewModel]() func insert() { list.append(CellViewModel()) } func drop() { list = list.dropLast() } } @main struct LeakApp: App { @State var viewModel = ListViewModel() var body: some Scene { WindowGroup { Button("Add") { viewModel.insert() } Button("Remove") { viewModel.drop() } ScrollView { LazyVStack { ForEach(viewModel.list) { model in CellView(viewModel: model) } } } } } } But this won't work if I want to use @Bindable such as @Bindable var viewModel: CellViewModel? I don't understand why SwiftUI doesn't want to release the objects?
0
1
516
Dec ’24
Strange Behavior of UITabBarController selectedIndex and UINavigationController pop
UITabBarController | | VC_Tab1 --------------------------- VC_Tab2 | | | | VC_Tab1_Child VC_Tab2_Child | (HeaderView) | (MyButton) The structure of the view controllers and views in the project is as described above. <case 1> self.navigationController?.popToRootViewController(animated: false) tabBarController.selectedIndex = 1 When popToRootViewController(animated: false) is called in VC_Tab1_Child, followed by setting the tab controller’s selectedIndex = 1, the following results are observed: viewWillAppear(_:), <VC_Tab2_Child> deinit, <VC_Tab1_Child> viewDidAppear(_:), <VC_Tab2_Child> The originally expected results are as follows viewWillDisappear(_:), <VC_Tab1_Child> viewDidDisappear(_:), <VC_Tab1_Child> deinit, <VC_Tab1_Child> deinit, <HeaderView> deinit, <MyButton> headerView.backButton.rx.tap -> Event completed headerView.backButton.rx.tap -> isDisposed viewWillAppear(_:), <VC_Tab2_Child> viewDidAppear(_:), <VC_Tab2_Child> The HeaderView belonging to VC_Tab1_Child was not deallocated, and the resources associated with that view were also not released. Similarly, VC_Tab1_Child.viewWillDisappear and VC_Tab1_Child.didDisappear were not called. <case 2> self.navigationController?.popToRootViewController(animated: false) DispatchQueue.main.async { tabBarController.selectedIndex = 1 } After performing the pop operation as shown in the code and waiting for a short period before testing, the expected results were generally achieved. (However, rarely, the results were similar to those observed when called without async.)” <case 3> self.navigationController?.popToRootViewController(animated: false) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { tabBarController.selectedIndex = 1 } When a sufficient delay was ensured as described above, the expected results were achieved 100% of the time.” The abnormal behavior is more pronounced in iOS versions prior to 18 and varies depending on the iOS version. I couldn’t find any documentation explaining the unexpected behavior shown in the results above. What could be the cause? The simulation code is provided below. https://github.com/linusix/UITabBarController_Test2
0
0
294
Dec ’24
Navigation Bar Jumping Issue in SwiftUI with TabView and NavigationStack (Observed in Apple's Example Code)
I recently started exploring the latest version of SwiftUI and came across a issue while working with TabView and NavigationStack. I downloaded Apple’s provided example code and began making changes to explore new SwiftUI changes. i added navigationtitle and toolbarBackground to first two tab. However, I noticed that the navigation bar "jumps" or resets when switching between tabs, even in their own example implementation. Here’s a simplified version of the example code I was testing: file name - WatchNowView .navigationTitle("Watch Now") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color("AccentColor"),for: .navigationBar) file name - LibraryView .navigationTitle("Library") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color("AccentColor"),for: .navigationBar) Here is a sample code link (provided by Apple developer document) : destination-video I have attached a gif below demonstrating this issue: Questions: Is this behavior expected in the latest version of SwiftUI, or is it a bug in the framework's handling of TabView and NavigationStack? Is this behavior expected as all tabbar item have their own nativationStack? Are there any official recommendations for maintaining seamless navigation experiences when using navigationStack and TabView? This behavior detracts from the otherwise smooth experience SwiftUI aims to provide. If anyone has encountered this issue and found a workaround, I’d greatly appreciate your insights. I hope Apple can review this problem to enhance the usability of SwiftUI. Thank you!
3
3
320
Dec ’24
Navigation Bar Jumping Issue in SwiftUI with TabView and NavigationStack (Observed in Apple's Example Code)
I recently started exploring the latest version of SwiftUI and encountered an issue while working with TabView and NavigationStack. I downloaded the example code provided by Apple and began making changes to explore new SwiftUI features. However, I noticed that the navigation bar "jumps" or resets when switching between tabs, even in their example implementation. Here are some changes which I made below in the files: LibraryView: .navigationTitle("Library") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color("AccentColor"),for: .navigationBar) WatchNowView: .navigationTitle("Watch Now") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(Color("AccentColor"),for: .navigationBar) example code url :- destinationVideo I suspect the issue arises because each tab bar item has its own NavigationStack. When we set a navigation title for each view, the NavigationStack resets the navigation bar on view appearance, which causes this visual bug. Thank you!
0
0
300
Dec ’24
Using TextField:text:selection crashes on macOS
I am trying out the new TextField selection ability on macOS but it crashes in various different ways with extremely large stack traces. Looks like it is getting into re-entrant function calls. A similar problem is described on the SwiftUI forums with no responses yet. Here is my simple example struct ContentView: View { @State private var text: String = "" @State private var selection: TextSelection? var body: some View { TextField("Message", text: $text, selection: $selection) .padding() } } Setting text to a value like "Hallo World" causes an instant crash as soon as you start typing in the TextField. Setting text empty (as in example above) lets you edit the text but as it crashes as soon as you commit it (press enter). Any workarounds or fixes?
4
1
531
Dec ’24
Why am I unable to render .dae file in Playground?
This is my code in ContentView: import SwiftUI import SceneKit import PlaygroundSupport struct ContentView: View { var body: some View { VStack { Text("SceneKit with SwiftUI") .font(.headline) .padding() SceneView( scene: loadScene(), options: [.autoenablesDefaultLighting, .allowsCameraControl] ) .frame(width: 400, height: 400) .border(Color.gray, width: 1) } } } func loadScene() -> SCNScene? { if let fileURL = Bundle.main.url(forResource: "a", withExtension: "dae") { do { let scene = try SCNScene(url: fileURL, options: [ SCNSceneSource.LoadingOption.checkConsistency: true ]) print("Scene loaded successfully.") return scene } catch { print("Error loading scene: \(error.localizedDescription)") } } else { print("Error: Unable to locate a.dae in Resources.") } return nil } a.dae file exists in the Resources section of macOS Playground app. And a.dae can be viewed in Xcode. Console shows: Error loading scene: The operation couldn’t be completed. (Foundation._GenericObjCError error 0.) Any input is appreciated.
1
0
533
Dec ’24
MapProxy conversion from screen to coords is wrong on macOS
Try the following code on macOS, and you'll see the marker is added in the wrong place, as the conversion from screen coordinates to map coordinates doesn't work correctly. The screenCoord value is correct, but reader.convert(screenCoord, from: .local) offsets the resulting coordinate by the height of the content above the map, despite the .local parameter. struct TestMapView: View { @State var placeAPin = false @State var pinLocation :CLLocationCoordinate2D? = nil @State private var cameraProsition: MapCameraPosition = .camera( MapCamera( centerCoordinate: .denver, distance: 3729, heading: 92, pitch: 70 ) ) var body: some View { VStack { Text("This is a bug demo.") Text("If there are other views above the map, the MapProxy doesn't convert the coordinates correctly.") MapReader { reader in Map( position: $cameraProsition, interactionModes: .all ) { if let pl = pinLocation { Marker("(\(pl.latitude), \(pl.longitude))", coordinate: pl) } } .onTapGesture(perform: { screenCoord in pinLocation = reader.convert(screenCoord, from: .local) placeAPin = false if let pinLocation { print("tap: screen \(screenCoord), location \(pinLocation)") } }) .mapControls{ MapCompass() MapScaleView() MapPitchToggle() } .mapStyle(.standard(elevation: .automatic)) } } } } extension CLLocationCoordinate2D { static var denver = CLLocationCoordinate2D(latitude: 39.742043, longitude: -104.991531) } (FB13135770)
5
1
1.4k
Dec ’24
Initialize view struct with @StateObject parameter
May I inquire about the differences between the two ways of view under the hood in SwiftUI? class MyViewModel: ObservableObject { @Published var state: Any init(state: Any) { self.state = state } } struct MyView: View { @StateObject var viewModel: MyViewModel var body: some View { // ... } } struct CustomView: View { let navigationPath: NavigationPath @StateObject var viewModel: MyViewModel var body: some View { Button("Go to My View") { navigationPath.append(makeMyView()) } } } // Option 1: A viewModel is initialized outside view's initialization func makeMyView(state: Any) -> some View { let viewModel = MyViewModel(state: state) MyView(viewModel: viewModel) } // Option 2: A viewModel is initialized inside view's initialization func makeMyView(state: Any) -> some View { MyView(viewModel: MyViewModel(state: state)) } For option 1, the view model will be initialized whenever custom view is re-rendered by changes whereas the view model is only initialized once when the view is re-rendered for option 2. So what happens here?
0
0
229
Dec ’24