Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

Posts under SwiftUI tag

200 Posts

Post

Replies

Boosts

Views

Activity

Button or onTapGesture grows its hit region by a fixed amount past the view’s bounds.
SwiftUI Button hit area extends ~16pt beyond its visual frame — expected OS behavior? Hi everyone, I’m working on a custom SwiftUI segmented control and noticed some unexpected hit-testing behavior with Button. I have a button structured roughly like this: Button { selectItem(at: index) } label: { ZStack { if selectedIndex == index { selectedBackground } SegmentCellView( tab: tab, isSelected: selectedIndex == index ) } .frame(maxWidth: .infinity) .contentShape(Rectangle()) } .accessibilityLabel(Text(tab.labelText)) The visual frame of the button has a certain height, but when testing on a real device, I can tap approximately 16pt outside the button's visible frame and the button still receives the tap. This seems to happen even though I explicitly use: .contentShape(Rectangle()) to define the tappable shape. My questions are: Is this ~16pt expansion of the interactive area expected SwiftUI/OS behavior? Is this related to Apple's minimum hit-target/accessibility guidelines being automatically applied to controls such as Button? If so, is there an official API/documentation describing this behavior? Is there a recommended way to make the Button's hit-testing area exactly match the bounds defined by .contentShape(Rectangle()), if that is actually possible? I'm particularly interested in understanding whether this is intentional system behavior or something caused by the ButtonStyle / layout hierarchy. Thanks!
0
0
68
1w
toolbarMinimizationBehavior(_:for:) crashes on some iOS 27.0.0 devices: "missing weak symbol" despite #available(iOS 27.0, *)
We are seeing a production crash that only affects devices reporting iOS 27.0.0. Devices on 27.0.1 and later, and on iOS 26, are unaffected. The app is built with Xcode 27.0 (the same binary shape appears with the Xcode 27.1 RC toolchain), deployment target iOS 26.0. The crashing code is a plain availability-gated call: extension View { @ViewBuilder func disableNavigationBarMinimization() -> some View { if #available(iOS 27.0, *) { toolbarMinimizationBehavior(.never, for: .navigationBar) } else { self } } } Crash report excerpt (addresses and app symbols removed): crash_info_entry_0: Failed to look up symbolic reference at <addr> - offset <n> - symbol <nearest stripped symbol> in <app binary> - pointer at <addr> is likely a reference to a missing weak symbol Crashed: com.apple.main-thread 0 libsystem_kernel.dylib __pthread_kill 2 libsystem_c.dylib abort 3 libswiftCore.dylib <redacted> 9 libswiftCore.dylib swift_getTypeByMangledName + 908 10 libswiftCore.dylib swift_getTypeByMangledNameInContext2 + 248 11 <app> __swift_instantiateConcreteTypeFromMangledNameV2 12 <app> specialized closure #1 in View.disableNavigationBarMinimization() 13 <app> closure #1 in SomeView.body.getter What we established: Because the deployment target is 26.0 and the API is @available(iOS 27.0, *), the app weak-imports both the function and its opaque result type descriptor. dyld_info -imports on the binary shows: _$s7SwiftUI4ViewPAAE27toolbarMinimizationBehavior_3forQrAA07ToolbareF0V_AA0H9PlacementVdtF [weak-import] (from SwiftUI) _$s7SwiftUI4ViewPAAE27toolbarMinimizationBehavior_3forQrAA07ToolbareF0V_AA0H9PlacementVdtFQOMQ [weak-import] (from SwiftUI) The call site has to instantiate the opaque return type's metadata before calling the function. That is the frame that aborts. So when dyld binds the descriptor to null, #available(iOS 27.0, *) passes and the process still dies. The symbol is present and exported on the iOS 27.0 build we can inspect locally (24A437, via Xcode's DeviceSupport symbols), and the same code runs fine on the iOS 27.0 simulator runtime (24A434). We cannot reproduce the crash on any device we own. The SwiftUI binary in 24A437 exports both toolbarMinimizeBehavior(_:for:) / ToolbarMinimizeBehavior and toolbarMinimizationBehavior(_:for:) / ToolbarMinimizationBehavior. The iOS 27.1 SDK only declares the latter, still annotated @available(iOS 27.0, *). This looks like a rename late in the 27.0 cycle. Our working theory is that some iOS 27.0.0 builds in the field (earlier 27.0 builds, possibly the ones preinstalled on new devices) predate the rename and only contain toolbarMinimizeBehavior, so the renamed symbol is absent and the weak import resolves to null. Questions: Is the @available(iOS 27.0, *) annotation on toolbarMinimizationBehavior(_:for:) accurate for every shipped 27.0.0 build? Should it be 27.0.1 or 27.1? Is there a supported way to guard against a missing opaque type descriptor at runtime, short of raising the availability check? Feedback filed as FB25095054.
0
0
133
1w
SwiftUI views lock up after background and sleep for “Designed for iPad” apps
There's an easily reproducible SwiftUI bug on macOS where an app's UI state no longer updates/re-renders for "Designed for iPad" apps (i.e. ProcessInfo.processInfo.isiOSAppOnMac == true). The bug occurs in Xcode and also if the app is running independent of Xcode. The bug occurs when: the user Hides the app (i.e. it goes into the background) the user puts the Mac to sleep (e.g. Apple menu > Sleep) a total of ~60 seconds transpires (i.e. macOS puts the app into the "suspended state") when the app is brought back into the foreground the UI no longer updates properly The only way I have found to fix this is to manually open a new actual full app window via File > New, in which case the app works fine again in the new window. The following extremely simple code in a default Xcode project illustrates the issue: import SwiftUI @main struct staleApp: App { @State private var isBright = true var body: some Scene { WindowGroup() { ZStack { (isBright ? Color.white : Color.black).ignoresSafeArea() Button("TOGGLE") { isBright.toggle(); print("TAPPED") } } .onAppear { print("\(isBright ? "light" : "dark") view appeared") } } } } For the code above, after Hiding the app and putting the computer to sleep for 60 seconds or more, the button no longer swaps views, although the print statements still appear in the console upon tapping the button. Also, while in this buggy state, i can get the view to update to the current state (i.e. the view triggered by the last tap) by manually dragging the corner of the app window to resize the window. But after resizing, the view again does not update upon button tapping until I resize the window again. so it appears the diff engine is mucked or that the Scene or WindowGroup are no longer correctly running on the main thread I have tried rebuilding the entire view hierarchy by updating .id() on views but this approach does NOT work. I have tried many other options/hacks but have not been able to reset the 'view engine' other than opening a new window manually or by using: @Environment(.openWindow) private var openWindow openWindow could be a viable solution except there's no way to programmatically close the old window for isiOSAppOnMac (@Environment(.dismissWindow) private var dismissWindow doesn't work for iOS)
2
1
371
1w
iOS 27: SwiftUI keyboard safe area is not restored after returning to a UIHostingController
I have a SwiftUI view embedded in a UIHostingController. It contains a numeric text field at the top and a .borderedProminent button at the bottom. The text field is automatically focused when the view appears. The bottom button pushes another instance of the same hosting controller. On the initial appearance, SwiftUI correctly positions the button above the keyboard. However, after navigating back from VC2 to VC1, the text field is focused and the keyboard is visible, but the button remains behind the keyboard. This occurs regardless of whether VC2 is closed using the navigation bar back button or the interactive back gesture. This appears to be an iOS 27 regression involving SwiftUI keyboard avoidance inside a UIHostingController. The issue is especially problematic with a numeric keyboard because it has no Return or Done key. If the covered button is the primary way to continue or dismiss the keyboard, the user can no longer access it. Interestingly, the button is repositioned correctly as soon as I begin moving the app into the background. This suggests that SwiftUI still knows about the keyboard safe area but does not update the layout correctly when the hosting controller reappears after being popped to. Minimal reproducible example import SwiftUI import UIKit final class NumericInputHostingController: UIHostingController<NumericInputView> { init() { super.init( rootView: NumericInputView(onContinue: {}) ) rootView = NumericInputView { [weak self] in self?.navigationController?.pushViewController( NumericInputHostingController(), animated: true ) } } @available(*, unavailable) @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } struct NumericInputView: View { @State private var value = "" @FocusState private var isTextFieldFocused: Bool let onContinue: () -> Void var body: some View { VStack(spacing: 24) { TextField("Enter a number", text: $value) .keyboardType(.numberPad) .focused($isTextFieldFocused) .padding(.horizontal, 16) .frame(height: 52) .background { RoundedRectangle(cornerRadius: 10) .stroke( Color.accentColor, lineWidth: 1.5 ) } Spacer() Button("Open Another", action: onContinue) .buttonStyle(.borderedProminent) .controlSize(.large) .frame(maxWidth: .infinity) } .padding(20) .background(Color(.systemBackground)) .onAppear { DispatchQueue.main.async { isTextFieldFocused = true } } .onDisappear { isTextFieldFocused = false } } } The initial controller is embedded in a navigation controller: let controller = NumericInputHostingController() let navigationController = UINavigationController( rootViewController: controller ) Steps to reproduce Present VC1. VC1 automatically focuses the numeric text field. Confirm that the bottom button is above the keyboard. Tap the button to push VC2. Navigate back to VC1. VC1 focuses its text field and displays the numeric keyboard. Observe that the bottom button is now behind the keyboard. Begin putting the app into the background. Observe that the button suddenly moves to the correct position above the keyboard. UIKit’s keyboardLayoutGuide handles the equivalent UIKit layout correctly. Is this a known issue with keyboard safe-area updates when a UIHostingController reappears after navigation? Is there a supported SwiftUI solution that does not require observing keyboard notifications manually?
3
2
404
1w
.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
10
3
1.6k
1w
Dismiss/Close button - leading or trailing edge?
What is the recommended position of the "X" button dismissing a modal sheet on iOS? When we have "X" and "✓" the case is easy, but what if we just have "X" button? For example, on a "What's New" screen. I found guidelines here: https://developer.apple.com/design/human-interface-guidelines/sheets Based on resizable sheets presented there, the "X" button should be on leading edge. However, iOS is inconsistent and sometimes it presents the button on the leading edge and sometimes on the trailing edge. How do you approach it? Do you prefer having it on the leading or trailing edge? SwiftUI provides ToolbarItem(placement: .cancellationAction) , but it doesn't feel like the right placement. cancellationAction sounds like something for actionable sheets when we have something to be cancelled. I'm missing placement: .dismiss :).
1
0
1.3k
1w
NavigationSplitView without detail information
Hi, I have a working program with categories, lists and detail views. All connected via NavigationLink inside a NavigationStack. It looks a bit strange on an iPad, so I replaced the NavigationStack with a NavigationSplitView. The categories went into the sidebar and the lists into the content section. The detail section of the NavigationSplitView is empty and it still works. Is that the correct behavior? I don't mind it, because it works. example code: var body: some View { NavigationSplitView { List(selection: $listSelection) { Section("Section 1") { NavigationLink(value: EnumValue.V1) { CardView("Card 1") } NavigationLink(value: EnumValue.V2) { CardView("Card 2") } NavigationLink(value: EnumValue.V3) { CardView("Card 3") } } Section("Section 2") { NavigationLink(value: EnumValue.V4) { CardView("Card 4") } NavigationLink(value: EnumValue.V5) { CardView("Card 5") } } } .scrollIndicators(.hidden) .navigationTitle("Detail Empty") } content: { if let mt = listSelection { switch mt { case .V1: ListView1() case .V2: ListView2() case .V3: ListView3() case .V4: ListView4() case .V5: ListView5() } } } detail: { // empty } } In each ListView, I assign the ID to the EditorView so that the EditorView is updated. .navigationDestination(for: ViewListTransfer.self) { vl in switch vl { case .trend(bp: let bp): EditorView(value: bp) .id(bp.Id) case .trendNew: EditorView(val: nil) } } It seems to work because everything is linked via NavigationLinks, but it does look strange that the detail section is empty. The details section is mandatory; therefore, it cannot be omitted. Is this behavior something for a bug report? Christian
2
0
404
1w
Using .sidebarAdaptable style breaks NavigationStack's transition animation in tvOS
When a NavigationStack is nested inside a TabView that's using the .sidebarAdaptable style, it completely overwrites the transition animations of the NavigationStack. In tvOS 26, the transition for push and pop instantly snaps in place. in tvOS 27, it uses its own transition animation, which has strange behaviors, such as slow crossfade, and items rearranging if the frame of the child view is different. Here is a sample code that shows what I'm talking about. struct ContentView: View { @State private var selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { Tab("Home", systemImage: "house", value: 0) { NavigationStack { VStack(spacing: 30) { Text("Root Home View") .font(.title) NavigationLink("Push Detail View") { Text("Child View") .font(.largeTitle) } } } } } .tabViewStyle(.sidebarAdaptable) } } Note: Placing NavigationStack over a TabView will retain the NavigationStack's transition animations, but you lose navigation paths per tab, and child views a rendered over the tabview.
0
0
326
1w
Question: Task modifier with id called on initial
Hello, I have a question regarding the task(id:) modifier. It is possible, of course, that I am using the modifier for the wrong purpose. Consider a multi-field search scenario where any change to a field triggers a loading process via the task(id:) modifier. The problem is that every task(id:) modifier executes when the view is first loaded, causing the same data to be loaded multiple times—which makes no sense. I do not want to add a separate search button or implement debouncing; those would merely be workarounds to bypass the actual issue. The onChange modifier has an initial parameter that defaults to false, preventing the action from running when the view is first loaded. However, the task(id:) modifier does not have this parameter. How can I prevent all task(id:) modifiers from executing when the view loads? After all, the standard task modifier should handle the initial load. struct ContentView: View { @State private var field1: String = "" @State private var field2: String = "" @State private var field3: String = "" var body: some View { VStack { TextField("val1", text: $field1) TextField("val2", text: $field2) TextField("val3", text: $field3) } .padding() .task { print("task") } .task(id: field1) { print("task 1") } .task(id: field2) { print("task 2") } .task(id: field3) { print("task 3") } } } Christian
1
0
300
1w
Can we opt out of the Horizontal Bar on the iPhone Duo?
We are aware that we can opt out of the vertical bar to make it horizontal for folded and unfolded landscape: https://developer.apple.com/documentation/uikit/uiviewcontroller/preferredverticalbarbehavior As per the title, we would like to know if if the reverse is possible (i.e. can we have the unfolded portrait display a vertical bar instead of a horizontal one)
1
0
228
1w
[VoiceOver] Dynamic fractions (e.g., ½, 1/2) are completely omitted/skipped in iOS 27
Platform & OS Version: iOS 27 Screen Reader: VoiceOver Framework: SwiftUI / UIKit / WebKit (WKWebView) Issue Summary: When updating dynamic text or UI elements containing fraction values—whether using Unicode fraction glyphs (e.g., ½, ¾) VoiceOver in iOS 27 completely omits and skips reading the fraction value entirely. Steps to Reproduce: Enable VoiceOver on iOS 27 (Settings > Accessibility > VoiceOver). Open an app or web view where fraction values, tap to focus on the element or trigger voice over Expected Result: VoiceOver should speak the accessible label or text content (e.g., "one half") Actual Result: VoiceOver skips the fraction entirely, reading only surrounding text or remaining silent over the fraction value.
0
0
519
1w
SwiftUI interactive Liquid Glass highlight boundary for a custom L shape
Full code following thread 826485. Feedback: FB25055820. Earlier calibrated Lab recordings on an iPhone 17 Pro Simulator (iOS 27.2 Beta 1, 24B5084k; Xcode 27.2, 27B5019j) showed a round press highlight inside the L, including on its empty cutout where the Button action did not fire. Interactive off removed it; a wider host widened it without stretching the L. These are Simulator observations. This extracted English project compiled, but has not been newly run or optically recorded. The baseline already uses the SAME L contentShape after glassEffect. Other matching-L placements are not established failures. Which public API controls the highlight boundary, and where should contentShape go? Are nonconvex shapes supported? Hold each arm, then the cutout; compare the highlight and action counter. Default uses the recorded baseline order. import SwiftUI @main struct CustomGlassHighlightApp: App { var body: some Scene { WindowGroup { GlassHighlightStudy() } } } private enum Experiment: String, CaseIterable, Identifiable { case baseline = "Baseline — L after glass" case interactiveOff = "Interactive off" case wideHost = "Wide host (180 pt)" case labelAndAfter = "L on label + after glass" case rectangleBefore = "Rectangle before glass" case rectangleAfter = "Rectangle after glass" var id: String { rawValue } var hostWidth: CGFloat { self == .wideHost ? 180 : 120 } var isInteractive: Bool { self != .interactiveOff } var explanation: String { switch self { case .baseline: return "120 × 120 pt host. The same L Shape is used by glassEffect and by contentShape after glassEffect." case .interactiveOff: return "Same L, frame, and modifier order as baseline; only interactive is false." case .wideHost: return "180 × 120 pt host. The L remains 120 × 120 pt, centered in the wider host." case .labelAndAfter: return "Baseline plus the same L contentShape on the Button label, before the Button is constructed." case .rectangleBefore: return "Diagnostic mismatch: Rectangle contentShape before glassEffect; the glass remains an L." case .rectangleAfter: return "Diagnostic mismatch: Rectangle contentShape after glassEffect; the glass remains an L." } } } // A fixed-size nonconvex path. Widening its host does not stretch the L. private struct Fixed120GlassL: Shape { func path(in rect: CGRect) -> Path { let origin = CGPoint(x: rect.midX - 60, y: rect.midY - 60) let points: [CGPoint] = [ .zero, CGPoint(x: 120, y: 0), CGPoint(x: 120, y: 42), CGPoint(x: 42, y: 42), CGPoint(x: 42, y: 120), CGPoint(x: 0, y: 120) ] var path = Path() for (index, point) in points.enumerated() { let target = CGPoint(x: origin.x + point.x, y: origin.y + point.y) if index == 0 { path.move(to: target) } else { path.addLine(to: target) } } path.closeSubpath() return path } } private enum HintPosition { case label, before, after } private struct HitShapeHint: ViewModifier { let experiment: Experiment let position: HintPosition @ViewBuilder func body(content: Content) -> some View { switch (experiment, position) { case (.labelAndAfter, .label): content.contentShape(.interaction, Fixed120GlassL()) case (.rectangleBefore, .before), (.rectangleAfter, .after): content.contentShape(.interaction, Rectangle()) case (.baseline, .after), (.interactiveOff, .after), (.wideHost, .after), (.labelAndAfter, .after): content.contentShape(.interaction, Fixed120GlassL()) default: content } } } private struct GlassHighlightStudy: View { @State private var experiment: Experiment = .baseline @State private var actionCount = 0 var body: some View { ZStack { LinearGradient( colors: [Color(red: 0.06, green: 0.14, blue: 0.26), Color(red: 0.38, green: 0.00, blue: 0.25), .black], startPoint: .topLeading, endPoint: .bottomTrailing ).ignoresSafeArea() VStack(spacing: 24) { Text("Custom Glass Highlight") .font(.title2.bold()) Picker("Experiment", selection: $experiment) { ForEach(Experiment.allCases) { item in Text(item.rawValue).tag(item) } } .pickerStyle(.menu) Text(experiment.explanation) .font(.callout) .frame(minHeight: 76, alignment: .topLeading) .frame(maxWidth: .infinity, alignment: .leading) // This modifier order is extracted from the earlier causal probe. Button { actionCount += 1 } label: { Fixed120GlassL().fill(Color.white.opacity(0.001)) .frame(width: experiment.hostWidth, height: 120) .modifier(HitShapeHint(experiment: experiment, position: .label)) } .buttonStyle(.plain) .modifier(HitShapeHint(experiment: experiment, position: .before)) .glassEffect(.regular.interactive(experiment.isInteractive), in: Fixed120GlassL()) .modifier(HitShapeHint(experiment: experiment, position: .after)) .overlay { Fixed120GlassL() .stroke(Color.white.opacity(0.35), lineWidth: 0.5) .allowsHitTesting(false) .accessibilityHidden(true) } .frame(width: experiment.hostWidth, height: 120) .frame(width: 220, height: 220) .id(experiment) .accessibilityLabel("L-shaped glass button") Text("Button actions: \(actionCount)") .monospacedDigit() Text("Press and hold the top arm, then the lower arm. Try the empty lower-right cutout. Compare the highlight with the L contour and the action counter.") .font(.callout) .frame(maxWidth: .infinity, alignment: .leading) Spacer(minLength: 0) } .foregroundStyle(.white) .padding(24) } .preferredColorScheme(.dark) .onChange(of: experiment) { _, _ in actionCount = 0 } } }
0
0
130
1w
TimelineView's custom schedule termination
Hello, How should I terminate my custom implementation of TimelineSchedule? Should I return nil or .distantFuture? Is there actual difference? Please find my sample implementation below: struct FixedNumberSchedule: TimelineSchedule { let count: Int func entries( from startDate: Date, mode: Mode ) -> Entries { Entries(startDate: startDate, count: count) } struct Entries: Sequence, IteratorProtocol { var currentDate: Date? let initialCount: Int var count: Int init(startDate: Date,count: Int) { self.currentDate = startDate self.initialCount = count self.count = count } mutating func next() -> Date? { defer { if count > 1 { currentDate?.addTimeInterval(1) count -= 1 } else { self.currentDate = nil } } print("Seq \(initialCount), \(currentDate, default: "none")") return currentDate } } } Here's the catch: For FixedNumberSchedule(count: 1) I get logs in console: Seq 1, 2026-10-03 10:04:33 +0000 Seq 1, none For FixedNumberSchedule(count: 2) I get infinite logs in console: Seq 2, 2026-10-03 10:04:33 +0000 Seq 2, 2026-10-03 10:04:34 +0000 Seq 2, none Seq 2, none ... Side note: the displayed result is the same for both sequences (the last valid element from second sequence is never displayed). So, If the sequence has more than a single entry to return, it actually never ends. Is this a bug? Of course, if the sequence has ended, I could keep the code as simple as possible. But the loop still troubles me, thus my initial question: Should I actually return nil or .distantFuture? BR, Krystian
0
0
326
2w
Live Activity background tint and action colour use light appearance on a dark Lock Screen (iOS 27)
On iOS 27, a Live Activity's activityBackgroundTint and activitySystemActionForegroundColor are sometimes resolved with their light-appearance variants while the Lock Screen is dark. The activity's own content correctly uses dark at the same time. This is corrected the next time the activity is updated. If the tint's light variant is near-white, the system replaces it with its default blue. The Live Activity background and the "Allow Live Activities" prompt turn bright blue. The "Allow"/"Don't Allow" labels also use the action colour's light variant (black instead of white). Observed on the following: iPhone Duo, iOS 27.1: start an activity on a dark wallpaper when the extension's first render is slow. Adding Thread.sleep(forTimeInterval: 1) to the body reproduces it reliably. In a real app, a heavier first render (custom font registration, larger layouts) triggers it some of the time. iPhone 18 Pro, iOS 27.0: start an activity on a light wallpaper, then switch to a dark wallpaper. I have tried an asset catalogue colour, a dynamic UIColor, switching on colorScheme, and an identical update straight after requesting the activity, but all still show blue. Only a fixed dark tint with a fixed white action colour avoids it. I've filed FB25019357 with a sample project attached. Are there any known workarounds?
0
0
224
2w
Revealed keyboard drifts in progressive immersion, and no immersion style gives windows-in-front + Crown + passthrough cutouts
Environment: Apple Vision Pro, visionOS 27.0.1 (24M372). Magic Keyboard, with Settings › Awareness & Safety › Reveal Keyboards set to Always. Our app puts the user in an opaque virtual room while they work at their real desk, using app windows and Mac Virtual Display. A virtual desk is placed on the real desk. It is world-locked: positioned once from a PlaneAnchor (PlaneDetectionProvider), then a plain entity at fixed ImmersiveSpace coordinates. No AnchorEntity or WorldAnchor is involved. We offer two modes: .mixed, and .progressive for Digital Crown control. Each gives us only half of what desk work needs. 1. The revealed keyboard drifts in progressive immersion (FB25006789) In .progressive, the physical keyboard shown by keyboard awareness does not stay registered with the virtual desk. After the keyboard has been out of view for a few seconds (for example, while looking at a Mac Virtual Display window), it looks "floaty" when you look back: as the head turns, it slides against the desk. Turning right, the keyboard moves left farther than the desk does. What we checked: It happens in .progressive at any Crown amount, and in .full. With identical content in .mixed (no app passthrough cutout, so again only keyboard awareness shows the keyboard), it is much steadier. On 27.0.1, PlaneAnchor and DeviceAnchor coordinateSpace(correction: .rendered) are exactly equal to their .none transforms: 0 mm, 0°, scale 1. That held over 835 per-frame samples with ±50° yaw and up to 36 cm of head movement. So the rendered correction gives the app nothing to compensate with. The app's desk transform is constant while the drift is visible. upperLimbVisibility(.automatic) vs .visible and a .portrait progressive aspect ratio make no difference. Possibly related: https://developer.apple.com/forums/thread/796557 (FB19666209). 2. No immersion style combines what desk work needs (FB25006837) We need, at the same time: windows (our own and Mac Virtual Display) never covered by the room's walls, furniture or trees; the Digital Crown to bring the real surroundings back in; a passthrough cutout over the real desk, and a well-registered keyboard. .mixed: ✓ OcclusionMaterial cutouts reveal passthrough. ✓ The keyboard stays registered. ✗ RealityKit entities can render in front of windows, including Mac Virtual Display and window chrome. breakthroughEffect doesn't cover whole windows or Mac Virtual Display. ✗ The Crown can't adjust immersion. .progressive: ✓ Windows always stay in front of immersive content (as an Apple engineer confirmed in https://developer.apple.com/forums/thread/764556). ✓ The Crown adjusts immersion. ✗ OcclusionMaterial reveals nothing inside the portal, so there is no desk cutout. ✗ The keyboard drifts (issue 1). Because of issue 1 we have switched our default back to .mixed, and lost windows-in-front. Questions Is keyboard awareness in .progressive/.full expected to be registered less accurately than in .mixed? Is there a supported way to keep it registered with world-locked content, or to get the pose at which the system draws it? In .mixed, is there a supported way to keep windows (including Mac Virtual Display) in front of an app's immersive content? Or, in .progressive, a supported way to show passthrough in an app-defined region inside the portal? Any pointers or workarounds are appreciated.
0
0
1.5k
2w
navigationTransition in NavigationStack does not work in sheets in iOS 27
navigationTransition(_ style: some NavigationTransition) in NavigationStack does not work in sheets in iOS 27 and iPadOS 27. NavigationTransitions allow a smooth transition from one view to another. An app perfectly working on iOS/iPadOS 26.x does not work correctly on iOS/iPadOS 27.x We have made a simple sample project to show the issue. On 26 OSes there are smooth transitions, on 27.0 when a navigationTransition should occur after a push in the NavigationStack in a sheet, the sheet becomes invisible (the main screen reappears) and suddenly the new view appears without transition. Sample project in Feedback FB24996035 Sample line with the issue SecondPathView(namespace: namespace, path: $path) .navigationTransition(.zoom(sourceID: transitionFromFirstPathId, in: namespace))
0
1
412
2w
Ghost Padding on NavigationBarPlatterContainer
Hello, I have a SwiftUI View sitting in the UIHosting view controller. On rotation to landscape, the system would add padding to the toolbar. I presume it's the nav bar. Anyone experienced this? What would be this space? padding? content margin?
Replies
4
Boosts
0
Views
515
Activity
1w
Button or onTapGesture grows its hit region by a fixed amount past the view’s bounds.
SwiftUI Button hit area extends ~16pt beyond its visual frame — expected OS behavior? Hi everyone, I’m working on a custom SwiftUI segmented control and noticed some unexpected hit-testing behavior with Button. I have a button structured roughly like this: Button { selectItem(at: index) } label: { ZStack { if selectedIndex == index { selectedBackground } SegmentCellView( tab: tab, isSelected: selectedIndex == index ) } .frame(maxWidth: .infinity) .contentShape(Rectangle()) } .accessibilityLabel(Text(tab.labelText)) The visual frame of the button has a certain height, but when testing on a real device, I can tap approximately 16pt outside the button's visible frame and the button still receives the tap. This seems to happen even though I explicitly use: .contentShape(Rectangle()) to define the tappable shape. My questions are: Is this ~16pt expansion of the interactive area expected SwiftUI/OS behavior? Is this related to Apple's minimum hit-target/accessibility guidelines being automatically applied to controls such as Button? If so, is there an official API/documentation describing this behavior? Is there a recommended way to make the Button's hit-testing area exactly match the bounds defined by .contentShape(Rectangle()), if that is actually possible? I'm particularly interested in understanding whether this is intentional system behavior or something caused by the ButtonStyle / layout hierarchy. Thanks!
Replies
0
Boosts
0
Views
68
Activity
1w
toolbarMinimizationBehavior(_:for:) crashes on some iOS 27.0.0 devices: "missing weak symbol" despite #available(iOS 27.0, *)
We are seeing a production crash that only affects devices reporting iOS 27.0.0. Devices on 27.0.1 and later, and on iOS 26, are unaffected. The app is built with Xcode 27.0 (the same binary shape appears with the Xcode 27.1 RC toolchain), deployment target iOS 26.0. The crashing code is a plain availability-gated call: extension View { @ViewBuilder func disableNavigationBarMinimization() -> some View { if #available(iOS 27.0, *) { toolbarMinimizationBehavior(.never, for: .navigationBar) } else { self } } } Crash report excerpt (addresses and app symbols removed): crash_info_entry_0: Failed to look up symbolic reference at <addr> - offset <n> - symbol <nearest stripped symbol> in <app binary> - pointer at <addr> is likely a reference to a missing weak symbol Crashed: com.apple.main-thread 0 libsystem_kernel.dylib __pthread_kill 2 libsystem_c.dylib abort 3 libswiftCore.dylib <redacted> 9 libswiftCore.dylib swift_getTypeByMangledName + 908 10 libswiftCore.dylib swift_getTypeByMangledNameInContext2 + 248 11 <app> __swift_instantiateConcreteTypeFromMangledNameV2 12 <app> specialized closure #1 in View.disableNavigationBarMinimization() 13 <app> closure #1 in SomeView.body.getter What we established: Because the deployment target is 26.0 and the API is @available(iOS 27.0, *), the app weak-imports both the function and its opaque result type descriptor. dyld_info -imports on the binary shows: _$s7SwiftUI4ViewPAAE27toolbarMinimizationBehavior_3forQrAA07ToolbareF0V_AA0H9PlacementVdtF [weak-import] (from SwiftUI) _$s7SwiftUI4ViewPAAE27toolbarMinimizationBehavior_3forQrAA07ToolbareF0V_AA0H9PlacementVdtFQOMQ [weak-import] (from SwiftUI) The call site has to instantiate the opaque return type's metadata before calling the function. That is the frame that aborts. So when dyld binds the descriptor to null, #available(iOS 27.0, *) passes and the process still dies. The symbol is present and exported on the iOS 27.0 build we can inspect locally (24A437, via Xcode's DeviceSupport symbols), and the same code runs fine on the iOS 27.0 simulator runtime (24A434). We cannot reproduce the crash on any device we own. The SwiftUI binary in 24A437 exports both toolbarMinimizeBehavior(_:for:) / ToolbarMinimizeBehavior and toolbarMinimizationBehavior(_:for:) / ToolbarMinimizationBehavior. The iOS 27.1 SDK only declares the latter, still annotated @available(iOS 27.0, *). This looks like a rename late in the 27.0 cycle. Our working theory is that some iOS 27.0.0 builds in the field (earlier 27.0 builds, possibly the ones preinstalled on new devices) predate the rename and only contain toolbarMinimizeBehavior, so the renamed symbol is absent and the weak import resolves to null. Questions: Is the @available(iOS 27.0, *) annotation on toolbarMinimizationBehavior(_:for:) accurate for every shipped 27.0.0 build? Should it be 27.0.1 or 27.1? Is there a supported way to guard against a missing opaque type descriptor at runtime, short of raising the availability check? Feedback filed as FB25095054.
Replies
0
Boosts
0
Views
133
Activity
1w
Accent Color is broken in Xcode 27.1 RC 1
In Xcode 27.1 RC 1 there is a regression bug displaying incorrect accent color across the app. It worked correctly in Xcode 27.1 Beta 1. It affects borderPromintent top bar buttons and tab bars: https://x.com/kulik_wojciech/status/2107252543709692166?s=20 I already reported it as a bug: FB25074768
Replies
4
Boosts
0
Views
206
Activity
1w
SwiftUI views lock up after background and sleep for “Designed for iPad” apps
There's an easily reproducible SwiftUI bug on macOS where an app's UI state no longer updates/re-renders for "Designed for iPad" apps (i.e. ProcessInfo.processInfo.isiOSAppOnMac == true). The bug occurs in Xcode and also if the app is running independent of Xcode. The bug occurs when: the user Hides the app (i.e. it goes into the background) the user puts the Mac to sleep (e.g. Apple menu > Sleep) a total of ~60 seconds transpires (i.e. macOS puts the app into the "suspended state") when the app is brought back into the foreground the UI no longer updates properly The only way I have found to fix this is to manually open a new actual full app window via File > New, in which case the app works fine again in the new window. The following extremely simple code in a default Xcode project illustrates the issue: import SwiftUI @main struct staleApp: App { @State private var isBright = true var body: some Scene { WindowGroup() { ZStack { (isBright ? Color.white : Color.black).ignoresSafeArea() Button("TOGGLE") { isBright.toggle(); print("TAPPED") } } .onAppear { print("\(isBright ? "light" : "dark") view appeared") } } } } For the code above, after Hiding the app and putting the computer to sleep for 60 seconds or more, the button no longer swaps views, although the print statements still appear in the console upon tapping the button. Also, while in this buggy state, i can get the view to update to the current state (i.e. the view triggered by the last tap) by manually dragging the corner of the app window to resize the window. But after resizing, the view again does not update upon button tapping until I resize the window again. so it appears the diff engine is mucked or that the Scene or WindowGroup are no longer correctly running on the main thread I have tried rebuilding the entire view hierarchy by updating .id() on views but this approach does NOT work. I have tried many other options/hacks but have not been able to reset the 'view engine' other than opening a new window manually or by using: @Environment(.openWindow) private var openWindow openWindow could be a viable solution except there's no way to programmatically close the old window for isiOSAppOnMac (@Environment(.dismissWindow) private var dismissWindow doesn't work for iOS)
Replies
2
Boosts
1
Views
371
Activity
1w
iOS 27: SwiftUI keyboard safe area is not restored after returning to a UIHostingController
I have a SwiftUI view embedded in a UIHostingController. It contains a numeric text field at the top and a .borderedProminent button at the bottom. The text field is automatically focused when the view appears. The bottom button pushes another instance of the same hosting controller. On the initial appearance, SwiftUI correctly positions the button above the keyboard. However, after navigating back from VC2 to VC1, the text field is focused and the keyboard is visible, but the button remains behind the keyboard. This occurs regardless of whether VC2 is closed using the navigation bar back button or the interactive back gesture. This appears to be an iOS 27 regression involving SwiftUI keyboard avoidance inside a UIHostingController. The issue is especially problematic with a numeric keyboard because it has no Return or Done key. If the covered button is the primary way to continue or dismiss the keyboard, the user can no longer access it. Interestingly, the button is repositioned correctly as soon as I begin moving the app into the background. This suggests that SwiftUI still knows about the keyboard safe area but does not update the layout correctly when the hosting controller reappears after being popped to. Minimal reproducible example import SwiftUI import UIKit final class NumericInputHostingController: UIHostingController<NumericInputView> { init() { super.init( rootView: NumericInputView(onContinue: {}) ) rootView = NumericInputView { [weak self] in self?.navigationController?.pushViewController( NumericInputHostingController(), animated: true ) } } @available(*, unavailable) @MainActor required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } struct NumericInputView: View { @State private var value = "" @FocusState private var isTextFieldFocused: Bool let onContinue: () -> Void var body: some View { VStack(spacing: 24) { TextField("Enter a number", text: $value) .keyboardType(.numberPad) .focused($isTextFieldFocused) .padding(.horizontal, 16) .frame(height: 52) .background { RoundedRectangle(cornerRadius: 10) .stroke( Color.accentColor, lineWidth: 1.5 ) } Spacer() Button("Open Another", action: onContinue) .buttonStyle(.borderedProminent) .controlSize(.large) .frame(maxWidth: .infinity) } .padding(20) .background(Color(.systemBackground)) .onAppear { DispatchQueue.main.async { isTextFieldFocused = true } } .onDisappear { isTextFieldFocused = false } } } The initial controller is embedded in a navigation controller: let controller = NumericInputHostingController() let navigationController = UINavigationController( rootViewController: controller ) Steps to reproduce Present VC1. VC1 automatically focuses the numeric text field. Confirm that the bottom button is above the keyboard. Tap the button to push VC2. Navigate back to VC1. VC1 focuses its text field and displays the numeric keyboard. Observe that the bottom button is now behind the keyboard. Begin putting the app into the background. Observe that the button suddenly moves to the correct position above the keyboard. UIKit’s keyboardLayoutGuide handles the equivalent UIKit layout correctly. Is this a known issue with keyboard safe-area updates when a UIHostingController reappears after navigation? Is there a supported SwiftUI solution that does not require observing keyboard notifications manually?
Replies
3
Boosts
2
Views
404
Activity
1w
.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
Replies
10
Boosts
3
Views
1.6k
Activity
1w
Dismiss/Close button - leading or trailing edge?
What is the recommended position of the "X" button dismissing a modal sheet on iOS? When we have "X" and "✓" the case is easy, but what if we just have "X" button? For example, on a "What's New" screen. I found guidelines here: https://developer.apple.com/design/human-interface-guidelines/sheets Based on resizable sheets presented there, the "X" button should be on leading edge. However, iOS is inconsistent and sometimes it presents the button on the leading edge and sometimes on the trailing edge. How do you approach it? Do you prefer having it on the leading or trailing edge? SwiftUI provides ToolbarItem(placement: .cancellationAction) , but it doesn't feel like the right placement. cancellationAction sounds like something for actionable sheets when we have something to be cancelled. I'm missing placement: .dismiss :).
Replies
1
Boosts
0
Views
1.3k
Activity
1w
NavigationSplitView without detail information
Hi, I have a working program with categories, lists and detail views. All connected via NavigationLink inside a NavigationStack. It looks a bit strange on an iPad, so I replaced the NavigationStack with a NavigationSplitView. The categories went into the sidebar and the lists into the content section. The detail section of the NavigationSplitView is empty and it still works. Is that the correct behavior? I don't mind it, because it works. example code: var body: some View { NavigationSplitView { List(selection: $listSelection) { Section("Section 1") { NavigationLink(value: EnumValue.V1) { CardView("Card 1") } NavigationLink(value: EnumValue.V2) { CardView("Card 2") } NavigationLink(value: EnumValue.V3) { CardView("Card 3") } } Section("Section 2") { NavigationLink(value: EnumValue.V4) { CardView("Card 4") } NavigationLink(value: EnumValue.V5) { CardView("Card 5") } } } .scrollIndicators(.hidden) .navigationTitle("Detail Empty") } content: { if let mt = listSelection { switch mt { case .V1: ListView1() case .V2: ListView2() case .V3: ListView3() case .V4: ListView4() case .V5: ListView5() } } } detail: { // empty } } In each ListView, I assign the ID to the EditorView so that the EditorView is updated. .navigationDestination(for: ViewListTransfer.self) { vl in switch vl { case .trend(bp: let bp): EditorView(value: bp) .id(bp.Id) case .trendNew: EditorView(val: nil) } } It seems to work because everything is linked via NavigationLinks, but it does look strange that the detail section is empty. The details section is mandatory; therefore, it cannot be omitted. Is this behavior something for a bug report? Christian
Replies
2
Boosts
0
Views
404
Activity
1w
Using .sidebarAdaptable style breaks NavigationStack's transition animation in tvOS
When a NavigationStack is nested inside a TabView that's using the .sidebarAdaptable style, it completely overwrites the transition animations of the NavigationStack. In tvOS 26, the transition for push and pop instantly snaps in place. in tvOS 27, it uses its own transition animation, which has strange behaviors, such as slow crossfade, and items rearranging if the frame of the child view is different. Here is a sample code that shows what I'm talking about. struct ContentView: View { @State private var selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { Tab("Home", systemImage: "house", value: 0) { NavigationStack { VStack(spacing: 30) { Text("Root Home View") .font(.title) NavigationLink("Push Detail View") { Text("Child View") .font(.largeTitle) } } } } } .tabViewStyle(.sidebarAdaptable) } } Note: Placing NavigationStack over a TabView will retain the NavigationStack's transition animations, but you lose navigation paths per tab, and child views a rendered over the tabview.
Replies
0
Boosts
0
Views
326
Activity
1w
Question: Task modifier with id called on initial
Hello, I have a question regarding the task(id:) modifier. It is possible, of course, that I am using the modifier for the wrong purpose. Consider a multi-field search scenario where any change to a field triggers a loading process via the task(id:) modifier. The problem is that every task(id:) modifier executes when the view is first loaded, causing the same data to be loaded multiple times—which makes no sense. I do not want to add a separate search button or implement debouncing; those would merely be workarounds to bypass the actual issue. The onChange modifier has an initial parameter that defaults to false, preventing the action from running when the view is first loaded. However, the task(id:) modifier does not have this parameter. How can I prevent all task(id:) modifiers from executing when the view loads? After all, the standard task modifier should handle the initial load. struct ContentView: View { @State private var field1: String = "" @State private var field2: String = "" @State private var field3: String = "" var body: some View { VStack { TextField("val1", text: $field1) TextField("val2", text: $field2) TextField("val3", text: $field3) } .padding() .task { print("task") } .task(id: field1) { print("task 1") } .task(id: field2) { print("task 2") } .task(id: field3) { print("task 3") } } } Christian
Replies
1
Boosts
0
Views
300
Activity
1w
Is it possible to apply the glass effect to the top toolbar?
when I tried ToolbarItemGroup(placement: .title) { VStack { header() }.glassEffect() } the glass effect is only applied to the element itself. And the ToolbarItemGroup does seem to have the glass effect apis either. What I want is for the overall toolbar to have the glass effect, not just the individual elements in the toolbar.
Replies
0
Boosts
0
Views
116
Activity
1w
Can we opt out of the Horizontal Bar on the iPhone Duo?
We are aware that we can opt out of the vertical bar to make it horizontal for folded and unfolded landscape: https://developer.apple.com/documentation/uikit/uiviewcontroller/preferredverticalbarbehavior As per the title, we would like to know if if the reverse is possible (i.e. can we have the unfolded portrait display a vertical bar instead of a horizontal one)
Replies
1
Boosts
0
Views
228
Activity
1w
[VoiceOver] Dynamic fractions (e.g., ½, 1/2) are completely omitted/skipped in iOS 27
Platform & OS Version: iOS 27 Screen Reader: VoiceOver Framework: SwiftUI / UIKit / WebKit (WKWebView) Issue Summary: When updating dynamic text or UI elements containing fraction values—whether using Unicode fraction glyphs (e.g., ½, ¾) VoiceOver in iOS 27 completely omits and skips reading the fraction value entirely. Steps to Reproduce: Enable VoiceOver on iOS 27 (Settings > Accessibility > VoiceOver). Open an app or web view where fraction values, tap to focus on the element or trigger voice over Expected Result: VoiceOver should speak the accessible label or text content (e.g., "one half") Actual Result: VoiceOver skips the fraction entirely, reading only surrounding text or remaining silent over the fraction value.
Replies
0
Boosts
0
Views
519
Activity
1w
SwiftUI interactive Liquid Glass highlight boundary for a custom L shape
Full code following thread 826485. Feedback: FB25055820. Earlier calibrated Lab recordings on an iPhone 17 Pro Simulator (iOS 27.2 Beta 1, 24B5084k; Xcode 27.2, 27B5019j) showed a round press highlight inside the L, including on its empty cutout where the Button action did not fire. Interactive off removed it; a wider host widened it without stretching the L. These are Simulator observations. This extracted English project compiled, but has not been newly run or optically recorded. The baseline already uses the SAME L contentShape after glassEffect. Other matching-L placements are not established failures. Which public API controls the highlight boundary, and where should contentShape go? Are nonconvex shapes supported? Hold each arm, then the cutout; compare the highlight and action counter. Default uses the recorded baseline order. import SwiftUI @main struct CustomGlassHighlightApp: App { var body: some Scene { WindowGroup { GlassHighlightStudy() } } } private enum Experiment: String, CaseIterable, Identifiable { case baseline = "Baseline — L after glass" case interactiveOff = "Interactive off" case wideHost = "Wide host (180 pt)" case labelAndAfter = "L on label + after glass" case rectangleBefore = "Rectangle before glass" case rectangleAfter = "Rectangle after glass" var id: String { rawValue } var hostWidth: CGFloat { self == .wideHost ? 180 : 120 } var isInteractive: Bool { self != .interactiveOff } var explanation: String { switch self { case .baseline: return "120 × 120 pt host. The same L Shape is used by glassEffect and by contentShape after glassEffect." case .interactiveOff: return "Same L, frame, and modifier order as baseline; only interactive is false." case .wideHost: return "180 × 120 pt host. The L remains 120 × 120 pt, centered in the wider host." case .labelAndAfter: return "Baseline plus the same L contentShape on the Button label, before the Button is constructed." case .rectangleBefore: return "Diagnostic mismatch: Rectangle contentShape before glassEffect; the glass remains an L." case .rectangleAfter: return "Diagnostic mismatch: Rectangle contentShape after glassEffect; the glass remains an L." } } } // A fixed-size nonconvex path. Widening its host does not stretch the L. private struct Fixed120GlassL: Shape { func path(in rect: CGRect) -> Path { let origin = CGPoint(x: rect.midX - 60, y: rect.midY - 60) let points: [CGPoint] = [ .zero, CGPoint(x: 120, y: 0), CGPoint(x: 120, y: 42), CGPoint(x: 42, y: 42), CGPoint(x: 42, y: 120), CGPoint(x: 0, y: 120) ] var path = Path() for (index, point) in points.enumerated() { let target = CGPoint(x: origin.x + point.x, y: origin.y + point.y) if index == 0 { path.move(to: target) } else { path.addLine(to: target) } } path.closeSubpath() return path } } private enum HintPosition { case label, before, after } private struct HitShapeHint: ViewModifier { let experiment: Experiment let position: HintPosition @ViewBuilder func body(content: Content) -> some View { switch (experiment, position) { case (.labelAndAfter, .label): content.contentShape(.interaction, Fixed120GlassL()) case (.rectangleBefore, .before), (.rectangleAfter, .after): content.contentShape(.interaction, Rectangle()) case (.baseline, .after), (.interactiveOff, .after), (.wideHost, .after), (.labelAndAfter, .after): content.contentShape(.interaction, Fixed120GlassL()) default: content } } } private struct GlassHighlightStudy: View { @State private var experiment: Experiment = .baseline @State private var actionCount = 0 var body: some View { ZStack { LinearGradient( colors: [Color(red: 0.06, green: 0.14, blue: 0.26), Color(red: 0.38, green: 0.00, blue: 0.25), .black], startPoint: .topLeading, endPoint: .bottomTrailing ).ignoresSafeArea() VStack(spacing: 24) { Text("Custom Glass Highlight") .font(.title2.bold()) Picker("Experiment", selection: $experiment) { ForEach(Experiment.allCases) { item in Text(item.rawValue).tag(item) } } .pickerStyle(.menu) Text(experiment.explanation) .font(.callout) .frame(minHeight: 76, alignment: .topLeading) .frame(maxWidth: .infinity, alignment: .leading) // This modifier order is extracted from the earlier causal probe. Button { actionCount += 1 } label: { Fixed120GlassL().fill(Color.white.opacity(0.001)) .frame(width: experiment.hostWidth, height: 120) .modifier(HitShapeHint(experiment: experiment, position: .label)) } .buttonStyle(.plain) .modifier(HitShapeHint(experiment: experiment, position: .before)) .glassEffect(.regular.interactive(experiment.isInteractive), in: Fixed120GlassL()) .modifier(HitShapeHint(experiment: experiment, position: .after)) .overlay { Fixed120GlassL() .stroke(Color.white.opacity(0.35), lineWidth: 0.5) .allowsHitTesting(false) .accessibilityHidden(true) } .frame(width: experiment.hostWidth, height: 120) .frame(width: 220, height: 220) .id(experiment) .accessibilityLabel("L-shaped glass button") Text("Button actions: \(actionCount)") .monospacedDigit() Text("Press and hold the top arm, then the lower arm. Try the empty lower-right cutout. Compare the highlight with the L contour and the action counter.") .font(.callout) .frame(maxWidth: .infinity, alignment: .leading) Spacer(minLength: 0) } .foregroundStyle(.white) .padding(24) } .preferredColorScheme(.dark) .onChange(of: experiment) { _, _ in actionCount = 0 } } }
Replies
0
Boosts
0
Views
130
Activity
1w
TimelineView's custom schedule termination
Hello, How should I terminate my custom implementation of TimelineSchedule? Should I return nil or .distantFuture? Is there actual difference? Please find my sample implementation below: struct FixedNumberSchedule: TimelineSchedule { let count: Int func entries( from startDate: Date, mode: Mode ) -> Entries { Entries(startDate: startDate, count: count) } struct Entries: Sequence, IteratorProtocol { var currentDate: Date? let initialCount: Int var count: Int init(startDate: Date,count: Int) { self.currentDate = startDate self.initialCount = count self.count = count } mutating func next() -> Date? { defer { if count > 1 { currentDate?.addTimeInterval(1) count -= 1 } else { self.currentDate = nil } } print("Seq \(initialCount), \(currentDate, default: "none")") return currentDate } } } Here's the catch: For FixedNumberSchedule(count: 1) I get logs in console: Seq 1, 2026-10-03 10:04:33 +0000 Seq 1, none For FixedNumberSchedule(count: 2) I get infinite logs in console: Seq 2, 2026-10-03 10:04:33 +0000 Seq 2, 2026-10-03 10:04:34 +0000 Seq 2, none Seq 2, none ... Side note: the displayed result is the same for both sequences (the last valid element from second sequence is never displayed). So, If the sequence has more than a single entry to return, it actually never ends. Is this a bug? Of course, if the sequence has ended, I could keep the code as simple as possible. But the loop still troubles me, thus my initial question: Should I actually return nil or .distantFuture? BR, Krystian
Replies
0
Boosts
0
Views
326
Activity
2w
in iPadOS remove New window from Window top menu bar in Swift UI
Hi, I have an univesal iOS app that supports multiple windows but i dont want the user to create the new window from WIndow top menu bar in iPad. I will create the windows from the main app using openWindow. Is there a way i can hide the New Window in the Window Menu top bar? Thanks in advance
Replies
0
Boosts
0
Views
347
Activity
2w
Live Activity background tint and action colour use light appearance on a dark Lock Screen (iOS 27)
On iOS 27, a Live Activity's activityBackgroundTint and activitySystemActionForegroundColor are sometimes resolved with their light-appearance variants while the Lock Screen is dark. The activity's own content correctly uses dark at the same time. This is corrected the next time the activity is updated. If the tint's light variant is near-white, the system replaces it with its default blue. The Live Activity background and the "Allow Live Activities" prompt turn bright blue. The "Allow"/"Don't Allow" labels also use the action colour's light variant (black instead of white). Observed on the following: iPhone Duo, iOS 27.1: start an activity on a dark wallpaper when the extension's first render is slow. Adding Thread.sleep(forTimeInterval: 1) to the body reproduces it reliably. In a real app, a heavier first render (custom font registration, larger layouts) triggers it some of the time. iPhone 18 Pro, iOS 27.0: start an activity on a light wallpaper, then switch to a dark wallpaper. I have tried an asset catalogue colour, a dynamic UIColor, switching on colorScheme, and an identical update straight after requesting the activity, but all still show blue. Only a fixed dark tint with a fixed white action colour avoids it. I've filed FB25019357 with a sample project attached. Are there any known workarounds?
Replies
0
Boosts
0
Views
224
Activity
2w
Revealed keyboard drifts in progressive immersion, and no immersion style gives windows-in-front + Crown + passthrough cutouts
Environment: Apple Vision Pro, visionOS 27.0.1 (24M372). Magic Keyboard, with Settings › Awareness & Safety › Reveal Keyboards set to Always. Our app puts the user in an opaque virtual room while they work at their real desk, using app windows and Mac Virtual Display. A virtual desk is placed on the real desk. It is world-locked: positioned once from a PlaneAnchor (PlaneDetectionProvider), then a plain entity at fixed ImmersiveSpace coordinates. No AnchorEntity or WorldAnchor is involved. We offer two modes: .mixed, and .progressive for Digital Crown control. Each gives us only half of what desk work needs. 1. The revealed keyboard drifts in progressive immersion (FB25006789) In .progressive, the physical keyboard shown by keyboard awareness does not stay registered with the virtual desk. After the keyboard has been out of view for a few seconds (for example, while looking at a Mac Virtual Display window), it looks "floaty" when you look back: as the head turns, it slides against the desk. Turning right, the keyboard moves left farther than the desk does. What we checked: It happens in .progressive at any Crown amount, and in .full. With identical content in .mixed (no app passthrough cutout, so again only keyboard awareness shows the keyboard), it is much steadier. On 27.0.1, PlaneAnchor and DeviceAnchor coordinateSpace(correction: .rendered) are exactly equal to their .none transforms: 0 mm, 0°, scale 1. That held over 835 per-frame samples with ±50° yaw and up to 36 cm of head movement. So the rendered correction gives the app nothing to compensate with. The app's desk transform is constant while the drift is visible. upperLimbVisibility(.automatic) vs .visible and a .portrait progressive aspect ratio make no difference. Possibly related: https://developer.apple.com/forums/thread/796557 (FB19666209). 2. No immersion style combines what desk work needs (FB25006837) We need, at the same time: windows (our own and Mac Virtual Display) never covered by the room's walls, furniture or trees; the Digital Crown to bring the real surroundings back in; a passthrough cutout over the real desk, and a well-registered keyboard. .mixed: ✓ OcclusionMaterial cutouts reveal passthrough. ✓ The keyboard stays registered. ✗ RealityKit entities can render in front of windows, including Mac Virtual Display and window chrome. breakthroughEffect doesn't cover whole windows or Mac Virtual Display. ✗ The Crown can't adjust immersion. .progressive: ✓ Windows always stay in front of immersive content (as an Apple engineer confirmed in https://developer.apple.com/forums/thread/764556). ✓ The Crown adjusts immersion. ✗ OcclusionMaterial reveals nothing inside the portal, so there is no desk cutout. ✗ The keyboard drifts (issue 1). Because of issue 1 we have switched our default back to .mixed, and lost windows-in-front. Questions Is keyboard awareness in .progressive/.full expected to be registered less accurately than in .mixed? Is there a supported way to keep it registered with world-locked content, or to get the pose at which the system draws it? In .mixed, is there a supported way to keep windows (including Mac Virtual Display) in front of an app's immersive content? Or, in .progressive, a supported way to show passthrough in an app-defined region inside the portal? Any pointers or workarounds are appreciated.
Replies
0
Boosts
0
Views
1.5k
Activity
2w
navigationTransition in NavigationStack does not work in sheets in iOS 27
navigationTransition(_ style: some NavigationTransition) in NavigationStack does not work in sheets in iOS 27 and iPadOS 27. NavigationTransitions allow a smooth transition from one view to another. An app perfectly working on iOS/iPadOS 26.x does not work correctly on iOS/iPadOS 27.x We have made a simple sample project to show the issue. On 26 OSes there are smooth transitions, on 27.0 when a navigationTransition should occur after a push in the NavigationStack in a sheet, the sheet becomes invisible (the main screen reappears) and suddenly the new view appears without transition. Sample project in Feedback FB24996035 Sample line with the issue SecondPathView(namespace: namespace, path: $path) .navigationTransition(.zoom(sourceID: transitionFromFirstPathId, in: namespace))
Replies
0
Boosts
1
Views
412
Activity
2w