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
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.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
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:
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
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
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?
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))
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.
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 }
}
}
}
}
}```
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)
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.
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 }
}
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
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.
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?
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?
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.
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.
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()
}
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
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?