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

The sidebar toggle of the NavigationSplitView disappears when used with a .inspector modifier on iPadOS 26
If the NavigationSplitView on iPadOS 26 is combined with a .inspector column, the sidebarToggle is always hidden, when the sidebar is collapsed. If you remove the .inspector modifier, the sidebarToggle stays visible throughout the collapsed or expanded state. Has maybe someone a workaround for this issue? The problem does not exist in iOS 18. The bug is reported as FB20061260
1
2
151
8h
iWeαlth — private household finance tracker (SwiftUI, on-device data, iPhone)
iWeαlth is a household finance tracker for iPhone built in SwiftUI, with all data stored on device (GRDB/SQLite). There's no account and no backend that receives financial data. I'd especially appreciate feedback on: onboarding and first-run clarity (passcode, sample data) the expense keypad and the transaction detail screen the guided Enable Banking setup (an in-app web view that walks the user through registering their own PSD2 application), if you're in Europe and want to try it with your own accounts general UI polish on different iPhone sizes, light and dark mode Shared trips use a small relay where expense data is end-to-end encrypted with a key that only lives in the share link. TestFlight: https://testflight.apple.com/join/sQNvXPdJ Fastest path: Settings → "Load Sample Data". Thanks for any feedback!
0
0
9
12h
Binding Picker to enum value in SwiftData struct
I have a SwiftData @Model class that has a var of Media. enum Media: String, CaseIterable, Identifiable, Codable { case dvd = "DVD" case bluray = "Blu-ray" case bluray4k = "Blu-ray 4k" var id:String { self.rawValue } } In one of my views, I'm trying to show a Picker with the enum values, and then bind the @Model's field to the Picker's selection. I've been trying all kinds of things from suggesting I'm finding online. Here's a poorly pasted version of what my last attempt looks like (I'm sure I have the wrong number of closing curly brackets, but just ignore that): struct DetailView: View { @Binding var item:Item; // The @Model class. @State private var selectedMedia:Media = .dvd; var body:some View { Picker("Media", selection:$selectedMedia) { ForEach(Media.allCases, id:\.self) { media in Text(media.rawValue); } } .labelsHidden() .onAppear() { selectedMedia = item.media; } .onChange(of:selectedMedia) { oldValue, newValue in item.media = selectedMedia; } } } What's a good way to make this work? Am I going to have to use a custom formatter of some type, bind the Picker to a string, and convert the selected value with the formatter?
1
0
325
1d
Crash in ResolvedStyledText.draw
Hi! We're trying to investigate the following crash, but have a little bit hard time of understanding what the reason for it might be. I assume this might be us trying to reduce the state too often? Thread 0 Crashed: 0 libswiftCore.dylib 0x00000001884fec20 _swift_release_dealloc + 48 (:-1) 1 libswiftCore.dylib 0x00000001884fd938 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 240 (:-1) 2 SwiftUICore 0x000000018ed7f940 specialized _ArrayBuffer._consumeAndCreateNew(bufferIsUnique:minimumCapacity:growForAppend:) + 244 (:-1) 3 SwiftUICore 0x000000018ed7ecdc specialized Array._createNewBuffer(bufferIsUnique:minimumCapacity:growForAppend:) + 16 (:-1) 4 SwiftUICore 0x000000018ed7ecdc specialized Array._reserveCapacityAssumingUniqueBuffer(oldCount:) + 24 (:-1) 5 SwiftUICore 0x000000018ed7ecdc specialized Array.append(_:) + 56 (:-1) 6 SwiftUICore 0x000000018ed7ecdc NSAttributedString.MetricsCache.metrics(requestedSize:layoutMargins:wantsNumberOfLineFragments:context:) + 2996 (:-1) 7 SwiftUICore 0x000000018eda7afc ResolvedStyledText.StringDrawing.draw(in:with:applyingMarginOffsets:containsResolvable:context:renderer:) + 208 (:-1) 8 SwiftUICore 0x000000018ee5fe90 ResolvedStyledText.draw(in:with:applyingMarginOffsets:context:renderer:) + 100 (:-1) 9 SwiftUICore 0x000000018ee5fe90 closure #1 in DisplayList.ViewUpdater.Platform.updateItemView(_:index:item:state:) + 176 (:-1) 10 SwiftUICore 0x000000018ee5f7a0 closure #1 in PlatformDrawableContent.draw(in:size:contentsScale:state:) + 20 (:-1) 11 SwiftUICore 0x000000018ee5f7a0 CGContextRef.withPlatformContext(do:) + 56 (:-1) 12 SwiftUICore 0x000000018ee5f7a0 PlatformDrawableContent.draw(in:size:contentsScale:state:) + 136 (:-1) 13 SwiftUICore 0x000000018ee5fd94 CGDrawingLayer.draw(in:) + 88 (:-1) 14 SwiftUICore 0x000000018ee5fd94 @objc CGDrawingLayer.draw(in:) + 224 (:-1) 15 QuartzCore 0x000000018ad2304c CABackingStoreUpdate_ + 344 (:-1) 16 QuartzCore 0x000000018ad22df0 invocation function for block in CA::Layer::display_() + 72 (:-1) 17 QuartzCore 0x000000018ad257c4 CA::Layer::display_() + 2180 (:-1) 18 QuartzCore 0x000000018ad257c4 -[CALayer _display] + 2312 (:-1) 19 QuartzCore 0x000000018ac5d0f4 CA::Layer::display_if_needed(CA::Transaction*) + 256 (:-1) 20 QuartzCore 0x000000018ac5d0f4 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 732 (:-1) 21 QuartzCore 0x000000018ac593c8 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 568 (:-1) 22 QuartzCore 0x000000018ac587c0 CA::Transaction::commit() + 428 (:-1) 23 QuartzCore 0x000000018ac59018 CA::Transaction::flush() + 84 (:-1) 24 QuartzCore 0x000000018ac585c0 CA::Transaction::flush_as_runloop_observer(bool) + 224 (:-1) 25 UIKitCore 0x000000018b111800 _UIApplicationFlushCATransaction + 52 (:-1) 26 UIKitCore 0x000000018b110e74 performCATransactionCommitAction + 308 (:-1) 27 UIKitCore 0x000000018b110e74 __setupUpdateSequence_block_invoke_2 + 336 (:-1) 28 UIKitCore 0x000000018b117b88 _UIUpdateSequenceRunNext + 128 (:-1) 29 UIKitCore 0x000000018b117928 schedulerStepScheduledMainSectionContinue + 48 (:-1) 30 UpdateCycle 0x00000002b9c47bc0 UC::DriverCore::continueProcessing() + 84 (:-1) 31 CoreFoundation 0x0000000186ea8c20 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (:-1) 32 CoreFoundation 0x0000000186ea8b94 __CFRunLoopDoSource0 + 192 (:-1) 33 CoreFoundation 0x0000000186e8a268 __CFRunLoopDoSources0 + 316 (:-1) 34 CoreFoundation 0x0000000186e89384 __CFRunLoopRun + 416 (:-1) 35 CoreFoundation 0x0000000186e8a7f4 _CFRunLoopRunSpecificWithOptions + 344 (:-1) 36 GraphicsServices 0x000000023154cf24 GSEventRunModal + 120 (:-1) 37 UIKitCore 0x000000018b1681cc UIApplicationMain + 336 (:-1) 38 UIKitCore 0x000000018b403644 UIApplicationMain(_:_:_:_:) + 104 (:-1) 39 MyApp 0x00000001021d9c08 main + 116 (/<compiler-generated>:10) 40 MyApp 0x00000001021d9c08 0x1021d4000 + 23560 (AppDelegate.swift:0) 41 dyld 0x0000000186cdb5b8 start + 6616 (:-1) Attaching one of the crashes, but I do have an access to xcrashpoint file as well. Crash: 2026-10-13_00-35-13.8383_-0400-3db78b940c1a263bd5595645b082edf503063be8.crash It seems to happen on the following version of iOS: 18.7.8 26.5 26.6.2 26.7.1 27.0 27.0.1 Would appreciate any tips!
1
0
375
1d
Live Activity (ActivityKit) always defaults to dark colorScheme on iOS 27
Environment: iOS Version: iOS 27.0 Framework: ActivityKit Xcode Version: Xcode 26.1 Issue Description: In iOS 27, Live Activities fail to adapt to the system colorScheme. Regardless of whether the system theme is set to Light or Dark mode, @Environment(.colorScheme) inside the Live Activity view always returns .dark. This behavior worked as expected on iOS 26, where the Live Activity correctly responded to light/dark mode changes and rendered the appropriate theme. Expected Behavior: The Live Activity view should respect the system's current colorScheme (returning .light when the system is in Light Mode) on iOS 27, consistent with the behavior on iOS 26. Actual Behavior: The Live Activity view strictly defaults to .dark mode on iOS 27, even when the device is explicitly set to Light Mode. struct LockScreenView<T: BaseLiveActivityAttributes>: View { let context: ActivityViewContext<T> @Environment(\.colorScheme) var colorScheme var isLight: Bool { colorScheme == .light } var body: some View { Color(isLight ? .white : .black) .overlay(alignment: .topTrailing) { VStack(alignment: .trailing, spacing: 2) { Text("colorScheme: \(String(describing: colorScheme))") } .font(.system(size: 9, weight: .bold, design: .monospaced)) .foregroundColor(.yellow) .padding(4) .background(Color.black.opacity(0.6)) .cornerRadius(4) .padding(.trailing, 8) .padding(.top, 4) } } } Any insights or workarounds would be greatly appreciated. Thanks!
1
0
1.1k
1d
Full screen
Hello. Do you know how to make full screen on SwiftUI? Not like when you tap on green circle placed top-left side in any MacOS app, now my app opens on small window but I need to this app was stretched for horizontal and vertical sides
0
0
311
1d
How can I set per-window titles in the Window menu for an iPad app running on Apple silicon Mac?
I’m developing a SwiftUI iPad app that runs on an Apple silicon Mac as an iPad app—not as a Mac Catalyst app. The app supports multiple windows, with each Live Screen window associated with a different virtual machine. In the macOS Window menu, all child windows appear as “VirtualProg” rather than showing their content or VM name. The Live Screen window is created from a data-driven  WindowGroup , roughly like this: WindowGroup(id: "vm-screen", for: String.self) { $vmName in VMScreenWindow(vmName: vmName) .navigationTitle(vmName.map { "Live ($0)" } ?? "Live Screen") } I’ve tried setting the title with  .navigationTitle  both on the  WindowGroup  content and inside the window’s  NavigationStack . I also tried setting  windowScene.title  on the specific scene obtained from  view.window?.windowScene  after the view controller appeared. Neither changed the Window-menu entries. Is there a supported way to set a per-window title for the macOS Window menu when running an iPad app on Mac? Does  UIScene.title  affect that menu, or only the window title bar and app switcher? Is this a limitation of the iPad-app-on-Mac runtime, or is there a recommended SwiftUI/UIKit lifecycle hook for this? I’m looking for a public API solution rather than private AppKit/window introspection.
2
1
410
1d
(iPhone Duo) Toolbar Items are misplaced for RTL layout-direction
On iPhone Duo, when I use .environment(\.layoutDirection, .rightToLeft) for a navigation view, the toolbar items sometimes appear on the left side instead of the right side, below the camera. This results in a view with two sides, and in some cases, it displays in an unexpected place. For example, in the code below, the button is placed behind the camera, as shown in the attachment. NavigationSplitView { Text("Sidebar") .toolbar { ToolbarItem(placement: .cancellationAction) { Button("X", systemImage: "xmark", action: {}) } } } detail: { Text("Detail") } .environment(\.layoutDirection, .rightToLeft) Is this intended behavior for RTL layout direction, a known issue, or a simulator issue?
5
1
183
2d
How to set custom height for keyboard extension without resize flicker?
Description I'm developing a custom keyboard extension using UIInputViewController and need to set a specific height of 268 points. The keyboard functions correctly, but there's a visible flicker and resize animation during launch that I cannot eliminate. The Problem When the keyboard launches, iOS provides incorrect heights before settling on the correct one. At launch, the view starts at 0×0. Around 295ms later, iOS sets the frame to 440×956 which is full screen height and wrong. Around 373ms, iOS changes it to 440×452 which is still wrong. Finally around 390ms, iOS settles at 440×268 which matches our constraint. This causes visible flicker as the view resizes three times rapidly. The keyboard appears to shrink from full screen down to the correct height, and users can clearly see this animation happening. What I've Tried I've tried adding a height constraint on self.view which gives me the correct height but causes the visible flicker. I created a custom UIInputView subclass and overrode intrinsicContentSize to return my desired height. iOS completely ignores this and gives random heights like 471pt, 680pt, or 956pt instead. I set allowsSelfSizing to true on my UIInputView subclass. iOS ignores this property. I set preferredContentSize on the view controller. iOS ignores this as well. I tried adding the constraint in viewDidAppear instead of viewDidLoad, thinking iOS might have settled by then. It still causes flicker. I overrode the frame and bounds setters on my UIInputView to clamp the height to my desired value. iOS bypasses these overrides somehow. I overrode layoutSubviews to force the correct height after the super call. iOS still applies its own height. Specific Question What is the correct API or technique to specify a keyboard extension's height that iOS will respect immediately upon launch, without triggering the resize animation sequence? Other third-party keyboards like Grammarly and SwiftKey appear to have solved this problem. Their keyboards appear at the correct height without any visible flicker. How do they achieve this? Expected Outcome The keyboard should appear at 268pt height on the first frame with no visible resize animation. Steps to Reproduce Create a new iOS App project in Xcode and add a Keyboard Extension target. In KeyboardViewController.swift, add a height constraint in viewDidLoad: override func viewDidLoad() { super.viewDidLoad() let heightConstraint = view.heightAnchor.constraint(equalToConstant: 268) heightConstraint.priority = .defaultHigh heightConstraint.isActive = true let label = UILabel() label.text = "Demo Keyboard" label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: view.centerXAnchor), label.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } Build and run on a physical device. Enable the keyboard in Settings, then General, then Keyboard, then Keyboards. Open any app with a text field and switch to the custom keyboard using the globe button. Observe the height changing from around 956pt to 452pt to 268pt with visible animation. Environment iOS 17 and iOS 18 and 26.2, Xcode 16 and Xcode 26, affects all iPhone models tested, reproducible on both simulator and physical device.
4
6
1.1k
3d
Tapping saved login does nothing after typing when .autocorrectionDisabled() or .textInputAutocapitalization(.never) modifier is applied
If a username field has .autocorrectionDisabled() or .textInputAutocapitalization(.never), tapping the saved login above the keyboard stops working once the user has typed part of their email. Nothing gets filled in. Seen on iPhone 16, iOS 27.0 (24A437). I've also reported this to Apple through Feedback Assistant (FB24825410). Steps to reproduce You need an app with Password AutoFill set up (a webcredentials associated domain) and a saved login for that domain. Tap the email field. The saved login appears above the keyboard. Type a few letters of the email. Tap the saved login. Expected: the email and password fields fill in, replacing what was typed. Actual: nothing happens. What works A username field with only .textContentType(.username) and .keyboardType(.emailAddress). Tapping the saved login fills both fields, no matter how much the user has typed. What breaks With .autocorrectionDisabled(), the tap works after 1 typed character, but does nothing after 2 or more. With .textInputAutocapitalization(.never), the tap does nothing after even 1 typed character. UIKit has the same problem: a UITextField with autocorrectionType = .no behaves like .autocorrectionDisabled(). In all of these cases, choosing the login from the key icon's Passwords sheet still fills both fields. Only the tap on the suggestion above the keyboard fails. Sample struct LoginView: View { @State private var email = "" @State private var password = "" var body: some View { VStack { TextField("Email", text: $email) .textContentType(.username) .keyboardType(.emailAddress) // Either of these breaks AutoFill once the user types: .autocorrectionDisabled() // fails after 2+ typed characters .textInputAutocapitalization(.never) // fails after 1 typed character SecureField("Password", text: $password) .textContentType(.password) } } } Workaround I removed both modifiers from our username field and it works. Typed emails still didn't get autocorrected in my testing. Has anyone found a way to keep these modifiers on a username field without breaking AutoFill? Possibly related: https://developer.apple.com/forums/thread/702970
2
0
268
3d
What would be the expectation for view invalidations and compositing cost when compositing mixed frame rate TimelineView items
In my app there are two main layers both animate. Due to performance reasons the lower of the two layers is completely opaque and animates at 12fps via a TimelineView. It has a lot of views that move per frame however everything moves slowly so that lower frame rate is fine. The second layer however has only one View and uses a Canvas rather than various SwiftUI views. It however does look better running at 60fps. It fills the screen but is mostly transparent. My hope would be that despite having the 60fps layer on top that the lower layer is still invalidating at a lower frame rate. I would furthermore hope that the compositing step would recognize it doesn't need to re-render layer one for a least half of the frames and can just composite layer two on top. Does anyone happen to know how SwiftUI and layout step and render server behave in this scenario?
1
0
64
3d
iOS 27 beta 1: .scrollEdgeEffectStyle(.soft) renders fully transparent above safeAreaBar
Feedback ID: FB23086400 On iOS 27 beta 1, .scrollEdgeEffectStyle(.soft, for: .top) on a List underneath a custom .safeAreaBar(edge: .top) no longer renders the progressive fade-blur. The top edge is fully transparent — scrolled rows pass under the bar with no visual treatment at all, as if scrollEdgeEffectDisabled() had been applied. What I've verified so far: .hard renders correctly in the exact same hierarchy; only .soft is affected. The same binary works correctly on iOS 26.x Xcode preview. I'm building with Xcode 26.3 (iOS 26 SDK). Minimal reproduction: import SwiftUI struct EdgeEffectRepro: View { enum Style: String, CaseIterable, Identifiable { case automatic, soft, hard var id: Self { self } var value: ScrollEdgeEffectStyle { switch self { case .automatic: .automatic case .soft: .soft case .hard: .hard } } } @State private var style: Style = .soft @State private var useSystemBarOnly = false var body: some View { NavigationStack { List(0..<60, id: \.self) { i in Text("Row \(i)") .frame(maxWidth: .infinity, alignment: .leading) .listRowBackground( i.isMultiple(of: 2) ? Color.orange.opacity(0.45) : Color.teal.opacity(0.45) ) } .scrollIndicators(.hidden) .scrollEdgeEffectStyle(style.value, for: .top) .safeAreaBar(edge: .top) { if !useSystemBarOnly { VStack(spacing: 8) { HStack { Text("Custom Top Bar") .font(.system(size: 28, weight: .bold)) Spacer() } HStack { Text("Second row (e.g. date range picker)") .font(.caption) .foregroundStyle(.secondary) Spacer() } } .padding(.horizontal) } } .safeAreaInset(edge: .bottom) { VStack(spacing: 8) { Picker("Edge effect style", selection: $style) { ForEach(Style.allCases) { Text($0.rawValue).tag($0) } } .pickerStyle(.segmented) Toggle("System bar only (control group)", isOn: $useSystemBarOnly) .font(.caption) } .padding() .background(.regularMaterial) } .navigationTitle("EdgeEffect Repro") .navigationBarTitleDisplayMode(.inline) } } } Steps: run on iOS 27 beta 1, set the picker to soft, scroll rows under the bar. Expected: fade-blur as on iOS 26. Actual: fully transparent. Switch to hard: renders fine.
25
14
5.2k
3d
[tvOS] ScrollView with Text does not scroll
I'm trying to do something so seemingly basic, yet I can't get it to work and I'm flummoxed. In a basic, vanilla SwiftUI app for tvOS, embed a single Text element with a very long string (hundreds of lines) in it: struct ContentView: View { var body: some View { ScrollView(.vertical) { Text(veryLargeString) .focusable() } } } Then fire up the app on tvOS, and it will not scroll. No matter what I do. Pressing arrow keys, swiping fast with my thumb, and nothing. It will not move. Ironically, in the Xcode SwiftUI Preview window—it does scroll, so that's always a fun tease. What I do know is that the focus engine is throwing a few errors, so it's leading me to believe the issue is with how I have the focusable element attached. I'm using a combination of -UIFocusLoggingEnabled YES as well as listening for UIFocusSystem.movementDidFailNotification. Unfortunately since this is SwiftUI, the notification failure and debugging logs aren't really all that actionable. Help appreciated!
4
4
1.6k
4d
Best practice Replacement for Edit Button on iPhone Duo
On the iPhone Duo the standard "Edit" button for navigation bars is a text-based button, and text-based buttons are not pushed to the vertical toolbars on the Duo. This looks sometimes silly, if it is the only remaining button that can not be placed in the vertical toolbar and which has the result that an almost empty horizontal navigation bar must be kept visible as well. So I guess it would be good idea to have an icon-based replacement for the Editor button to avoid having two toolbars on the Duo. Is there a best practice for the icon to pick for such an edit button? Would be "square.and.pencil" (SF Symbols) a good choice? Or better the "pencil .circle", because the "square" one is often used for text-based input? Anything better?
4
0
993
4d
visionOS : any non-nil preferredSurroundingsEffect enables a light-spill halo around VideoPlayerComponent video
Hi all — sharing a platform behavior I isolated while building a custom immersive video player, in case others hit it (and to ask whether it's intended). Finding On visionOS 27 (Xcode 27), an entity playing video via VideoPlayerComponent(videoRenderer:) gets a light-spill "screen glow" halo around its edges whenever the immersive space declares any non-nil preferredSurroundingsEffect. The halo follows the video's colors frame by frame. With no surroundings effect (nil), there is no halo. Quick repro Progressive immersive space + VideoPlayerComponent playing. Add .preferredSurroundingsEffect(.dim(intensity: 0.0)) → halo appears (even though the effect is visually a no-op). Remove it → halo disappears. Same with .colorMultiply(_:) and preset effects. Things I ruled out on device (Vision Pro) ImmersiveContentBrightness (.custom(1.0) vs .automatic): no difference. VideoPlayerComponent configuration (.stereo, spatial video mode, isPassthroughTintingEnabled) and component attach timing: no difference. Presenting the same video via VideoMaterial: no halo. Bright white SwiftUI content: no halo. Questions Is this coupling intended? The preferredSurroundingsEffect docs don't mention any effect on video playback. Is there a public way to keep a surroundings effect (e.g. room dimming) while avoiding the video light spill? I found none on VideoPlayerComponent. Current workaround: don't declare any surroundings effect in the modes where the halo is undesirable. Also filed via Feedback Assistant(FB25130748). Curious if anyone else has seen this or knows a switch.
0
1
1.3k
1w
iOS 27: ScrollViewProxy.scrollTo no longer reaches unrealized LazyVStack rows; how to preserve position when prepending?
I have a chat screen built with ScrollView { LazyVStack } and ScrollViewReader. Two things that worked through iOS 26 stopped working on iOS 27 (Xcode 27, iOS 27.0 simulator and device; unchanged code still works on the iOS 26.x simulator): Open at the newest message. On appear I call proxy.scrollTo(lastId, anchor: .bottom). On iOS 27 the list stops two or three rows above the bottom when the rows have variable heights (text bubbles mixed with 200pt images). Load older messages at the top without the list jumping. When the user reaches the top, I prepend 25 older messages and call proxy.scrollTo(previousTopId, anchor: .top) so the row they were reading stays put. On iOS 27 the call does nothing: the list stays at the top of the newly inserted page (offset stays at 0), which immediately re-triggers the load. Re-issuing scrollTo on every layout change for a short window (which is what made this reliable on iOS 26) has no effect on iOS 27. Minimal reproduction import SwiftUI struct Message: Identifiable, Hashable { let id: Int let height: CGFloat // simulates text vs. image bubbles } @MainActor final class ChatModel: ObservableObject { @Published var messages: [Message] = [] @Published var previousTopId: Int? // set when a page is prepended private var nextOldId = 1_000 init() { messages = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 } func loadOlder() { let top = messages.first!.id let page = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 messages.insert(contentsOf: page, at: 0) previousTopId = top // "keep this row at the top" } static func randomHeight() -> CGFloat { [44, 60, 90, 200, 260].randomElement()! } } struct ChatView: View { @StateObject private var model = ChatModel() var body: some View { ScrollViewReader { proxy in ScrollView { LazyVStack(spacing: 8) { // top sentinel: load older when it becomes visible Color.clear.frame(height: 1) .onAppear { model.loadOlder() } ForEach(model.messages) { m in RoundedRectangle(cornerRadius: 12) .fill(m.height >= 200 ? .orange.opacity(0.4) : .blue.opacity(0.3)) .frame(height: m.height) .overlay(Text("\(m.id)")) .padding(.horizontal) .id(m.id) } } } .onAppear { // (1) open at the newest message DispatchQueue.main.async { proxy.scrollTo(model.messages.last!.id, anchor: .bottom) } } .onChange(of: model.previousTopId) { _, id in // (2) restore the row that was at the top before the prepend guard let id else { return } DispatchQueue.main.async { var t = Transaction(); t.disablesAnimations = true withTransaction(t) { proxy.scrollTo(id, anchor: .top) } } } } } } Observed (1) Open at the newest message, scrollTo(lastId, anchor: .bottom) iOS 26.x: lands on the last row. iOS 27.0: stops 2–3 rows above the bottom. (2) Prepend 25 rows, then scrollTo(previousTopId, anchor: .top) iOS 26.x: the previous top row is at the top of the viewport. iOS 27.0: the offset stays at 0 and the new page's first row is at the top. What I have tried on iOS 27 Re-issuing scrollTo for 0.5s on every content-height or offset change (via GeometryReader preferences). No effect; the target row is not realized, and the visible rows are kept stable instead. .defaultScrollAnchor(.bottom) and .defaultScrollAnchor(.bottom, for: .sizeChanges): fixes the initial open, but the stored anchor is re-applied once on the first content change (the prepend), which snaps the list to the newest message. .sizeChanges did not preserve the prepend. .scrollPosition(id: $topId, anchor: .top) with .scrollTargetLayout() on the LazyVStack: the binding tracks the top row correctly, but after the prepend SwiftUI re-targets the binding to the new page's first row. Writing the previous id back, in the same update or on later run-loop turns, does not restore the position. Replacing LazyVStack with VStack fixes both cases, but the list holds hundreds of image rows and needs the lazy container for memory. What does work: reaching the backing UIScrollView (SwiftUIIntrospect), recording the visible rows' frames before the insert and adjusting contentOffset after layout. It works but is a lot of code for something that used to be one scrollTo. Questions Is it intended on iOS 27 that ScrollViewProxy.scrollTo does not scroll to a LazyVStack row that is not currently realized, or that the lazy stack keeps the currently visible rows stable in preference to the requested target? The WWDC26 lazy-stacks session describes the stack and scroll view coordinating the offset as estimates update; is scrollTo to an unrealized row now unsupported? Is there a supported SwiftUI way on iOS 27 to keep the visible rows in place when items are prepended to a LazyVStack, or to scroll reliably to an unrealized row? For example a ScrollPosition usage or an anchor role I am missing. If not, is adjusting the UIScrollView offset the expected approach, or is List now the recommended container for chat-style lists with bidirectional paging? I have filed this as FB24968838 with the sample project attached.
1
0
256
1w
NavigationSplitView, endless loop detail<->content
Hi, I already had a problem with the NavigationSplitView before, and I'm still working on it. The NavigationSplitView issue on the phone occurs when trying to navigate back from the detail view to the content view; the app automatically navigates straight back to the detail view. I reproduced the issue in a small program and identified the critical section. However, I do not know why the problem is occurring. I did the same thing again, but this time with @Query, and there was no problem. The categories, recipes and single recipe, all loaded by @Query and no side effects. Requirements for the content: A state variable used in the view, e.g., isLoading A task that loads the data from a ModelActor in a ViewModel, setting isLoading to true beforehand and back to false upon completion Subsequently, it is only possible to exit the detail view after many attempts, because the app immediately navigates back to the detail view automatically upon trying to leave it. One issue is changing the isLoading status variable from within the task. That in itself isn't the problem; the issue only arises when reading data via a ModelActor. If the status variable is changed and, for example, the list is modified manually, there is no problem. It only occurs when a ModelActor is used. I based it on the NavigationCookbook: This is the view I use for the content section (list): @State private var vm: RecipeListViewModel = .init() @State var isLoading: Bool = false if isLoading { ProgressView("Loading...") } else { List(selection: $selectedRecipe) { ForEach(vm.recipes) { recipe in NavigationLink(recipe.name, value: recipe) } } } .task { // bug, when used in UI, because of ModelActor isLoading = true await vm.list(context, categoryName: selectedCategory.name) isLoading = false } .task { // works, because manually modifying the list isLoading = true await vm.addRecipe(context, categoryName: selectedCategory.name) isLoading = false } inside ViewModel // bug func list(_ modelContext: ModelContext, categoryName: String) async { recipes.removeAll() let dh: RecipeDataHandler = .init(modelContainer: modelContext.container) let values = await dh.listByName(categoryName: categoryName) for v in values { recipes.append(.init(recipeId: v.recipeId, name: v.name)) } } // works func addRecipe(_ modelContext: ModelContext, categoryName: String) async { isLoading = true defer { isLoading = false } recipes.removeAll() recipes.append(.init(recipeId: "a", name: "Pambazo")) } ModelActor @ModelActor actor RecipeDataHandler { func listByName(categoryName: String) async -> [RecipeData] { var list: [RecipeData] = [] do { let fd = FetchDescriptor<Recipe>(predicate: #Predicate { $0.category.name == categoryName }, sortBy: [SortDescriptor(\Recipe.name)]) let data = try modelContext.fetch(fd) for item in data { let rec: RecipeData = await .init(recipeId: item.recipeId, name: item.name) list.append(rec) } } catch { print("error: \(error.localizedDescription)") } return list } } Recipe is a @Model and RecipeData just a struct. In principle, everything should run on the MainActor, but the inclusion of the ModelActor still leads to this strange behavior. Any idea what the problem might be? Christian
0
0
99
1w
Toolbar background behaviour
Say i have a scrollview: I find it’s dumb how if you have the safe area inset or any kind of element below the toolbar, the background of those will extend to the top edge of the screen (by default), yet if the scrollview scrolls to the toolbar the toolbar background just “appears”, covering the existing background— its even worse cuz the toolbar background is there by default even when you didn’t set it in code. It seems more like a bug; I feel like this is something the SwiftUI team should improve upon. How about disabling toolbar background by default? Just wanted to get some more perspectives.
0
0
324
1w
Hard scroll edge effect
The example they gave for the hard scroll edge effect is the calendar app, but Idk if this is a bad example or if i am missing something, because the calendar app theres a date picker row below the toolbar, and from my testing, whenever theres some element between the toolbar and the scrollview, the fade and blur effect does not happen -- so in fact, the hard scroll edge effect is completely unnecessary.
0
0
97
1w
NSToolbarItemViewer's min/max size is nan
When attempting to use Toolbar(id: "") this crash occurs immediately upon showing the window. FAULT: NSInternalInconsistencyException: NSToolbarItemViewer's min/max size is nan. This indicates an item's size has been changed during layout which is illegal; { NSAssertFile = "NSToolbarItemViewer.m"; NSAssertLine = 942; } If I comment out the toolbars, re-run show the window once, then re-add id: the window properly displays, allows customization and does not error unless I clear data within ~/Library/Containers.
1
0
377
1w
The sidebar toggle of the NavigationSplitView disappears when used with a .inspector modifier on iPadOS 26
If the NavigationSplitView on iPadOS 26 is combined with a .inspector column, the sidebarToggle is always hidden, when the sidebar is collapsed. If you remove the .inspector modifier, the sidebarToggle stays visible throughout the collapsed or expanded state. Has maybe someone a workaround for this issue? The problem does not exist in iOS 18. The bug is reported as FB20061260
Replies
1
Boosts
2
Views
151
Activity
8h
iWeαlth — private household finance tracker (SwiftUI, on-device data, iPhone)
iWeαlth is a household finance tracker for iPhone built in SwiftUI, with all data stored on device (GRDB/SQLite). There's no account and no backend that receives financial data. I'd especially appreciate feedback on: onboarding and first-run clarity (passcode, sample data) the expense keypad and the transaction detail screen the guided Enable Banking setup (an in-app web view that walks the user through registering their own PSD2 application), if you're in Europe and want to try it with your own accounts general UI polish on different iPhone sizes, light and dark mode Shared trips use a small relay where expense data is end-to-end encrypted with a key that only lives in the share link. TestFlight: https://testflight.apple.com/join/sQNvXPdJ Fastest path: Settings → "Load Sample Data". Thanks for any feedback!
Replies
0
Boosts
0
Views
9
Activity
12h
Binding Picker to enum value in SwiftData struct
I have a SwiftData @Model class that has a var of Media. enum Media: String, CaseIterable, Identifiable, Codable { case dvd = "DVD" case bluray = "Blu-ray" case bluray4k = "Blu-ray 4k" var id:String { self.rawValue } } In one of my views, I'm trying to show a Picker with the enum values, and then bind the @Model's field to the Picker's selection. I've been trying all kinds of things from suggesting I'm finding online. Here's a poorly pasted version of what my last attempt looks like (I'm sure I have the wrong number of closing curly brackets, but just ignore that): struct DetailView: View { @Binding var item:Item; // The @Model class. @State private var selectedMedia:Media = .dvd; var body:some View { Picker("Media", selection:$selectedMedia) { ForEach(Media.allCases, id:\.self) { media in Text(media.rawValue); } } .labelsHidden() .onAppear() { selectedMedia = item.media; } .onChange(of:selectedMedia) { oldValue, newValue in item.media = selectedMedia; } } } What's a good way to make this work? Am I going to have to use a custom formatter of some type, bind the Picker to a string, and convert the selected value with the formatter?
Replies
1
Boosts
0
Views
325
Activity
1d
Crash in ResolvedStyledText.draw
Hi! We're trying to investigate the following crash, but have a little bit hard time of understanding what the reason for it might be. I assume this might be us trying to reduce the state too often? Thread 0 Crashed: 0 libswiftCore.dylib 0x00000001884fec20 _swift_release_dealloc + 48 (:-1) 1 libswiftCore.dylib 0x00000001884fd938 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int) + 240 (:-1) 2 SwiftUICore 0x000000018ed7f940 specialized _ArrayBuffer._consumeAndCreateNew(bufferIsUnique:minimumCapacity:growForAppend:) + 244 (:-1) 3 SwiftUICore 0x000000018ed7ecdc specialized Array._createNewBuffer(bufferIsUnique:minimumCapacity:growForAppend:) + 16 (:-1) 4 SwiftUICore 0x000000018ed7ecdc specialized Array._reserveCapacityAssumingUniqueBuffer(oldCount:) + 24 (:-1) 5 SwiftUICore 0x000000018ed7ecdc specialized Array.append(_:) + 56 (:-1) 6 SwiftUICore 0x000000018ed7ecdc NSAttributedString.MetricsCache.metrics(requestedSize:layoutMargins:wantsNumberOfLineFragments:context:) + 2996 (:-1) 7 SwiftUICore 0x000000018eda7afc ResolvedStyledText.StringDrawing.draw(in:with:applyingMarginOffsets:containsResolvable:context:renderer:) + 208 (:-1) 8 SwiftUICore 0x000000018ee5fe90 ResolvedStyledText.draw(in:with:applyingMarginOffsets:context:renderer:) + 100 (:-1) 9 SwiftUICore 0x000000018ee5fe90 closure #1 in DisplayList.ViewUpdater.Platform.updateItemView(_:index:item:state:) + 176 (:-1) 10 SwiftUICore 0x000000018ee5f7a0 closure #1 in PlatformDrawableContent.draw(in:size:contentsScale:state:) + 20 (:-1) 11 SwiftUICore 0x000000018ee5f7a0 CGContextRef.withPlatformContext(do:) + 56 (:-1) 12 SwiftUICore 0x000000018ee5f7a0 PlatformDrawableContent.draw(in:size:contentsScale:state:) + 136 (:-1) 13 SwiftUICore 0x000000018ee5fd94 CGDrawingLayer.draw(in:) + 88 (:-1) 14 SwiftUICore 0x000000018ee5fd94 @objc CGDrawingLayer.draw(in:) + 224 (:-1) 15 QuartzCore 0x000000018ad2304c CABackingStoreUpdate_ + 344 (:-1) 16 QuartzCore 0x000000018ad22df0 invocation function for block in CA::Layer::display_() + 72 (:-1) 17 QuartzCore 0x000000018ad257c4 CA::Layer::display_() + 2180 (:-1) 18 QuartzCore 0x000000018ad257c4 -[CALayer _display] + 2312 (:-1) 19 QuartzCore 0x000000018ac5d0f4 CA::Layer::display_if_needed(CA::Transaction*) + 256 (:-1) 20 QuartzCore 0x000000018ac5d0f4 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 732 (:-1) 21 QuartzCore 0x000000018ac593c8 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 568 (:-1) 22 QuartzCore 0x000000018ac587c0 CA::Transaction::commit() + 428 (:-1) 23 QuartzCore 0x000000018ac59018 CA::Transaction::flush() + 84 (:-1) 24 QuartzCore 0x000000018ac585c0 CA::Transaction::flush_as_runloop_observer(bool) + 224 (:-1) 25 UIKitCore 0x000000018b111800 _UIApplicationFlushCATransaction + 52 (:-1) 26 UIKitCore 0x000000018b110e74 performCATransactionCommitAction + 308 (:-1) 27 UIKitCore 0x000000018b110e74 __setupUpdateSequence_block_invoke_2 + 336 (:-1) 28 UIKitCore 0x000000018b117b88 _UIUpdateSequenceRunNext + 128 (:-1) 29 UIKitCore 0x000000018b117928 schedulerStepScheduledMainSectionContinue + 48 (:-1) 30 UpdateCycle 0x00000002b9c47bc0 UC::DriverCore::continueProcessing() + 84 (:-1) 31 CoreFoundation 0x0000000186ea8c20 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 (:-1) 32 CoreFoundation 0x0000000186ea8b94 __CFRunLoopDoSource0 + 192 (:-1) 33 CoreFoundation 0x0000000186e8a268 __CFRunLoopDoSources0 + 316 (:-1) 34 CoreFoundation 0x0000000186e89384 __CFRunLoopRun + 416 (:-1) 35 CoreFoundation 0x0000000186e8a7f4 _CFRunLoopRunSpecificWithOptions + 344 (:-1) 36 GraphicsServices 0x000000023154cf24 GSEventRunModal + 120 (:-1) 37 UIKitCore 0x000000018b1681cc UIApplicationMain + 336 (:-1) 38 UIKitCore 0x000000018b403644 UIApplicationMain(_:_:_:_:) + 104 (:-1) 39 MyApp 0x00000001021d9c08 main + 116 (/<compiler-generated>:10) 40 MyApp 0x00000001021d9c08 0x1021d4000 + 23560 (AppDelegate.swift:0) 41 dyld 0x0000000186cdb5b8 start + 6616 (:-1) Attaching one of the crashes, but I do have an access to xcrashpoint file as well. Crash: 2026-10-13_00-35-13.8383_-0400-3db78b940c1a263bd5595645b082edf503063be8.crash It seems to happen on the following version of iOS: 18.7.8 26.5 26.6.2 26.7.1 27.0 27.0.1 Would appreciate any tips!
Replies
1
Boosts
0
Views
375
Activity
1d
Live Activity (ActivityKit) always defaults to dark colorScheme on iOS 27
Environment: iOS Version: iOS 27.0 Framework: ActivityKit Xcode Version: Xcode 26.1 Issue Description: In iOS 27, Live Activities fail to adapt to the system colorScheme. Regardless of whether the system theme is set to Light or Dark mode, @Environment(.colorScheme) inside the Live Activity view always returns .dark. This behavior worked as expected on iOS 26, where the Live Activity correctly responded to light/dark mode changes and rendered the appropriate theme. Expected Behavior: The Live Activity view should respect the system's current colorScheme (returning .light when the system is in Light Mode) on iOS 27, consistent with the behavior on iOS 26. Actual Behavior: The Live Activity view strictly defaults to .dark mode on iOS 27, even when the device is explicitly set to Light Mode. struct LockScreenView<T: BaseLiveActivityAttributes>: View { let context: ActivityViewContext<T> @Environment(\.colorScheme) var colorScheme var isLight: Bool { colorScheme == .light } var body: some View { Color(isLight ? .white : .black) .overlay(alignment: .topTrailing) { VStack(alignment: .trailing, spacing: 2) { Text("colorScheme: \(String(describing: colorScheme))") } .font(.system(size: 9, weight: .bold, design: .monospaced)) .foregroundColor(.yellow) .padding(4) .background(Color.black.opacity(0.6)) .cornerRadius(4) .padding(.trailing, 8) .padding(.top, 4) } } } Any insights or workarounds would be greatly appreciated. Thanks!
Replies
1
Boosts
0
Views
1.1k
Activity
1d
Full screen
Hello. Do you know how to make full screen on SwiftUI? Not like when you tap on green circle placed top-left side in any MacOS app, now my app opens on small window but I need to this app was stretched for horizontal and vertical sides
Replies
0
Boosts
0
Views
311
Activity
1d
How can I set per-window titles in the Window menu for an iPad app running on Apple silicon Mac?
I’m developing a SwiftUI iPad app that runs on an Apple silicon Mac as an iPad app—not as a Mac Catalyst app. The app supports multiple windows, with each Live Screen window associated with a different virtual machine. In the macOS Window menu, all child windows appear as “VirtualProg” rather than showing their content or VM name. The Live Screen window is created from a data-driven  WindowGroup , roughly like this: WindowGroup(id: "vm-screen", for: String.self) { $vmName in VMScreenWindow(vmName: vmName) .navigationTitle(vmName.map { "Live ($0)" } ?? "Live Screen") } I’ve tried setting the title with  .navigationTitle  both on the  WindowGroup  content and inside the window’s  NavigationStack . I also tried setting  windowScene.title  on the specific scene obtained from  view.window?.windowScene  after the view controller appeared. Neither changed the Window-menu entries. Is there a supported way to set a per-window title for the macOS Window menu when running an iPad app on Mac? Does  UIScene.title  affect that menu, or only the window title bar and app switcher? Is this a limitation of the iPad-app-on-Mac runtime, or is there a recommended SwiftUI/UIKit lifecycle hook for this? I’m looking for a public API solution rather than private AppKit/window introspection.
Replies
2
Boosts
1
Views
410
Activity
1d
(iPhone Duo) Toolbar Items are misplaced for RTL layout-direction
On iPhone Duo, when I use .environment(\.layoutDirection, .rightToLeft) for a navigation view, the toolbar items sometimes appear on the left side instead of the right side, below the camera. This results in a view with two sides, and in some cases, it displays in an unexpected place. For example, in the code below, the button is placed behind the camera, as shown in the attachment. NavigationSplitView { Text("Sidebar") .toolbar { ToolbarItem(placement: .cancellationAction) { Button("X", systemImage: "xmark", action: {}) } } } detail: { Text("Detail") } .environment(\.layoutDirection, .rightToLeft) Is this intended behavior for RTL layout direction, a known issue, or a simulator issue?
Replies
5
Boosts
1
Views
183
Activity
2d
How to set custom height for keyboard extension without resize flicker?
Description I'm developing a custom keyboard extension using UIInputViewController and need to set a specific height of 268 points. The keyboard functions correctly, but there's a visible flicker and resize animation during launch that I cannot eliminate. The Problem When the keyboard launches, iOS provides incorrect heights before settling on the correct one. At launch, the view starts at 0×0. Around 295ms later, iOS sets the frame to 440×956 which is full screen height and wrong. Around 373ms, iOS changes it to 440×452 which is still wrong. Finally around 390ms, iOS settles at 440×268 which matches our constraint. This causes visible flicker as the view resizes three times rapidly. The keyboard appears to shrink from full screen down to the correct height, and users can clearly see this animation happening. What I've Tried I've tried adding a height constraint on self.view which gives me the correct height but causes the visible flicker. I created a custom UIInputView subclass and overrode intrinsicContentSize to return my desired height. iOS completely ignores this and gives random heights like 471pt, 680pt, or 956pt instead. I set allowsSelfSizing to true on my UIInputView subclass. iOS ignores this property. I set preferredContentSize on the view controller. iOS ignores this as well. I tried adding the constraint in viewDidAppear instead of viewDidLoad, thinking iOS might have settled by then. It still causes flicker. I overrode the frame and bounds setters on my UIInputView to clamp the height to my desired value. iOS bypasses these overrides somehow. I overrode layoutSubviews to force the correct height after the super call. iOS still applies its own height. Specific Question What is the correct API or technique to specify a keyboard extension's height that iOS will respect immediately upon launch, without triggering the resize animation sequence? Other third-party keyboards like Grammarly and SwiftKey appear to have solved this problem. Their keyboards appear at the correct height without any visible flicker. How do they achieve this? Expected Outcome The keyboard should appear at 268pt height on the first frame with no visible resize animation. Steps to Reproduce Create a new iOS App project in Xcode and add a Keyboard Extension target. In KeyboardViewController.swift, add a height constraint in viewDidLoad: override func viewDidLoad() { super.viewDidLoad() let heightConstraint = view.heightAnchor.constraint(equalToConstant: 268) heightConstraint.priority = .defaultHigh heightConstraint.isActive = true let label = UILabel() label.text = "Demo Keyboard" label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: view.centerXAnchor), label.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } Build and run on a physical device. Enable the keyboard in Settings, then General, then Keyboard, then Keyboards. Open any app with a text field and switch to the custom keyboard using the globe button. Observe the height changing from around 956pt to 452pt to 268pt with visible animation. Environment iOS 17 and iOS 18 and 26.2, Xcode 16 and Xcode 26, affects all iPhone models tested, reproducible on both simulator and physical device.
Replies
4
Boosts
6
Views
1.1k
Activity
3d
Tapping saved login does nothing after typing when .autocorrectionDisabled() or .textInputAutocapitalization(.never) modifier is applied
If a username field has .autocorrectionDisabled() or .textInputAutocapitalization(.never), tapping the saved login above the keyboard stops working once the user has typed part of their email. Nothing gets filled in. Seen on iPhone 16, iOS 27.0 (24A437). I've also reported this to Apple through Feedback Assistant (FB24825410). Steps to reproduce You need an app with Password AutoFill set up (a webcredentials associated domain) and a saved login for that domain. Tap the email field. The saved login appears above the keyboard. Type a few letters of the email. Tap the saved login. Expected: the email and password fields fill in, replacing what was typed. Actual: nothing happens. What works A username field with only .textContentType(.username) and .keyboardType(.emailAddress). Tapping the saved login fills both fields, no matter how much the user has typed. What breaks With .autocorrectionDisabled(), the tap works after 1 typed character, but does nothing after 2 or more. With .textInputAutocapitalization(.never), the tap does nothing after even 1 typed character. UIKit has the same problem: a UITextField with autocorrectionType = .no behaves like .autocorrectionDisabled(). In all of these cases, choosing the login from the key icon's Passwords sheet still fills both fields. Only the tap on the suggestion above the keyboard fails. Sample struct LoginView: View { @State private var email = "" @State private var password = "" var body: some View { VStack { TextField("Email", text: $email) .textContentType(.username) .keyboardType(.emailAddress) // Either of these breaks AutoFill once the user types: .autocorrectionDisabled() // fails after 2+ typed characters .textInputAutocapitalization(.never) // fails after 1 typed character SecureField("Password", text: $password) .textContentType(.password) } } } Workaround I removed both modifiers from our username field and it works. Typed emails still didn't get autocorrected in my testing. Has anyone found a way to keep these modifiers on a username field without breaking AutoFill? Possibly related: https://developer.apple.com/forums/thread/702970
Replies
2
Boosts
0
Views
268
Activity
3d
What would be the expectation for view invalidations and compositing cost when compositing mixed frame rate TimelineView items
In my app there are two main layers both animate. Due to performance reasons the lower of the two layers is completely opaque and animates at 12fps via a TimelineView. It has a lot of views that move per frame however everything moves slowly so that lower frame rate is fine. The second layer however has only one View and uses a Canvas rather than various SwiftUI views. It however does look better running at 60fps. It fills the screen but is mostly transparent. My hope would be that despite having the 60fps layer on top that the lower layer is still invalidating at a lower frame rate. I would furthermore hope that the compositing step would recognize it doesn't need to re-render layer one for a least half of the frames and can just composite layer two on top. Does anyone happen to know how SwiftUI and layout step and render server behave in this scenario?
Replies
1
Boosts
0
Views
64
Activity
3d
iOS 27 beta 1: .scrollEdgeEffectStyle(.soft) renders fully transparent above safeAreaBar
Feedback ID: FB23086400 On iOS 27 beta 1, .scrollEdgeEffectStyle(.soft, for: .top) on a List underneath a custom .safeAreaBar(edge: .top) no longer renders the progressive fade-blur. The top edge is fully transparent — scrolled rows pass under the bar with no visual treatment at all, as if scrollEdgeEffectDisabled() had been applied. What I've verified so far: .hard renders correctly in the exact same hierarchy; only .soft is affected. The same binary works correctly on iOS 26.x Xcode preview. I'm building with Xcode 26.3 (iOS 26 SDK). Minimal reproduction: import SwiftUI struct EdgeEffectRepro: View { enum Style: String, CaseIterable, Identifiable { case automatic, soft, hard var id: Self { self } var value: ScrollEdgeEffectStyle { switch self { case .automatic: .automatic case .soft: .soft case .hard: .hard } } } @State private var style: Style = .soft @State private var useSystemBarOnly = false var body: some View { NavigationStack { List(0..<60, id: \.self) { i in Text("Row \(i)") .frame(maxWidth: .infinity, alignment: .leading) .listRowBackground( i.isMultiple(of: 2) ? Color.orange.opacity(0.45) : Color.teal.opacity(0.45) ) } .scrollIndicators(.hidden) .scrollEdgeEffectStyle(style.value, for: .top) .safeAreaBar(edge: .top) { if !useSystemBarOnly { VStack(spacing: 8) { HStack { Text("Custom Top Bar") .font(.system(size: 28, weight: .bold)) Spacer() } HStack { Text("Second row (e.g. date range picker)") .font(.caption) .foregroundStyle(.secondary) Spacer() } } .padding(.horizontal) } } .safeAreaInset(edge: .bottom) { VStack(spacing: 8) { Picker("Edge effect style", selection: $style) { ForEach(Style.allCases) { Text($0.rawValue).tag($0) } } .pickerStyle(.segmented) Toggle("System bar only (control group)", isOn: $useSystemBarOnly) .font(.caption) } .padding() .background(.regularMaterial) } .navigationTitle("EdgeEffect Repro") .navigationBarTitleDisplayMode(.inline) } } } Steps: run on iOS 27 beta 1, set the picker to soft, scroll rows under the bar. Expected: fade-blur as on iOS 26. Actual: fully transparent. Switch to hard: renders fine.
Replies
25
Boosts
14
Views
5.2k
Activity
3d
[tvOS] ScrollView with Text does not scroll
I'm trying to do something so seemingly basic, yet I can't get it to work and I'm flummoxed. In a basic, vanilla SwiftUI app for tvOS, embed a single Text element with a very long string (hundreds of lines) in it: struct ContentView: View { var body: some View { ScrollView(.vertical) { Text(veryLargeString) .focusable() } } } Then fire up the app on tvOS, and it will not scroll. No matter what I do. Pressing arrow keys, swiping fast with my thumb, and nothing. It will not move. Ironically, in the Xcode SwiftUI Preview window—it does scroll, so that's always a fun tease. What I do know is that the focus engine is throwing a few errors, so it's leading me to believe the issue is with how I have the focusable element attached. I'm using a combination of -UIFocusLoggingEnabled YES as well as listening for UIFocusSystem.movementDidFailNotification. Unfortunately since this is SwiftUI, the notification failure and debugging logs aren't really all that actionable. Help appreciated!
Replies
4
Boosts
4
Views
1.6k
Activity
4d
Best practice Replacement for Edit Button on iPhone Duo
On the iPhone Duo the standard "Edit" button for navigation bars is a text-based button, and text-based buttons are not pushed to the vertical toolbars on the Duo. This looks sometimes silly, if it is the only remaining button that can not be placed in the vertical toolbar and which has the result that an almost empty horizontal navigation bar must be kept visible as well. So I guess it would be good idea to have an icon-based replacement for the Editor button to avoid having two toolbars on the Duo. Is there a best practice for the icon to pick for such an edit button? Would be "square.and.pencil" (SF Symbols) a good choice? Or better the "pencil .circle", because the "square" one is often used for text-based input? Anything better?
Replies
4
Boosts
0
Views
993
Activity
4d
visionOS : any non-nil preferredSurroundingsEffect enables a light-spill halo around VideoPlayerComponent video
Hi all — sharing a platform behavior I isolated while building a custom immersive video player, in case others hit it (and to ask whether it's intended). Finding On visionOS 27 (Xcode 27), an entity playing video via VideoPlayerComponent(videoRenderer:) gets a light-spill "screen glow" halo around its edges whenever the immersive space declares any non-nil preferredSurroundingsEffect. The halo follows the video's colors frame by frame. With no surroundings effect (nil), there is no halo. Quick repro Progressive immersive space + VideoPlayerComponent playing. Add .preferredSurroundingsEffect(.dim(intensity: 0.0)) → halo appears (even though the effect is visually a no-op). Remove it → halo disappears. Same with .colorMultiply(_:) and preset effects. Things I ruled out on device (Vision Pro) ImmersiveContentBrightness (.custom(1.0) vs .automatic): no difference. VideoPlayerComponent configuration (.stereo, spatial video mode, isPassthroughTintingEnabled) and component attach timing: no difference. Presenting the same video via VideoMaterial: no halo. Bright white SwiftUI content: no halo. Questions Is this coupling intended? The preferredSurroundingsEffect docs don't mention any effect on video playback. Is there a public way to keep a surroundings effect (e.g. room dimming) while avoiding the video light spill? I found none on VideoPlayerComponent. Current workaround: don't declare any surroundings effect in the modes where the halo is undesirable. Also filed via Feedback Assistant(FB25130748). Curious if anyone else has seen this or knows a switch.
Replies
0
Boosts
1
Views
1.3k
Activity
1w
iOS 27: ScrollViewProxy.scrollTo no longer reaches unrealized LazyVStack rows; how to preserve position when prepending?
I have a chat screen built with ScrollView { LazyVStack } and ScrollViewReader. Two things that worked through iOS 26 stopped working on iOS 27 (Xcode 27, iOS 27.0 simulator and device; unchanged code still works on the iOS 26.x simulator): Open at the newest message. On appear I call proxy.scrollTo(lastId, anchor: .bottom). On iOS 27 the list stops two or three rows above the bottom when the rows have variable heights (text bubbles mixed with 200pt images). Load older messages at the top without the list jumping. When the user reaches the top, I prepend 25 older messages and call proxy.scrollTo(previousTopId, anchor: .top) so the row they were reading stays put. On iOS 27 the call does nothing: the list stays at the top of the newly inserted page (offset stays at 0), which immediately re-triggers the load. Re-issuing scrollTo on every layout change for a short window (which is what made this reliable on iOS 26) has no effect on iOS 27. Minimal reproduction import SwiftUI struct Message: Identifiable, Hashable { let id: Int let height: CGFloat // simulates text vs. image bubbles } @MainActor final class ChatModel: ObservableObject { @Published var messages: [Message] = [] @Published var previousTopId: Int? // set when a page is prepended private var nextOldId = 1_000 init() { messages = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 } func loadOlder() { let top = messages.first!.id let page = (0..<25).map { Message(id: nextOldId - $0, height: Self.randomHeight()) }.reversed() nextOldId -= 25 messages.insert(contentsOf: page, at: 0) previousTopId = top // "keep this row at the top" } static func randomHeight() -> CGFloat { [44, 60, 90, 200, 260].randomElement()! } } struct ChatView: View { @StateObject private var model = ChatModel() var body: some View { ScrollViewReader { proxy in ScrollView { LazyVStack(spacing: 8) { // top sentinel: load older when it becomes visible Color.clear.frame(height: 1) .onAppear { model.loadOlder() } ForEach(model.messages) { m in RoundedRectangle(cornerRadius: 12) .fill(m.height >= 200 ? .orange.opacity(0.4) : .blue.opacity(0.3)) .frame(height: m.height) .overlay(Text("\(m.id)")) .padding(.horizontal) .id(m.id) } } } .onAppear { // (1) open at the newest message DispatchQueue.main.async { proxy.scrollTo(model.messages.last!.id, anchor: .bottom) } } .onChange(of: model.previousTopId) { _, id in // (2) restore the row that was at the top before the prepend guard let id else { return } DispatchQueue.main.async { var t = Transaction(); t.disablesAnimations = true withTransaction(t) { proxy.scrollTo(id, anchor: .top) } } } } } } Observed (1) Open at the newest message, scrollTo(lastId, anchor: .bottom) iOS 26.x: lands on the last row. iOS 27.0: stops 2–3 rows above the bottom. (2) Prepend 25 rows, then scrollTo(previousTopId, anchor: .top) iOS 26.x: the previous top row is at the top of the viewport. iOS 27.0: the offset stays at 0 and the new page's first row is at the top. What I have tried on iOS 27 Re-issuing scrollTo for 0.5s on every content-height or offset change (via GeometryReader preferences). No effect; the target row is not realized, and the visible rows are kept stable instead. .defaultScrollAnchor(.bottom) and .defaultScrollAnchor(.bottom, for: .sizeChanges): fixes the initial open, but the stored anchor is re-applied once on the first content change (the prepend), which snaps the list to the newest message. .sizeChanges did not preserve the prepend. .scrollPosition(id: $topId, anchor: .top) with .scrollTargetLayout() on the LazyVStack: the binding tracks the top row correctly, but after the prepend SwiftUI re-targets the binding to the new page's first row. Writing the previous id back, in the same update or on later run-loop turns, does not restore the position. Replacing LazyVStack with VStack fixes both cases, but the list holds hundreds of image rows and needs the lazy container for memory. What does work: reaching the backing UIScrollView (SwiftUIIntrospect), recording the visible rows' frames before the insert and adjusting contentOffset after layout. It works but is a lot of code for something that used to be one scrollTo. Questions Is it intended on iOS 27 that ScrollViewProxy.scrollTo does not scroll to a LazyVStack row that is not currently realized, or that the lazy stack keeps the currently visible rows stable in preference to the requested target? The WWDC26 lazy-stacks session describes the stack and scroll view coordinating the offset as estimates update; is scrollTo to an unrealized row now unsupported? Is there a supported SwiftUI way on iOS 27 to keep the visible rows in place when items are prepended to a LazyVStack, or to scroll reliably to an unrealized row? For example a ScrollPosition usage or an anchor role I am missing. If not, is adjusting the UIScrollView offset the expected approach, or is List now the recommended container for chat-style lists with bidirectional paging? I have filed this as FB24968838 with the sample project attached.
Replies
1
Boosts
0
Views
256
Activity
1w
NavigationSplitView, endless loop detail<->content
Hi, I already had a problem with the NavigationSplitView before, and I'm still working on it. The NavigationSplitView issue on the phone occurs when trying to navigate back from the detail view to the content view; the app automatically navigates straight back to the detail view. I reproduced the issue in a small program and identified the critical section. However, I do not know why the problem is occurring. I did the same thing again, but this time with @Query, and there was no problem. The categories, recipes and single recipe, all loaded by @Query and no side effects. Requirements for the content: A state variable used in the view, e.g., isLoading A task that loads the data from a ModelActor in a ViewModel, setting isLoading to true beforehand and back to false upon completion Subsequently, it is only possible to exit the detail view after many attempts, because the app immediately navigates back to the detail view automatically upon trying to leave it. One issue is changing the isLoading status variable from within the task. That in itself isn't the problem; the issue only arises when reading data via a ModelActor. If the status variable is changed and, for example, the list is modified manually, there is no problem. It only occurs when a ModelActor is used. I based it on the NavigationCookbook: This is the view I use for the content section (list): @State private var vm: RecipeListViewModel = .init() @State var isLoading: Bool = false if isLoading { ProgressView("Loading...") } else { List(selection: $selectedRecipe) { ForEach(vm.recipes) { recipe in NavigationLink(recipe.name, value: recipe) } } } .task { // bug, when used in UI, because of ModelActor isLoading = true await vm.list(context, categoryName: selectedCategory.name) isLoading = false } .task { // works, because manually modifying the list isLoading = true await vm.addRecipe(context, categoryName: selectedCategory.name) isLoading = false } inside ViewModel // bug func list(_ modelContext: ModelContext, categoryName: String) async { recipes.removeAll() let dh: RecipeDataHandler = .init(modelContainer: modelContext.container) let values = await dh.listByName(categoryName: categoryName) for v in values { recipes.append(.init(recipeId: v.recipeId, name: v.name)) } } // works func addRecipe(_ modelContext: ModelContext, categoryName: String) async { isLoading = true defer { isLoading = false } recipes.removeAll() recipes.append(.init(recipeId: "a", name: "Pambazo")) } ModelActor @ModelActor actor RecipeDataHandler { func listByName(categoryName: String) async -> [RecipeData] { var list: [RecipeData] = [] do { let fd = FetchDescriptor<Recipe>(predicate: #Predicate { $0.category.name == categoryName }, sortBy: [SortDescriptor(\Recipe.name)]) let data = try modelContext.fetch(fd) for item in data { let rec: RecipeData = await .init(recipeId: item.recipeId, name: item.name) list.append(rec) } } catch { print("error: \(error.localizedDescription)") } return list } } Recipe is a @Model and RecipeData just a struct. In principle, everything should run on the MainActor, but the inclusion of the ModelActor still leads to this strange behavior. Any idea what the problem might be? Christian
Replies
0
Boosts
0
Views
99
Activity
1w
Toolbar background behaviour
Say i have a scrollview: I find it’s dumb how if you have the safe area inset or any kind of element below the toolbar, the background of those will extend to the top edge of the screen (by default), yet if the scrollview scrolls to the toolbar the toolbar background just “appears”, covering the existing background— its even worse cuz the toolbar background is there by default even when you didn’t set it in code. It seems more like a bug; I feel like this is something the SwiftUI team should improve upon. How about disabling toolbar background by default? Just wanted to get some more perspectives.
Replies
0
Boosts
0
Views
324
Activity
1w
Hard scroll edge effect
The example they gave for the hard scroll edge effect is the calendar app, but Idk if this is a bad example or if i am missing something, because the calendar app theres a date picker row below the toolbar, and from my testing, whenever theres some element between the toolbar and the scrollview, the fade and blur effect does not happen -- so in fact, the hard scroll edge effect is completely unnecessary.
Replies
0
Boosts
0
Views
97
Activity
1w
NSToolbarItemViewer's min/max size is nan
When attempting to use Toolbar(id: "") this crash occurs immediately upon showing the window. FAULT: NSInternalInconsistencyException: NSToolbarItemViewer's min/max size is nan. This indicates an item's size has been changed during layout which is illegal; { NSAssertFile = "NSToolbarItemViewer.m"; NSAssertLine = 942; } If I comment out the toolbars, re-run show the window once, then re-add id: the window properly displays, allows customization and does not error unless I clear data within ~/Library/Containers.
Replies
1
Boosts
0
Views
377
Activity
1w