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

SwiftUI onChange fires twice when filtering data from @Observable store
Hi all, I’m running into a “double update” effect in SwiftUI when using the @Observable with @State. I’m trying to understand whether this is expected behavior, a misuse on my side, or a potential bug. Setup I have an observable store using the Observation macro: @Observable class AlbumStore { var albums: [Album] = [ Album(id: "1", title: "Album 1", author: "user1"), Album(id: "2", title: "Album 2", author: "user1"), Album(id: "3", title: "Album 3", author: "user1"), Album(id: "4", title: "Album 4", author: "user1"), Album(id: "5", title: "Album 5", author: "user1"), Album(id: "6", title: "Album 6", author: "user1") ] func addAlbum(_ album: Album) { albums.insert(album, at: 0) } func removeAlbum(_ album: Album) { albums.removeAll(where: { $0 == album }) } } In my view, I inject it via @Environment and also keep some local state: @Environment(AlbumStore.self) var albumStore @State private var albumToAdd: Album? I derive a computed array that depends on both the environment store and local state: private var filteredAlbums: [Album] { let albums = albumStore.albums.filter { album in if let albumToAdd { return album.id != albumToAdd.id } else { return true } } return albums } View usage Inside a horizontal ScrollView / LazyHStack, I observe changes to filteredAlbums: @ViewBuilder private func carousel() -> some View { GeometryReader { proxy in let itemWidth: CGFloat = proxy.size.width / 3 let sideMargin = (proxy.size.width - itemWidth) / 2 ScrollView(.horizontal, showsIndicators: false) { LazyHStack(spacing: 20) { ForEach(filteredAlbums, id: \.id) { album in albumItem(album: album) .frame(width: itemWidth) .scrollTransition(.interactive, axis: .horizontal) { content, phase in content .scaleEffect(phase.isIdentity ? 1.0 : 0.8) } } } .scrollTargetLayout() } .scrollTargetBehavior(.viewAligned(limitBehavior: .always)) .scrollPosition(id: $carouselScrollID, anchor: .center) .contentMargins(.horizontal, sideMargin, for: .scrollContent) .onChange(of: filteredAlbums) { old, new in print("filteredAlbums id: \(new.map { $0.id })") } } } Triggering the update When I add a new album, I do: albumToAdd = newAlbum albumStore.addAlbum(newAlbum) Expected behavior Since filteredAlbums explicitly filters out albumToAdd, I expect the result to remain unchanged. Actual behavior I consistently get two onChange callbacks, in this order: filteredAlbums id: ["E852E42A-AAEC-4360-A6A6-A95752805E2E", "1", "2", "3", "4", "5", "6"] filteredAlbums id: ["1", "2", "3", "4", "5", "6"] This suggests: The AlbumStore update (albums.insert) is observed first. The @State update (albumToAdd) is applied later. As a result, filteredAlbums is recomputed twice with different dependency snapshots. On a real iPad device, this also causes a visible scroll position jump. In the simulator, the jump is not visually observable; however, the onChange(of: filteredAlbums) callback still fires twice with the same sequence of values, indicating that the underlying state update behavior is identical. Strange observations This does not happen with ObservableObject If I replace @Observable with a classic ObservableObject + @Published: class OBAlbumStore: ObservableObject { @Published var albums: [Album] = [ Album(id: "1", title: "Album 1", author: "user1"), Album(id: "2", title: "Album 2", author: "user1"), Album(id: "3", title: "Album 3", author: "user1"), Album(id: "4", title: "Album 4", author: "user1"), Album(id: "5", title: "Album 5", author: "user1"), Album(id: "6", title: "Album 6", author: "user1") ] func addAlbum(_ album: Album) { albums.insert(album, at: 0) } func removeAlbum(_ album: Album) { albums.removeAll(where: { $0 == album }) } } …and inject it with @EnvironmentObject, the double update disappears. Removing GeometryReader also avoids the issue If I remove the surrounding GeometryReader and hardcode sizes: @ViewBuilder private func carousel() -> some View { // GeometryReader { proxy in let itemWidth: CGFloat = 400 let sideMargin: CGFloat = 410 ScrollView(.horizontal, showsIndicators: false) { LazyHStack(spacing: 20) { ForEach(filteredAlbums, id: \.id) { album in albumItem(album: album) .frame(width: itemWidth) .scrollTransition(.interactive, axis: .horizontal) { content, phase in content .scaleEffect(phase.isIdentity ? 1.0 : 0.8) } } } .scrollTargetLayout() } .scrollTargetBehavior(.viewAligned(limitBehavior: .always)) .scrollPosition(id: $carouselScrollID, anchor: .center) .contentMargins(.horizontal, sideMargin, for: .scrollContent) .onChange(of: filteredAlbums) { old, new in print("filteredAlbums id: \(new.map { $0.id })") } // } } …the double onChange no longer occurs. Questions Is this update ordering expected when using @Observable and @State? Does Observation intentionally propagate environment changes before local state updates? Is GeometryReader forcing an additional evaluation pass that exposes this ordering? Is this a known limitation / bug compared to ObservableObject? I want to understand why this behaves differently under Observation. Thanks in advance for any insights 🙏 Full Project Link
2
0
193
4w
In a List row on macOS, changing Image color when row is selected
When using an image in a List item, you sometimes want to tint that image, but only if the item isn’t selected. When it’s selected, you usually want the contents of the list item to be all-white, for contrast. The backgroundProminence Environment value ostensibly exists for this purpose, but in my tests, it never seems to change. Am I doing something wrong? Is there an alternative solution? For instance, this code: import SwiftUI struct ProminentBackgroundInList: View { var body: some View { List(selection: .constant(0)) { ListItem().tag(0) ListItem().tag(1) } } } struct ListItem: View { @Environment(\.backgroundProminence) var backgroundProminence var body: some View { HStack { Image(systemName: "person.fill") .foregroundStyle(backgroundProminence == .standard ? .orange : .primary) Text("Person") } } } #Preview { ProminentBackgroundInList() } Produces this result:
2
0
186
4w
SwiftUI View Stops Updating When Using @Environment - Xcode 26
Hi all - i'm encountering a strange issue since updating to Xcode 26.0.1. It seems that any SwiftUI Views that have an :ObservedObject property that contains @Published properties, and use those properties inside the View, no longer update when those properties are updated when the view also has an @Environment property. If I remove the @Environment property and any usage of it, the view updates correctly. The specific environment property i'm using is .safeAreaInsets, like so: @Environment(\.safeAreaInsets) private var safeAreaInsets Is this a recognised bug in the latest iOS 26 SDK? Thanks
3
2
242
4w
Same Color in View and colorEffect shader argument produce different results
Opened feedback item FB21877364. Context I have the following Metal shader, which replaces one color with another. [[ stitchable ]] half4 recolor( float2 position, half4 currentColor, half4 from, half4 to ) { if (all(currentColor == from)) return to; return currentColor; } Given this SwiftUI view: let shader = ShaderLibrary.recolor(.color(.red), .color(.green)) Color.red .colorEffect(shader) I get a red rectangle instead of the expected green one. Note that this works on both dynamic and non-dynamic colors. Note that this sometimes works with some colors, which is very inconvenient when trying to figure out what's going on. Did I miss something? I would've expected the shader to work with colors the same way. Issue To really highlight the issue, here's another test case. I'll define #94877E in an Asset Catalog as example and check the RGB values using the Digital Color Meter app in "Display native values" mode. We'll use the following shader to determine how colorEffect receives colors: [[ stitchable ]] half4 test( float2 position, half4 currentColor, half4 color ) { return color; } The following view yields "R: 0.572, G: 0.531, B: 0.498". Color.example While this one yields "R: 0.572, G: 0.531, B: 0.499". let shader = ShaderLibrary.test(.color(Color.example)) Color.white.colorEffect(shader) I would expect them to match.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
22
4w
Password AutoFill BUG
When using the Password AutoFill feature, after entering a password and navigating to another page, a system prompt appears asking whether to save the password to the keychain (as shown in the figure). If this prompt is not dismissed and the app is moved to the background, then brought back to the foreground, the prompt automatically disappears. However, after this occurs, all input fields within the app become unresponsive to keyboard input—no keyboard will appear when tapping any text field. The only way to restore keyboard functionality is to force-quit the app and relaunch it. How can this issue be resolved?
0
0
120
4w
Text with .secondary vanishes when Material background is clipped to UnevenRoundedRectangle in ScrollView
I just found a weird bug: If you place a Text view using .foregroundStyle(.secondary), .tertiary, or other semantic colors inside a ScrollView, and apply a Material background clipped to an UnevenRoundedRectangle, the text becomes invisible. This issue does not occur when: The text uses .primary or explicit colors (e.g., .red, Color.blue), or The background is clipped to a standard shape (e.g., RoundedRectangle). A minimal reproducible example is shown below: ScrollView{ VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello World.") .font(.system(size: 15)) .foregroundStyle(.quinary) } } .padding() .frame(height: 100) .background(Material.regular) .clipShape(UnevenRoundedRectangle(topLeadingRadius: 10,bottomLeadingRadius: 8,bottomTrailingRadius:8, topTrailingRadius: 8))
1
0
277
Feb ’26
Layout issue using .strokeBorder modifier
Hello, Developers! While writing custom view modifier I ran into unexpected behavior of .strokeBorder modifier. The underlying content seem to be “bleeding” outside of the stroke border edges, even though they share the exact same shape for their layout. This issue relevant for both Xcode Previews and on-device testing. Maybe someone has experienced this issue before, I'd be glad to see your opinion on this matter.
1
0
139
Feb ’26
Animation does not work with List, while works with ScrollView + ForEach
Why there is a working animation with ScrollView + ForEach of items removal, but there is none with List? ScrollView + ForEach: struct ContentView: View { @State var items: [String] = Array(1...5).map(\.description) var body: some View { ScrollView(.vertical) { ForEach(items, id: \.self) { item in Text(String(item)) .frame(maxWidth: .infinity, minHeight: 50) .background(.gray) .onTapGesture { withAnimation(.linear(duration: 0.1)) { items = items.filter { $0 != item } } } } } } } List: struct ContentView: View { @State var items: [String] = Array(1...5).map(\.description) var body: some View { List(items, id: \.self) { item in Text(String(item)) .frame(maxWidth: .infinity, minHeight: 50) .background(.gray) .onTapGesture { withAnimation(.linear(duration: 0.1)) { items = items.filter { $0 != item } } } } } }```
6
1
232
Feb ’26
screenshot on iOS26
I had take screenshots by following code let scenes = UIApplication.shared.connectedScenes let windowScene = scenes.first as? UIWindowScene let window = windowScene?.windows.first self.uiImage = window?.rootViewController?.view!.getImage(rect: rect) View has two views. One is ImageView contains some image and overlay of image detection results with .overlay. another view is InfoView contains several info and button which above code fired. on iOS 17, I can take screenshots as I saw, but on iOS26, missing on image of ImageView. Overlay(detected rectangle) in Imageview and InfView can be taken. How can I take screenshots as I saw on iOS26?(iPad)
10
0
714
Feb ’26
App Launchscreen Size NOT Correct on iPadOS 26
Hello, We’re seeing an iPad-specific Launch Screen issue related to multitasking window sizes. Environment Device: iPad (iPadOS 26) Device orientation: Landscape App is launched in a small window where the app window is portrait-shaped (width < height) Issue When the iPad is in landscape but the app is launched as a portrait-shaped small window, the LaunchScreen.storyboard appears to be rendered/layouted as landscape, not matching the actual window geometry. As a result, the Launch Screen content is clipped / partially missing (we see blank/empty area at the bottom during launch). After the app finishes launching, our first view controller uses the correct window size and the UI looks fine — the problem is mainly during the Launch Screen phase. What we checked LaunchScreen.storyboard uses Auto Layout and is expected to adapt to screen/window size. This only reproduces when the device orientation and the app window aspect ratio don’t match (landscape device + portrait-shaped app window, or vice versa). When device orientation and window shape are aligned, the Launch Screen displays correctly. Question Is it expected that iPadOS renders LaunchScreen.storyboard based on the interface orientation / size class rather than the actual window bounds in multitasking scenarios? If not expected, what is the recommended way to ensure the Launch Screen matches the app’s actual window size/aspect ratio at launch (without using code, since Launch Screen is static)? Are there any additional diagnostics or recommended steps to help us investigate and confirm the root cause (e.g., specific logs, APIs/values to capture at launch such as UIWindowScene bounds, interfaceOrientation, size classes, or any guidance on how Launch Screen snapshots are chosen/cached in multitasking)? Thank you.
0
0
171
Feb ’26
iOS 26 & Xcode 26 - bug with keyboard by WebView
Environment iOS 26 (23A343) Xcode 26 Reproduces on device and Simulator Description When presenting a SwiftUI WebView (native iOS 26 component) or a WKWebView/UIWebView via UIViewRepresentable, focusing a text field inside the web view and then dismissing it breaks the keyboard layout behavior. After returning to the main app, tapping any TextField causes the keyboard to cover bottom controls (e.g. buttons). Expected safe area insets are not applied. The issue is only resolved after closing and reopening the keyboard once. Steps to Reproduce Open a SwiftUI screen with WebView (via .sheet or NavigationLink). Inside the web view, tap a text field to show the keyboard. Dismiss the web view. Tap a TextField in the main app. Expected Result Layout should adjust correctly. Bottom controls stay visible above the keyboard. Actual Result Keyboard covers bottom controls. Insets are ignored until the keyboard is dismissed and reopened. Notes Reproduces with: Native SwiftUI WebView (iOS 26) WKWebView and UIWebView via UIViewRepresentable Presentation style (.sheet or navigation push) does not matter. Example video: https://youtu.be/Epgoz1vETKU FB: FB20386257 Sample Code import SwiftUI import WebKit struct ContentView: View { @State var url: URL? @FocusState private var isFocused: Bool var body: some View { VStack { TextField("TextField", text: .constant("")) .focused($isFocused) Button("HIDE KEYBOARD") { isFocused = false } Spacer() Button("ACTION") { url = URL(string: "https://google.com") } } .sheet(item: $url) { value in NavigationStack { WebView(url: value) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("CLOSE") { url = nil } } } } } } } extension URL: Identifiable { public var id: String { absoluteString } }
2
5
1.1k
Feb ’26
OS 26 Liquid Glass: UITabBar overrides selected title text color after trait changes, causing icon and title color mismatch
I’m seeing unexpected UITabBar behavior on iOS 26 when Liquid Glass is enabled. I’m using UITabBarAppearance with a dynamic UIColor to keep the selected tab bar icon and title text in sync (blue in light mode, green in dark mode). Expected behavior The selected tab bar icon and title text should always resolve to the same color based on the current trait collection. Actual behavior On initial load, the colors are correct. However, after switching light/dark mode (or any trait change that triggers a material update): The icon keeps the configured color The title text color is overridden by the system Result: selected icon and title text end up with different colors This happens even though both colors are explicitly set to the same dynamic UIColor. Minimal reproducible example: func applyAppearance() { let color = UIColor { trait in trait.userInterfaceStyle == .dark ? .green : .blue } self.tabBar.tintColor = color }
Topic: UI Frameworks SubTopic: UIKit
1
0
143
Feb ’26
Animation Glitch behind Tab-bar
I'm trying to replicate edit/select mode of iOS 26 photos app. When user clicks Select button, bottom tab bar is replaced by the toolbar buttons. When I press Done button, a white opaque bar appears at the bottom behind the tabbar. It looks pretty straightforward to implement but I'm banging my head here now. Any help will be appreciated. Code and animation frames attached bellow struct ContentView: View { var body: some View { TabView(selection: $selectedTab) { OverviewView() .tabItem { Image(systemName: "chart.pie") Text("Overview") } .tag(0) //rest of the tabs } } } struct OverviewView: View { @State private var editActive = false @State private var selection = Set<String>() @State private var items = [ "Item 1", "Item 2", "Item 3", ] var body: some View { NavigationStack { List(selection: $selection) { ForEach(items, id: \.self) { item in Text(item) } } .toolbar { if editActive { ToolbarItem(placement: .bottomBar) { Button { } label: { Label("Delete", systemImage: "trash") } } ToolbarItem(placement: .bottomBar) { Button { } label: { Label("Category", systemImage: "tag") } } } ToolbarItem(placement: .topBarTrailing) { Button(editActive ? "Done" : "Select") { withAnimation { editActive.toggle() } } } } .environment(\.editMode, .constant(editActive ? .active : .inactive)) .toolbar(editActive ? .hidden : .visible, for: .tabBar) } } } I have attached 5 frames during animation phase.
1
0
118
Feb ’26
.glassEffect() renders dark on device but works on simulator - TestFlight doesn't fix it
iOS 26 SwiftUI .glassEffect() renders dark/gray on physical device - TestFlight doesn't fix it .glassEffect() renders as dark/muddy gray on my physical iPhone instead of the light frosted glass like Apple Music's tab bar. What I've confirmed: Same code works perfectly on iOS Simulator Apple Music and other Apple apps show correct Liquid Glass on same device Brand new Xcode project with just .glassEffect() also renders dark TestFlight build (App Store signing) has the SAME problem - still dark/gray This rules out developer signing vs App Store signing as the cause What I've tried: Clean build, delete derived data, reinstall app Completely reinstalled Xcode All accessibility settings correct (Reduce Transparency OFF, Liquid Glass set to Clear) Disabled Metal diagnostics Debug and Release builds Added window configuration for Extended sRGB/P3 color space Added AppDelegate with configureWindowForLiquidGlass() Tried .preferredColorScheme(nil) Tried background animation to force "live" rendering Environment: iPhone 17 Pro, iOS 26.3 Xcode 26.2 macOS 26.3 The question: Why does .glassEffect() work for Apple's apps but not third party apps, even with App Store signing via TestFlight? What am I missing?
5
0
300
Feb ’26
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.8k
Feb ’26
Crash in swift::_getWitnessTable when passing UITraitBridgedEnvironmentKey
When using UITraitBridgedEnvironmentKey to pass a trait value to the swift environment, it causes a crash when trying to access the value from the environment. The issue seems to be related to how swift uses the UITraitBridgedEnvironmentKey protocol since the crash occurs in swift::_getWitnessTable () from lazy protocol witness table accessor…. It can occur when calling any function that is generic using the UITraitBridgedEnvironmentKey type. I originally encountered the issue when trying to use a UITraitBridgedEnvironmentKey in SwiftUI, but have been able to reproduce the issue with any function with a similar signature. https://developer.apple.com/documentation/swiftui/environmentvalues/subscript(_:)-9zku Steps to Reproduce Requirements for the issue to occur Project with a minimum iOS version of iOS 16 Build the project with Xcode 26 Run on iOS 18 Add the following code to a project and call foo(key: MyCustomTraitKey.self) from anywhere. @available(iOS 17.0, *) func foo<K>(key: K.Type) where K: UITraitBridgedEnvironmentKey { // Crashes before this is called } @available(iOS 17.0, *) public enum MyCustomTraitKey: UITraitBridgedEnvironmentKey { public static let defaultValue: Bool = false public static func read(from traitCollection: UITraitCollection) -> Bool { false } public static func write(to mutableTraits: inout UIMutableTraits, value: Bool) {} } // The crash will occur when calling this. It can be added to a project anywhere // The sample project calls it from scene(_:willConnectTo:options:) foo(key: MyCustomTraitKey.self) For example, I added it to the SceneDelegate in a UIKit Project class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if #available(iOS 17, *) { // The following line of code can be placed anywhere in a project, `SceneDelegate` is just a convenient place to put it to reproduce the issue. foo(key: MyCustomTraitKey.self) // ^ CRASH: Thread 1: EXC_BAD_ACCESS (code=1, address=0x10) } } } Actual Behaviour The app crashes with the stack trace showing the place calling foo but before foo is actually called. (ie, a breakpoint or print in foo is never hit) #0 0x000000019595fbc4 in swift::_getWitnessTable () #1 0x0000000104954128 in lazy protocol witness table accessor for type MyCustomTraitKey and conformance MyCustomTraitKey () #2 0x0000000104953bc4 in SceneDelegate.scene(_:willConnectTo:options:) at .../SceneDelegate.swift:20 The app does not crash when run on iOS 17, or 26 or when the minimum ios version is raised to iOS 17 or higher. It also doesn't crash on iOS 16 since it's not calling foo since UITraitBridgedEnvironmentKey was added in iOS 17. Expected behaviour The app should not crash. It should call foo on iOS 17, 18, and 26.
4
1
233
Feb ’26
Xcode26 build app with iOS26, UITabBarController set CustomTabBar issue
Our project using UITabBarController and set a custom tabbar using below code: let customTabBar = CustomTabBar(with: dataSource) setValue(customTabBar, forKey: "tabBar") But when using Xcode 26 build app in iOS 26, the tabbar does not show: above code works well in iOS 18: below is the demo code: AppDelegate.swift: import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { let window: UIWindow = UIWindow() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { window.rootViewController = TabBarViewController() window.makeKeyAndVisible() return true } } CustomTabBar.swift: import UIKit class CustomTabBar: UITabBar { class TabBarModel { let title: String let icon: UIImage? init(title: String, icon: UIImage?) { self.title = title self.icon = icon } } class TabBarItemView: UIView { lazy var titleLabel: UILabel = { let titleLabel = UILabel() titleLabel.translatesAutoresizingMaskIntoConstraints = false titleLabel.font = .systemFont(ofSize: 14) titleLabel.textColor = .black titleLabel.textAlignment = .center return titleLabel }() lazy var iconView: UIImageView = { let iconView = UIImageView() iconView.translatesAutoresizingMaskIntoConstraints = false iconView.contentMode = .center return iconView }() private var model: TabBarModel init(model: TabBarModel) { self.model = model super.init(frame: .zero) setupSubViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupSubViews() { addSubview(iconView) iconView.topAnchor.constraint(equalTo: topAnchor).isActive = true iconView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true iconView.widthAnchor.constraint(equalToConstant: 34).isActive = true iconView.heightAnchor.constraint(equalToConstant: 34).isActive = true iconView.image = model.icon addSubview(titleLabel) titleLabel.topAnchor.constraint(equalTo: iconView.bottomAnchor).isActive = true titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true titleLabel.heightAnchor.constraint(equalToConstant: 16).isActive = true titleLabel.text = model.title } } private var dataSource: [TabBarModel] init(with dataSource: [TabBarModel]) { self.dataSource = dataSource super.init(frame: .zero) setupTabBars() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func sizeThatFits(_ size: CGSize) -> CGSize { var sizeThatFits = super.sizeThatFits(size) let safeAreaBottomHeight: CGFloat = safeAreaInsets.bottom sizeThatFits.height = 52 + safeAreaBottomHeight return sizeThatFits } private func setupTabBars() { backgroundColor = .orange let multiplier = 1.0 / Double(dataSource.count) var lastItemView: TabBarItemView? for model in dataSource { let tabBarItemView = TabBarItemView(model: model) addSubview(tabBarItemView) tabBarItemView.translatesAutoresizingMaskIntoConstraints = false tabBarItemView.topAnchor.constraint(equalTo: topAnchor).isActive = true tabBarItemView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true if let lastItemView = lastItemView { tabBarItemView.leadingAnchor.constraint(equalTo: lastItemView.trailingAnchor).isActive = true } else { tabBarItemView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true } tabBarItemView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: multiplier).isActive = true lastItemView = tabBarItemView } } } TabBarViewController.swift: import UIKit class NavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() } } class HomeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .red navigationItem.title = "Home" } } class PhoneViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .purple navigationItem.title = "Phone" } } class PhotoViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .yellow navigationItem.title = "Photo" } } class SettingViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green navigationItem.title = "Setting" } } class TabBarViewController: UITabBarController { override func viewDidLoad() { super.viewDidLoad() let homeVC = HomeViewController() let homeNav = NavigationController(rootViewController: homeVC) let phoneVC = PhoneViewController() let phoneNav = NavigationController(rootViewController: phoneVC) let photoVC = PhotoViewController() let photoNav = NavigationController(rootViewController: photoVC) let settingVC = SettingViewController() let settingNav = NavigationController(rootViewController: settingVC) viewControllers = [homeNav, phoneNav, photoNav, settingNav] let dataSource = [ CustomTabBar.TabBarModel(title: "Home", icon: UIImage(systemName: "house")), CustomTabBar.TabBarModel(title: "Phone", icon: UIImage(systemName: "phone")), CustomTabBar.TabBarModel(title: "Photo", icon: UIImage(systemName: "photo")), CustomTabBar.TabBarModel(title: "Setting", icon: UIImage(systemName: "gear")) ] let customTabBar = CustomTabBar(with: dataSource) setValue(customTabBar, forKey: "tabBar") } } And I have post a feedback in Feedback Assistant(id: FB18141909), the demo project code can be found there. How are we going to solve this problem? Thank you.
Topic: UI Frameworks SubTopic: UIKit Tags:
5
0
791
Feb ’26
.safeAreaBar(edge: .bottom), animation lag on appear/disappear
When I try to show/hide the content in .safeAreaBar(edge: .bottom), especially the content with a large height, the background animation of the toolbar is very laggy. iOS 26 RC Feedback ID - FB19768797 import SwiftUI struct ContentView: View { @State private var isShown: Bool = false var body: some View { NavigationStack { Button("Toggle") { withAnimation { isShown.toggle() } } ScrollView(.vertical) { ForEach(0..<100) { index in Text("\(index)") .padding() .border(.blue) .background(.blue) .frame(maxWidth: .infinity) } } .scrollEdgeEffectStyle(.soft, for: .bottom) .safeAreaBar(edge: .bottom) { if isShown { Text("Safe area bar") .padding(64) .background(.red) } } } } } #Preview { ContentView() }
1
3
196
Feb ’26
.contactAccessPicker shows blank sheet on iOS 26.2.1 on device
Calling contactAccessPicker results in a blank sheet and a jetsam error, rather than the expected contact picker, using Apple’s sample code, only on device with iOS 26.2.1. This is happening on a iPhone 17 Pro Max running 26.2.1, and not on a simulator. I’m running Apple's sample project Accessing a person’s contact data using Contacts and ContactsUI Steps: Run the sample app on device running iOS 26.2.1. Use the flow to authorize .limited access with 1 contact: Tap request access, Continue, Select Contacts. Select a contact, Continue, Allow Selected Contact. This all works as expected. Tap the add contact button in the toolbar to add a second contact. Expected: This should show the Contact Access Picker UI. Actual: Sheet is shown with no contents. See screenshot of actual results on iOS device running 26.2.1. Reported as FB21812568 I see a similar (same?) error reported for 26.1. It seems strange that the feature is completely broken for multiple point releases. Is anyone else seeing this or are the two of us running into the same rare edge case? Expected Outcome, seen on simulator running 26.2 Actual outcome, seen on device running 26.2.1
3
0
200
Feb ’26
ASAuthorizationAccountCreationProvider does not work with 3rd party apps
hello im using the new IOS 26 api for passkey creation ASAuthorizationAccountCreationProvider however it only seems to work with apple's Passwords app. Selecting 3rd party password apps (1Password, google chrome, etc) does not create the passkey. The sign up sheet gives me the option to save in 3rd party apps, but when I select a 3rd party app, I just get the ASAuthorizationError cancelled error? So I dont even know what the problem is? When selecting "Save in Passwords(apple's app)" during the sign up it works fine Has anyone else run into this issue? Is there something I need to do enable 3rd party apps?
7
0
465
Feb ’26