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

SwiftUI Documentation

Posts under SwiftUI subtopic

Post

Replies

Boosts

Views

Activity

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.
6
8
735
1h
Xcode 26.3 Simulator renders SwiftUI app only inside a rounded rectangle instead of full screen
Hi everyone, I’m seeing a strange rendering issue in Xcode 26.3 that seems to affect only the iOS Simulator. Environment: Xcode 26.3 SwiftUI app Reproduces in Simulator only Reproduces across multiple simulator device models My code is just a minimal example Expected behavior: The view should fill the entire screen. Actual behavior: The app content is rendered only inside a centered rounded rectangle/card-like area, with black space around it, as if the app canvas is being clipped incorrectly. Minimal reproduction: import SwiftUI @main struct LayoutShowcaseApp: App { var body: some Scene { WindowGroup { Color.green.ignoresSafeArea() } } } I also tried wrapping it in a ZStack and using: .frame(maxWidth: .infinity, maxHeight: .infinity) .background(...) .ignoresSafeArea() but the result is the same. What I already tried: Clean Build Folder Switching simulator device models Resetting simulator content/settings Rebuilding from a fresh minimal SwiftUI project Since this happens with such a minimal example, it looks more like a Simulator/runtime rendering bug than a SwiftUI layout issue. Has anyone else seen this on Xcode 26.3? If yes, did you find any workaround? Thanks.
Topic: UI Frameworks SubTopic: SwiftUI
1
0
334
2h
SwiftUI: List cells flicker during scroll when .searchPresentationToolbarBehavior(.avoidHidingContent) is used with searchable modifier inside a sheet (iOS 26)
Description When a searchable List is presented inside a sheet and uses .searchPresentationToolbarBehavior(.avoidHidingContent) to keep the navigation bar visible during search, the list cells flicker/blink under the keyboard This reproduces with public SwiftUI API only — no UIKit, no appearance-proxy customization. Steps to Reproduce Create a new iOS App (SwiftUI) project (deployment target iOS 17.1+). Replace the generated App file with the sample code below. Run on iOS 26 (reproduces on both device and Simulator). Tap "Open transfer methods" to present the sheet. Tap the search field so the keyboard is shown (search becomes active). Scroll the list down, then up into the top bounce (overscroll) Observe the cells under the keyboard. Expected Cells scroll smoothly under the keyboard; no flicker. Actual Cells under the keyboard flicker/blink during top overscroll. See the attachments with shots of the process: 1. screen before a blink, 2. Screen in the moment of blinking Sample Code import SwiftUI @main struct FlickerReproApp: App { var body: some Scene { WindowGroup { RootView() } } } struct RootView: View { @State private var isSheetPresented = false var body: some View { Button("Open transfer methods") { isSheetPresented = true } .sheet(isPresented: $isSheetPresented) { NavigationStack { SimpleSearchScreen() } } } } struct SimpleSearchScreen: View { @State private var searchText = "" private let items = (0..<40).map { "Recipient \($0)" } private var filteredItems: [String] { searchText.isEmpty ? items : items.filter { $0.localizedCaseInsensitiveContains(searchText) } } var body: some View { List { ForEach(filteredItems, id: \.self) { item in Text(item) } } .listStyle(.plain) .navigationTitle("Transfer methods") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Close", systemImage: "xmark") {} } } .searchable(text: $searchText, placement: .automatic, prompt: "Search") // Remove the line below -> flicker disappears, but the navigation bar // (title + toolbar items) then hides during search, which we need to keep. .searchPresentationToolbarBehavior(.avoidHidingContent) } } Notes / What was ruled out Removing .searchPresentationToolbarBehavior(.avoidHidingContent) eliminates the flicker — but then the navigation bar hides during search, which is exactly the behavior the modifier is meant to prevent. Presenting the same screen NOT inside a sheet does not flicker — the sheet presentation is required to reproduce. Independent of app-level customization: reproduces with no UINavigationBar/UITabBar/UISearchBar appearance proxies and no UIKit. Also tried, did NOT help: .scrollEdgeEffectStyle(.hard, for: .all), .toolbarBackground(.hidden, for: .navigationBar), keeping the bar visible via UISearchController.hidesNavigationBarDuringPresentation = false instead, .geometryGroup() on rows, .scrollDismissesKeyboard(.never), removing safe area insets.
0
0
15
4h
Siri Intent Dialog with custom SwiftUIView not responding to buttons with intent
I have created an AppIntent and added it to shortcuts to be able to read by Siri. When I say the phrase, the Siri intent dialog appears just fine. I have added a custom SwiftUI View inside Siri dialog box with 2 buttons with intents. The callback or handling of those buttons is not working when initiated via Siri. It works fine when I initiate it in shortcuts. I tried using the UIButton without the intent action as well but it did not work. Here is the code. static let title: LocalizedStringResource = "My Custom Intent" static var openAppWhenRun: Bool = false @MainActor func perform() async throws -> some ShowsSnippetView & ProvidesDialog { return .result(dialog: "Here are the details of your order"), content: { OrderDetailsView() } } struct OrderDetailsView { var body: some View { HStack { if #available(iOS 17.0, *) { Button(intent: ModifyOrderIntent(), label : { Text("Modify Order") }) Button(intent: CancelOrderIntent(), label : { Text("Cancel Order") }) } } } } struct ModifyOrderIntent: AppIntent { static let title: LocalizedStringResource = "Modify Order" static var openAppWhenRun: Bool = true @MainActor func perform() async throws -> some OpensIntent { // performs the deeplinking to app to a certain page to modify the order } } struct CancelOrderIntent: AppIntent { static let title: LocalizedStringResource = "Cancel Order" static var openAppWhenRun: Bool = true @MainActor func perform() async throws -> some OpensIntent { // performs the deeplinking to app to a certain page to cancel the order } } Button(action: { if let url = URL(string: "myap://open-order") { UIApplication.shared.open(url) } }
1
2
429
4h
WindowGroup Tab Bar overlaps inspector column
I've been trying to replicate an app layout similar to Xcode where we have a tab bar in the canvas for different files that are open, while also having an inspector view. I have come across this problem where the tab bar from the WindowGroup goes into the inspector on the right. This happens because the inspector is apparently owned by the Window. Interestingly, this doesn't happen for the sidebar. I don't see why it's not possible for it to not cut into inspector space either. Here's my code for ContentView where the inspector is declared: var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { FitsSidebarView(currentFitID: fit.id) .navigationSplitViewColumnWidth(min: 180, ideal: 240, max: 360) } detail: { FittingCanvasView(fit: fit, session: session) } .inspector(isPresented: $isInspectorPresented) { InspectorView() .inspectorColumnWidth(min: 240, ideal: 280, max: 400) } And here's the code for my App Entry: struct KiwiFittingApp: App { var body: some Scene { WindowGroup( "Fit", id: "fit-window", for: FitRecord.ID.self ) { fitID in FitWindowScene(fitID: fitID.wrappedValue) } defaultValue: { FitCatalog.defaultFit.id } .defaultSize(width: 1280, height: 800) .commands { SidebarCommands() InspectorCommands() } } } Does anyone have any clue how to make it work with native components?
Topic: UI Frameworks SubTopic: SwiftUI
0
0
11
4h
visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
This was confirmed as a framework gap in an accepted answer from an Apple Vision Pro engineer in June 2024 (https://developer.apple.com/forums/thread/758014?answerId=792769022#792769022): .background fires identically whether the user taps a window's close button or the system backgrounds a window that's been out of the field of view for ~61 seconds, and there's no app-visible signal that distinguishes the two. The recommendation at the time was to use a gesture/affordance to reopen the window, and to file an enhancement request. Two years on, with the window-management APIs that have shipped since, I want to confirm whether the situation has changed as of visionOS 26. My case: VisionBlazer, a native spatial 3D creation tool (TestFlight beta, August 2026 launch). Users routinely work with several SwiftUI WindowGroup tool windows open at once — drawing tools, materials, timeline, properties, lighting — parked spatially around an ImmersiveSpace. Parking a window behind or beside you is core to the workflow. The app should terminate when the user closes the primary window, but must not terminate when a secondary window (or the primary) is simply parked out of view. What I've verified on-device (visionOS 26.5): scenePhase == .background fires identically for a user close tap and for the ~61s out-of-FoV backgrounding (SurfBoard: "…is out of FOV after 60.99 seconds. Backgrounding"). The phase sequence (active → inactive → background) and timing (~0.2–0.4s gap) are indistinguishable between the two cases. scenePhase on visionOS reflects visibility, not focus — a parked window stays .active while the user edits elsewhere, until the out-of-FoV timer fires. The session identifier reachable from the window's view hierarchy (view.window!.windowScene.session.persistentIdentifier) never matches the identifier reported by application(_:didDiscardSceneSessions:) or UIScene.didDisconnectNotification for that same close. The view-visible session stays in UIApplication.shared.openSessions indefinitely after the close. Those disconnect/discard callbacks arrive ~10–15s late and only ever carry foreign session identifiers, so they can't be attributed to a specific window. Stale-session discards from prior launches pollute the signal further. onDisappear does not fire on user close. Five strategies tried, all failed: (1) scene-object identity captured at didMoveToWindow; (2) session.persistentIdentifier matching against openSessions; (3) live re-capture of scene/session from the view hierarchy on every lifecycle change; (4) temporal correlation of didEnterBackground/didDisconnect; (5) a focus-recency heuristic on scenePhase transitions. All fail on the identifier mismatch and the visibility-not-focus semantics above. Questions: As of visionOS 26, is there now any supported way to detect that the user intentionally closed a specific window, distinct from system backgrounding? Is there a SwiftUI or scene-delegate callback tied to a window's own scene that fires only on user close? Is there a dismissalReason (or equivalent) anywhere on the close path? If none of the above exists, is an explicit in-app Quit button still the intended pattern for "quit when the main window is closed"? I have a focused test project reproducing all of this and can link it. Thanks.
3
0
485
21h
Is NavigationSplitView on macOS 27 broken?
On macOS 27 Beta 2, a simple NavigationSplitView example exhibits bizarre behaviour when the window is resized. The sidebar seemingly expands and collapses at random as the window is resized. The symptoms can be exacerbated with toolbar items. The sidebar appears to behave correctly when an inspector view is not present. Copy paste the code below into a new Xcode 27 project and run on macOS 27 and then resize the window: File -> New -> Project... -> App import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { NavigationSplitView { Text("Sidebar") } detail: { Text("Content") } .inspector(isPresented: .constant(true)) { Text("Inspector") } } } Adding .frame or .inspectorColumnWidth to any of the Text views does not appear to fix the issues. macOS: 27.0 Beta (26A5368g) Xcode: 27.0 beta 2 (27A5209h)
Topic: UI Frameworks SubTopic: SwiftUI Tags:
3
1
247
1d
Unexpected behavior in the interaction between LazyVStack and GeometryReader
Hello! I'd like to share a problem and its potential solution. Steps to reproduce: The issue can be reproduced with the following minimal example: struct TestConditionalScrollView: View { var body: some View { ConditionalScrollView { LazyVStack(spacing: 16) { Text("Text 1") .frame(height: 20) Text("Text 2") .frame(height: 30) Text("Text 3") .frame(height: 40) Text("Text 4") .frame(height: 500) Text("Text 5") .frame(height: 400) } .padding() } } } struct ConditionalScrollView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } @State private var contentHeight: CGFloat = 0 var body: some View { GeometryReader { geo in _ = print("Height: \(contentHeight)") return Group { if contentHeight > geo.size.height { ScrollView { measuredContent } } else { measuredContent } } } } private var measuredContent: some View { content .background( GeometryReader { geo in Color.clear .preference( key: ContentHeightKey.self, value: geo.size.height ) } ) .onPreferenceChange(ContentHeightKey.self) { contentHeight = $0 } } } struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } } If we hit the breakpoint on the following line: _ = print("Height: (contentHeight)") the output looks like this: Problems observed Different values are reported, and it is unclear where those values originate from. The selected execution branch appears to change multiple times during the layout process. Possible reason As Rens Breur mentioned in the WWDC26 session "Dive into lazy stacks and scrolling with SwiftUI", LazyVStack relies on estimated layout information during certain phases of the layout process. I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. To do that, let me show how the value reaches the GeometryReader closure: Step 1 In AttributeGraph, the GeometryReader<...> node and the LazyVStack node appear to be connected as shown below: Step 2 When the LazyVStack node is updated, the layout process appears to follow roughly this logic: `SwiftUICore 'SwiftUI.ForEachState.forEachItem:` n = number of cells to evaluate (initially n == 2) For first n cells: SwiftUICore`SwiftUI.ViewLayoutEngine.sizeThatFits(...) SwiftUI.EstimationCache.add(...) The total size of the lazy stack is then estimated: SwiftUI.LazyStack<...>.sizeThatFits(...): averageCellInfo = EstimationCache.average averageCellInfo.height = (firstCellHeight + secondCellHeight) / 2 totalHeight = firstCellHeight + secondCellHeight + averageCellInfo.height * remainingCells For the sample project, this produces an estimated height of 189. This value then appears to be cached inside a LazyLayoutComputer node. Step 3 When GeometryReader is updated and its closure executes, geo.size.height appears to be resolved from the cached value stored by LazyLayoutComputer. As a result, the reported height is: 189 + 32 (padding) = 221 Step 4 My assumption is that LazyVStack subsequently validates the estimated layout against the actual layout results. It seems to compare: The maximum Y position of the last list's cell The cached sizeThatFits value If those values differ sufficiently, the transaction is not committed and another layout pass is triggered. During a later pass, the real sizes become available and GeometryReader eventually reports the final correct value. If this interpretation is correct, the behavior shown in the logs would be expected: followed later by: Possible solution I could not find a public SwiftUI API that provides an accurate content size during the initial layout pass. I tried the following options: LazyVStack + GeometryReader LazyVStack + ViewThatFits LazyVStack + .scrollBounceBehavior(...) At the same time, SwiftUI itself appears to have information about layout validity. For example, the layout logs contain entries such as: placed(...) -> ... invalid: true This suggests that SwiftUI can determine when an estimated layout result is no longer valid and requires additional layout passes. If SwiftUI knows that the current layout is invalid, is there a way to access this information from within a GeometryReader closure or by some other means? Otherwise, clients may perform layout calculations based on invalid geometry, which can result in a broken dependent layout. Have a good day!
0
2
35
2d
iOS27: Bar Marks in Swift Charts exhibit multiple severe issues
Bar Marks in Swift Charts exhibit multiple severe issues on iOS27. Tested on: iPad Pro M2, 13", iOS27 Beta 2. Feedback submitted: FB23354502 Charts form a visual backbone of our app, and these issues render the chart unusable. Without a fix, we will not be able to support iOS27. The issues we identified: (1) We arrange mutually exclusive BarMarks on a time-based x-axis, inside a vertically scrolling Chart. We use init(xStart:, xEnd:, yStart: yEnd:), creating a visual timeline. Everything renders correctly on iOS26. On iOS27, many BarMarks are missing. (2) When we tap on a BarMark, we increase its height so make it appear selected. This works nicely in iOS26. The BarMark does not animate or change size at all on iOS27. (3) We have an outline around a BarMark, as part of styling. This uses .annotation(position: .overlay). The outline renders nicely in iOS26. On iOS27, the outline is rendered as a small circle inside the BarMark.
3
1
278
2d
iOS 26 Beta bug - keyboard toolbar with bottom safe area inset
Hello! I have experienced a weird bug in iOS 26 Beta (8) and previous beta versions. The safe area inset is not correctly aligned with the keyboard toolbar on real devices and simulators. When you focus a new textfield the bottom safe area is correctly placed aligned the keyboard toolbar. On real devices the safe area inset view is covered slightly by the keyboard toolbar, which is even worse than on the simulator. Here's a clip from a simulator: Here's the code that reproduced the bug I experienced in our app. #Preview { NavigationStack { ScrollView { TextField("", text: .constant("")) .padding() .background(Color.secondary) TextField("", text: .constant("")) .padding() .background(Color.green) } .padding() .safeAreaInset(edge: .bottom, content: { Color.red .frame(maxWidth: .infinity) .frame(height: 40) }) .toolbar { ToolbarItem(placement: .keyboard) { Button {} label: { Text("test") } } } } }
4
12
1.3k
2d
LazyVStack layout issue and potential fix
Hello! Over the last several months I've spent a lot of time investigating various LazyVStack issues. I’d like to share a one problem. Blank Screen + Cell trimming For reference, I've also submitted a Feedback Assistant report for this issue. Let's use the minimal example Copy the following code into a new project and run it: struct ScrollableLazyVStack: View { @StateObject private var viewModel: ScrollableLazyVStackViewModel init(count: Int) { self._viewModel = StateObject( wrappedValue: ScrollableLazyVStackViewModel(count: count) ) } var body: some View { ScrollView { LazyVStack(spacing: 8) { ForEach(viewModel.items, id: \.index) { CellView(by: $0) } } } .padding(.horizontal, 8) } } final class ScrollableLazyVStackViewModel: ObservableObject { @Published var items: [CellModel] = [] init(count: Int) { self.items = (0..<count).map { CellModel(index: $0) } } } private struct CellView: View { let item: CellModel init(by item: CellModel) { self.item = item } var body: some View { VStack(alignment: .leading, spacing: 2) { HStack(spacing: 4) { Image(systemName: item.icon) VStack(alignment: .leading, spacing: 4) { Text(item.title) .font(.body) Text(item.subtitle) .font(.caption) .foregroundColor(.secondary) if item.index < 100 { Text("Extra Text") .frame(height: 500) } } Spacer(minLength: .zero) } } .padding(16) .background( RoundedRectangle(cornerRadius: 14, style: .continuous) .fill(Color(.systemGray5)) ) } } struct CellModel: Hashable { private static let icons = [ "star.fill", "heart.fill", "bolt.fill", "flame.fill", "leaf.fill", "moon.fill", "cloud.fill", "paperplane.fill" ] private static let titles = [ "Random Item", "Sample Entry", "List Element", "Demo Cell", "Example Row", "Test Object" ] private static let subtitles = [ "Additional information", "Secondary description", "Some extra details", "Short explanation", "Supporting text" ] let index: Int let title: String let subtitle: String let icon: String init(index: Int) { self.index = index self.title = "\(index). \(Self.titles.randomElement()!)" self.subtitle = Self.subtitles.randomElement()! self.icon = Self.icons.randomElement()! } } Steps to Reproduce Fast-scroll to the bottom of the list using the scroll indicator. Then fast-scroll back to the top by dragging and holding the scroll indicator. Actual Results Blank Screen During fast scrolling, the visible content may temporarily disappear, resulting in a white screen. In most cases, the content eventually reappears. Truncated Cells After scrolling back toward the top, some of the topmost cells may become partially truncated. Possible Cause Blank Screen I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. I'd like to assume what appears to be the underlying algorithm. When fast scrolling is active, the following sequence seems to occur: Step 1 SwiftUICore::resolveIndexAndPosition calculates an anchorIndex. For example: anchorIndex ≈ (scrollOffset / contentSize) * totalRows Step 2 SwiftUICore::resolveIndexAndPosition calculates an anchorPosition. For example: anchorPosition ≈ anchorIndex * (estimatedCellSize + spacing) Step 3 SwiftUI searches for the first visible cell and updates the position of the last laid-out cell (y_max_last_cell). Step 4 (the most important) The layout's correctness validation: Conceptually: d1 = abs(y_last_max - cachedSizeThatFits) d2 = 0.1 * min(y_last_max, cachedSizeThatFits) If: d1 < d2 -> the current CA::Transaction is committed. Otherwise: cachedSizeThatFits = sizeThatFits(...) and the process restarts from Step 1. If logging is enabled via: com.apple.SwiftUI.LazyStackLogging the behavior can be observed in the attached screenshot. Why this may fail The algorithm appears to assume that the estimated cell size does not change dramatically while fast scrolling. However, if the beginning of the list contains very tall cells and the remainder contains much smaller cells, the estimate may become significantly inaccurate. In that situation, comparing cachedSizeThatFits against the position of the last visible cell does not seem sufficient to guarantee a stable result, which may explain the temporary blank screen. Cell Truncation While scrolling upward, the following state can occur (see attached screenshot): SwiftUICore::resolveIndexAndPosition produces: anchorIndex = 2 anchorPosition = -562 The content is translated by 486.47, which corresponds approximately to: estimatedCellSize * 2 As a result, the effective anchor position becomes: -76.32 and the first 0...1 cells are truncated. Possible Solution Blank screen's problem My assumption is that the current layout validation criteria may not be sufficient in all cases. An alternative approach could be to always fill the visible region sequentially: top → bottom while scrolling downward bottom → top while scrolling upward With such a strategy, the layout result could potentially be committed immediately once the visible area is fully covered by realized cells. Cell's Truncation For the truncation issue, it seems that the current translation-based approach may be the source of the problem. One possible alternative would be to use a contentInset-based adjustment combined with clipping against the first visible cell's minY. In that model, negative inset values would not necessarily be problematic, and the visible content could remain correctly aligned. What do you think about this?
Topic: UI Frameworks SubTopic: SwiftUI
0
2
428
3d
SwiftUI Button with Image view label has smaller hit target
[Also submitted as FB20213961] SwiftUI Button with a label: closure containing only an Image view has a smaller tap target than buttons created with a Label or the convenience initializer. The hit area shrinks to the image bounds instead of preserving the standard minimum tappable size. SCREEN RECORDING On a physical device, the difference is obvious—it’s easy to miss the button. Sometimes it even shows the button-tapped bounce animation but doesn’t trigger the action. SYSTEM INFO Xcode Version 26.0 (17A321) macOS 15.6.1 (24G90) iOS 26.0 (23A340) SAMPLE CODE The following snippet shows the difference in hit targets between the convenience initializer, a Label, and an Image (the latter two in a label: closure). // ✅ Hit target is entire button Button("Button 1", systemImage: "1.square.fill") { print("Button 1 tapped") } // ✅ Hit target is entire button Button { print("Button 2 tapped") } label: { Label("Button 2", systemImage: "2.square.fill") } // ❌ Hit target is smaller than button Button { print("Button 3 tapped") } label: { Image(systemName: "3.square.fill") }
7
4
772
3d
SwiftUI animation is laggy in NSStatusItem since macOS 26 Tahoe
My app is a bit of a special case and relies on a custom view in a NSStatusItem. I use a NSHostingView and add it as a subview to my NSStatusItem's .button property. Since macOS 26 Tahoe, even simple animations like a .frame change of a Circle won't animate smoothly even though the same SwiftUI animates normally in a WindowGroup. class AppDelegate: NSObject, NSApplicationDelegate { private let statusItem: NSStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) func applicationDidFinishLaunching(_ aNotification: Notification) { let subview = NSHostingView(rootView: AnimationView()) let view = self.statusItem.button view?.addSubview(subview) subview.translatesAutoresizingMaskIntoConstraints = false guard let view = view else { return } NSLayoutConstraint.activate([ subview.centerXAnchor.constraint(equalTo: view.centerXAnchor), subview.centerYAnchor.constraint(equalTo: view.centerYAnchor), subview.widthAnchor.constraint(equalToConstant: 22), subview.heightAnchor.constraint(equalToConstant: 22) ]) } } struct AnimationView: View { @State private var isTapped = false @State private var size: CGSize = .init(width: 4, height: 4) var body: some View { Circle() .fill(.pink) .frame(width: size.width, height: size.height) .frame(width: 20, height: 20) // .frame(maxHeight: .infinity) // .padding(.horizontal, 9) // .frame(height: 22) .contentShape(Rectangle()) // .background(Color.blue.opacity(0.5)) .onTapGesture { withAnimation(.interactiveSpring(response: 0.85, dampingFraction: 0.26, blendDuration: 0.45)) { // withAnimation(.spring()) { if isTapped { size = .init(width: 4, height: 4) } else { size = .init(width: 16, height: 16) } } isTapped.toggle() }} } Example project: https://app.box.com/s/q28upunrgkxyyd97ovslgud9yitqaxfk
1
0
205
4d
Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)
[Submitted as FB23998635] On iPhone, applying .tabBarMinimizeBehavior(.onScrollDown) to a SwiftUI TabView causes the native tab bar to briefly show the first declared tab before animating to the already-restored selection during app launch. The selected tab is persisted with @AppStorage and is already restored before the TabView is presented. The sample contains no explicit animations, transactions, navigation containers, loading states, asynchronous work, or post-launch selection changes. Removing .tabBarMinimizeBehavior(.onScrollDown) eliminates the launch animation entirely. Likewise, starting with .tabBarMinimizeBehavior(.never) and changing it to .onScrollDown after a one-second delay also eliminates the issue. The behavior reproduces with three simple tabs and a direct @AppStorage selection binding. ENVIRONMENT • iOS 26 & 27 REPRO STEPS Build and run the attached sample. Select the "Two" tab. Force-quit the app. Relaunch the app. Observe the tab bar during launch. ACTUAL The tab indicator initially appears on "One", the first declared tab, then animates to the correctly restored "Two" selection. EXPECTED The restored tab should be selected and stationary from the first visible frame, with no launch animation. SAMPLE CODE struct ContentView: View { private enum AppTab: String, Hashable { case one case two case three } @AppStorage("selectedGenericTab") private var selectedTab: AppTab = .three var body: some View { TabView(selection: $selectedTab) { Tab("One", systemImage: "1.circle", value: AppTab.one) { ReproTabContent(title: "One", color: .orange) } Tab("Two", systemImage: "2.circle", value: AppTab.two) { ReproTabContent(title: "Two", color: .blue) } Tab("Three", systemImage: "3.circle", value: AppTab.three) { ReproTabContent(title: "Three", color: .green) } } .tabBarMinimizeBehavior(.onScrollDown) } } private struct ReproTabContent: View { let title: String let color: Color var body: some View { ZStack { color.opacity(0.2) .ignoresSafeArea() Text(title) .font(.largeTitle) } } }
2
0
373
4d
popoverTips don't display for toolbar menu buttons in iOS 26.1
[Also submitted as FB20756013] A popoverTip does not display for toolbar menu buttons in iOS 26.1 (23B5073a). The same code displays tips correctly in iOS 18.6. The issue occurs both in the simulator and on a physical device. Repro Steps Build and run the Sample Code below on iOS 26.1. Observe that the popoverTip does not display. Repeat on iOS 18.6 to confirm expected behavior. Expected popoverTips should appear when attached to a toolbar menu button, as they do in iOS 18.6. Actual No tip is displayed on iOS 26.1. System Info macOS 15.7.1 (24G231) Xcode 26.1 beta 3 (17B5045g) iOS 26.1 (23B5073a) Screenshot Screenshot showing two simulators side by side—iOS 18.6 on the left (tip displayed) and iOS 26.1 on the right (no tip displayed). Sample code import SwiftUI import TipKit struct PopoverTip: Tip { var title: Text { Text("Menu Tip") } var message: Text? { Text("This tip displays on iOS 18.6, but NOT on iOS 26.1.") } } struct ContentView: View { var tip = PopoverTip() var body: some View { NavigationStack { Text("`popoverTip` doesn't display on iOS 26.1 but does in iOS 18.6") .padding() .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { Button("Dismiss", role: .cancel) { } Button("Do Nothing") { } } label: { Label("More", systemImage: "ellipsis") } .popoverTip(tip) } } .navigationTitle("Popover Tip Issue") .navigationBarTitleDisplayMode(.inline) } } }
6
3
882
4d
Inspector Panel Visual UI
How can I get the inspector panel on the Mac to be respected by the toolbar? In Xcode and Pages, the inspector panel runs the entire length of the app so that everything slides over when it's invoked. But in my testing, the toolbar always overlaps the inspector. import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { @State private var selectedItem: String? = "Page 1" @State private var inspectorVisible = true let items = ["Page 1", "Page 2", "Page 3"] var body: some View { NavigationSplitView { List(items, id: \.self, selection: $selectedItem) { item in Label(item, systemImage: "doc") } } detail: { Text(selectedItem ?? "Nothing selected") .font(.title2) .foregroundStyle(.secondary) .inspector(isPresented: $inspectorVisible) { List { Section("Properties") { LabeledContent("Width", value: "100") LabeledContent("Height", value: "200") } Section("Style") { LabeledContent("Color", value: "Blue") LabeledContent("Opacity", value: "100%") } } .scrollContentBackground(.hidden) } } .toolbar { ToolbarItemGroup(placement: .principal) { Button("Draw", systemImage: "pencil") {} Button("Shape", systemImage: "circle") {} Button("Text", systemImage: "textformat") {} } ToolbarItem () { Button("Share", systemImage: "square.and.arrow.up") {} } ToolbarItem() { Button("Inspector", systemImage: "sidebar.right") { inspectorVisible.toggle() } } } .navigationTitle("My Project") } } #Preview { ContentView() .frame(width: 1100, height: 600) }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
232
5d
Runtime crash from SwiftUI.State and variadic types from Xcode 27 Beta 3
I am seeing a weird crash from Xcode 27 Beta 3 when building a variadic type DynamicProperty that also needs SwiftUI.State. This does not crash from Xcode 26. Here is a repro: import SwiftUI struct Repeater<each Input>: DynamicProperty { @State private var storage = Storage() private var input: (repeat each Input) init(_ input: repeat each Input) { self.input = (repeat each input) } } extension Repeater { final class Storage { } } @main struct CrashDemoApp: App { private var repeater = Repeater(1) var body: some Scene { WindowGroup { EmptyView() } } } Here is the crash: Thread 1 Queue : com.apple.main-thread (serial) #0 0x000000019a93aec0 in swift::TargetMetadata<swift::InProcess>::isCanonicalStaticallySpecializedGenericMetadata () #1 0x000000019a946b38 in performOnMetadataCache<swift::MetadataResponse, swift_checkMetadataState::CheckStateCallbacks> () #2 0x000000019a8c85f0 in swift_checkMetadataState () #3 0x00000001004a2c78 in type metadata completion function for Repeater () #4 0x000000019a94cfe4 in swift::GenericCacheEntry::tryInitialize () #5 0x000000019a94c870 in swift::MetadataCacheEntryBase<swift::GenericCacheEntry, void const*>::doInitialization () #6 0x000000019a94f820 in swift::LockingConcurrentMap<swift::GenericCacheEntry, swift::LockingConcurrentMapStorage<swift::GenericCacheEntry, (unsigned short)14>>::getOrInsert<swift::MetadataCacheKey, swift::MetadataRequest&, swift::TargetTypeContextDescriptor<swift::InProcess> const*&, void const* const*&> () #7 0x000000019a93c714 in _swift_getGenericMetadata () #8 0x00000001004a4190 in __swift_instantiateGenericMetadata () #9 0x00000001004a2a5c in type metadata accessor for Repeater () #10 0x00000001004a5094 in type metadata accessor for Repeater<Pack{Int}> () #11 0x00000001004a4fcc in type metadata completion function for CrashDemoApp () #12 0x000000019a9543bc in swift::MetadataCacheEntryBase<(anonymous namespace)::SingletonMetadataCacheEntry, int>::doInitialization () #13 0x000000019a8d2ae0 in swift_getSingletonMetadata () #14 0x00000001004a479c in type metadata accessor for CrashDemoApp () #15 0x00000001004a473c in static CrashDemoApp.$main() () #16 0x00000001004a4a34 in main () #17 0x0000000186e47e00 in start () Here is a repo to demo: https://github.com/vanvoorden/2026-07-17 Please let me know if you have any ideas about that. Thanks!
Topic: UI Frameworks SubTopic: SwiftUI
3
0
194
1w
What's the preferred way enable scroll behind tab bar in nested ScrollView in SwiftUI
I am having a root TabView with tabs. One of the tabs has a TabBar with page style as the root view and each Page has a ScrollView. Unfortunately the scroll view get's clipped by the parent TabView size. But I want to make the ScrollView content to go behind the root TabView's tab bar like it would work if I would have the ScrollView as direct child to the root TabBar I tried using ignoreSafeArea on the page style TabView and there are other weird bugs, it stops reacting to the Binding pageIndex I am having as a State. The custom page index view disappears Sample code: https://github.com/BProg/TabViewScrollViewBug.git
2
0
98
1w
SwiftUI's `scrollTo(id:anchor:)` doesn't work if the ScrollView is scrolling
Can somebody tell me if I'm doing something wrong or SwiftUI's scrollTo(id:anchor:) just doesn't work if the ScrollView is scrolling? I have a trivial example that demonstrates the issue: import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct Item: Identifiable { let id = UUID() let timestamp: Date let text: String } struct ContentView: View { @State private var items: [Item] = (0...10_000).map{ .init(timestamp: Date(), text: "Row \($0)") } @State private var scrollPosition = ScrollPosition(idType: Item.ID.self) @State private var newMessage: String = "" var body: some View { ScrollView { LazyVStack { ForEach(items) { item in ItemView(item: item) } }.scrollTargetLayout() } .defaultScrollAnchor(.bottom, for: .initialOffset) .scrollPosition($scrollPosition, anchor: .bottom) .safeAreaBar(edge: .bottom) { HStack { TextField("Type here", text: $newMessage, axis: .vertical) .textFieldStyle(.roundedBorder) Button("Send", action: { let trimmed = newMessage.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } newMessage = "" items.append(.init(timestamp: .now, text: trimmed)) withAnimation(.smooth) { scrollPosition.scrollTo(id: items.last!.id, anchor: .bottom) } }) }.padding() } } } struct ItemView: View { let item: Item var body: some View { VStack(alignment: .leading) { Text(item.text) Text(item.timestamp.formatted()) }.padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))) } } #Preview { ContentView() }
2
0
140
1w
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
6
Boosts
8
Views
735
Activity
1h
Xcode 26.3 Simulator renders SwiftUI app only inside a rounded rectangle instead of full screen
Hi everyone, I’m seeing a strange rendering issue in Xcode 26.3 that seems to affect only the iOS Simulator. Environment: Xcode 26.3 SwiftUI app Reproduces in Simulator only Reproduces across multiple simulator device models My code is just a minimal example Expected behavior: The view should fill the entire screen. Actual behavior: The app content is rendered only inside a centered rounded rectangle/card-like area, with black space around it, as if the app canvas is being clipped incorrectly. Minimal reproduction: import SwiftUI @main struct LayoutShowcaseApp: App { var body: some Scene { WindowGroup { Color.green.ignoresSafeArea() } } } I also tried wrapping it in a ZStack and using: .frame(maxWidth: .infinity, maxHeight: .infinity) .background(...) .ignoresSafeArea() but the result is the same. What I already tried: Clean Build Folder Switching simulator device models Resetting simulator content/settings Rebuilding from a fresh minimal SwiftUI project Since this happens with such a minimal example, it looks more like a Simulator/runtime rendering bug than a SwiftUI layout issue. Has anyone else seen this on Xcode 26.3? If yes, did you find any workaround? Thanks.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
334
Activity
2h
SwiftUI: List cells flicker during scroll when .searchPresentationToolbarBehavior(.avoidHidingContent) is used with searchable modifier inside a sheet (iOS 26)
Description When a searchable List is presented inside a sheet and uses .searchPresentationToolbarBehavior(.avoidHidingContent) to keep the navigation bar visible during search, the list cells flicker/blink under the keyboard This reproduces with public SwiftUI API only — no UIKit, no appearance-proxy customization. Steps to Reproduce Create a new iOS App (SwiftUI) project (deployment target iOS 17.1+). Replace the generated App file with the sample code below. Run on iOS 26 (reproduces on both device and Simulator). Tap "Open transfer methods" to present the sheet. Tap the search field so the keyboard is shown (search becomes active). Scroll the list down, then up into the top bounce (overscroll) Observe the cells under the keyboard. Expected Cells scroll smoothly under the keyboard; no flicker. Actual Cells under the keyboard flicker/blink during top overscroll. See the attachments with shots of the process: 1. screen before a blink, 2. Screen in the moment of blinking Sample Code import SwiftUI @main struct FlickerReproApp: App { var body: some Scene { WindowGroup { RootView() } } } struct RootView: View { @State private var isSheetPresented = false var body: some View { Button("Open transfer methods") { isSheetPresented = true } .sheet(isPresented: $isSheetPresented) { NavigationStack { SimpleSearchScreen() } } } } struct SimpleSearchScreen: View { @State private var searchText = "" private let items = (0..<40).map { "Recipient \($0)" } private var filteredItems: [String] { searchText.isEmpty ? items : items.filter { $0.localizedCaseInsensitiveContains(searchText) } } var body: some View { List { ForEach(filteredItems, id: \.self) { item in Text(item) } } .listStyle(.plain) .navigationTitle("Transfer methods") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button("Close", systemImage: "xmark") {} } } .searchable(text: $searchText, placement: .automatic, prompt: "Search") // Remove the line below -> flicker disappears, but the navigation bar // (title + toolbar items) then hides during search, which we need to keep. .searchPresentationToolbarBehavior(.avoidHidingContent) } } Notes / What was ruled out Removing .searchPresentationToolbarBehavior(.avoidHidingContent) eliminates the flicker — but then the navigation bar hides during search, which is exactly the behavior the modifier is meant to prevent. Presenting the same screen NOT inside a sheet does not flicker — the sheet presentation is required to reproduce. Independent of app-level customization: reproduces with no UINavigationBar/UITabBar/UISearchBar appearance proxies and no UIKit. Also tried, did NOT help: .scrollEdgeEffectStyle(.hard, for: .all), .toolbarBackground(.hidden, for: .navigationBar), keeping the bar visible via UISearchController.hidesNavigationBarDuringPresentation = false instead, .geometryGroup() on rows, .scrollDismissesKeyboard(.never), removing safe area insets.
Replies
0
Boosts
0
Views
15
Activity
4h
Siri Intent Dialog with custom SwiftUIView not responding to buttons with intent
I have created an AppIntent and added it to shortcuts to be able to read by Siri. When I say the phrase, the Siri intent dialog appears just fine. I have added a custom SwiftUI View inside Siri dialog box with 2 buttons with intents. The callback or handling of those buttons is not working when initiated via Siri. It works fine when I initiate it in shortcuts. I tried using the UIButton without the intent action as well but it did not work. Here is the code. static let title: LocalizedStringResource = "My Custom Intent" static var openAppWhenRun: Bool = false @MainActor func perform() async throws -> some ShowsSnippetView & ProvidesDialog { return .result(dialog: "Here are the details of your order"), content: { OrderDetailsView() } } struct OrderDetailsView { var body: some View { HStack { if #available(iOS 17.0, *) { Button(intent: ModifyOrderIntent(), label : { Text("Modify Order") }) Button(intent: CancelOrderIntent(), label : { Text("Cancel Order") }) } } } } struct ModifyOrderIntent: AppIntent { static let title: LocalizedStringResource = "Modify Order" static var openAppWhenRun: Bool = true @MainActor func perform() async throws -> some OpensIntent { // performs the deeplinking to app to a certain page to modify the order } } struct CancelOrderIntent: AppIntent { static let title: LocalizedStringResource = "Cancel Order" static var openAppWhenRun: Bool = true @MainActor func perform() async throws -> some OpensIntent { // performs the deeplinking to app to a certain page to cancel the order } } Button(action: { if let url = URL(string: "myap://open-order") { UIApplication.shared.open(url) } }
Replies
1
Boosts
2
Views
429
Activity
4h
WindowGroup Tab Bar overlaps inspector column
I've been trying to replicate an app layout similar to Xcode where we have a tab bar in the canvas for different files that are open, while also having an inspector view. I have come across this problem where the tab bar from the WindowGroup goes into the inspector on the right. This happens because the inspector is apparently owned by the Window. Interestingly, this doesn't happen for the sidebar. I don't see why it's not possible for it to not cut into inspector space either. Here's my code for ContentView where the inspector is declared: var body: some View { NavigationSplitView(columnVisibility: $columnVisibility) { FitsSidebarView(currentFitID: fit.id) .navigationSplitViewColumnWidth(min: 180, ideal: 240, max: 360) } detail: { FittingCanvasView(fit: fit, session: session) } .inspector(isPresented: $isInspectorPresented) { InspectorView() .inspectorColumnWidth(min: 240, ideal: 280, max: 400) } And here's the code for my App Entry: struct KiwiFittingApp: App { var body: some Scene { WindowGroup( "Fit", id: "fit-window", for: FitRecord.ID.self ) { fitID in FitWindowScene(fitID: fitID.wrappedValue) } defaultValue: { FitCatalog.defaultFit.id } .defaultSize(width: 1280, height: 800) .commands { SidebarCommands() InspectorCommands() } } } Does anyone have any clue how to make it work with native components?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
11
Activity
4h
visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
This was confirmed as a framework gap in an accepted answer from an Apple Vision Pro engineer in June 2024 (https://developer.apple.com/forums/thread/758014?answerId=792769022#792769022): .background fires identically whether the user taps a window's close button or the system backgrounds a window that's been out of the field of view for ~61 seconds, and there's no app-visible signal that distinguishes the two. The recommendation at the time was to use a gesture/affordance to reopen the window, and to file an enhancement request. Two years on, with the window-management APIs that have shipped since, I want to confirm whether the situation has changed as of visionOS 26. My case: VisionBlazer, a native spatial 3D creation tool (TestFlight beta, August 2026 launch). Users routinely work with several SwiftUI WindowGroup tool windows open at once — drawing tools, materials, timeline, properties, lighting — parked spatially around an ImmersiveSpace. Parking a window behind or beside you is core to the workflow. The app should terminate when the user closes the primary window, but must not terminate when a secondary window (or the primary) is simply parked out of view. What I've verified on-device (visionOS 26.5): scenePhase == .background fires identically for a user close tap and for the ~61s out-of-FoV backgrounding (SurfBoard: "…is out of FOV after 60.99 seconds. Backgrounding"). The phase sequence (active → inactive → background) and timing (~0.2–0.4s gap) are indistinguishable between the two cases. scenePhase on visionOS reflects visibility, not focus — a parked window stays .active while the user edits elsewhere, until the out-of-FoV timer fires. The session identifier reachable from the window's view hierarchy (view.window!.windowScene.session.persistentIdentifier) never matches the identifier reported by application(_:didDiscardSceneSessions:) or UIScene.didDisconnectNotification for that same close. The view-visible session stays in UIApplication.shared.openSessions indefinitely after the close. Those disconnect/discard callbacks arrive ~10–15s late and only ever carry foreign session identifiers, so they can't be attributed to a specific window. Stale-session discards from prior launches pollute the signal further. onDisappear does not fire on user close. Five strategies tried, all failed: (1) scene-object identity captured at didMoveToWindow; (2) session.persistentIdentifier matching against openSessions; (3) live re-capture of scene/session from the view hierarchy on every lifecycle change; (4) temporal correlation of didEnterBackground/didDisconnect; (5) a focus-recency heuristic on scenePhase transitions. All fail on the identifier mismatch and the visibility-not-focus semantics above. Questions: As of visionOS 26, is there now any supported way to detect that the user intentionally closed a specific window, distinct from system backgrounding? Is there a SwiftUI or scene-delegate callback tied to a window's own scene that fires only on user close? Is there a dismissalReason (or equivalent) anywhere on the close path? If none of the above exists, is an explicit in-app Quit button still the intended pattern for "quit when the main window is closed"? I have a focused test project reproducing all of this and can link it. Thanks.
Replies
3
Boosts
0
Views
485
Activity
21h
Is NavigationSplitView on macOS 27 broken?
On macOS 27 Beta 2, a simple NavigationSplitView example exhibits bizarre behaviour when the window is resized. The sidebar seemingly expands and collapses at random as the window is resized. The symptoms can be exacerbated with toolbar items. The sidebar appears to behave correctly when an inspector view is not present. Copy paste the code below into a new Xcode 27 project and run on macOS 27 and then resize the window: File -> New -> Project... -> App import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { var body: some View { NavigationSplitView { Text("Sidebar") } detail: { Text("Content") } .inspector(isPresented: .constant(true)) { Text("Inspector") } } } Adding .frame or .inspectorColumnWidth to any of the Text views does not appear to fix the issues. macOS: 27.0 Beta (26A5368g) Xcode: 27.0 beta 2 (27A5209h)
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
3
Boosts
1
Views
247
Activity
1d
Unexpected behavior in the interaction between LazyVStack and GeometryReader
Hello! I'd like to share a problem and its potential solution. Steps to reproduce: The issue can be reproduced with the following minimal example: struct TestConditionalScrollView: View { var body: some View { ConditionalScrollView { LazyVStack(spacing: 16) { Text("Text 1") .frame(height: 20) Text("Text 2") .frame(height: 30) Text("Text 3") .frame(height: 40) Text("Text 4") .frame(height: 500) Text("Text 5") .frame(height: 400) } .padding() } } } struct ConditionalScrollView<Content: View>: View { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } @State private var contentHeight: CGFloat = 0 var body: some View { GeometryReader { geo in _ = print("Height: \(contentHeight)") return Group { if contentHeight > geo.size.height { ScrollView { measuredContent } } else { measuredContent } } } } private var measuredContent: some View { content .background( GeometryReader { geo in Color.clear .preference( key: ContentHeightKey.self, value: geo.size.height ) } ) .onPreferenceChange(ContentHeightKey.self) { contentHeight = $0 } } } struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } } If we hit the breakpoint on the following line: _ = print("Height: (contentHeight)") the output looks like this: Problems observed Different values are reported, and it is unclear where those values originate from. The selected execution branch appears to change multiple times during the layout process. Possible reason As Rens Breur mentioned in the WWDC26 session "Dive into lazy stacks and scrolling with SwiftUI", LazyVStack relies on estimated layout information during certain phases of the layout process. I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. To do that, let me show how the value reaches the GeometryReader closure: Step 1 In AttributeGraph, the GeometryReader<...> node and the LazyVStack node appear to be connected as shown below: Step 2 When the LazyVStack node is updated, the layout process appears to follow roughly this logic: `SwiftUICore 'SwiftUI.ForEachState.forEachItem:` n = number of cells to evaluate (initially n == 2) For first n cells: SwiftUICore`SwiftUI.ViewLayoutEngine.sizeThatFits(...) SwiftUI.EstimationCache.add(...) The total size of the lazy stack is then estimated: SwiftUI.LazyStack<...>.sizeThatFits(...): averageCellInfo = EstimationCache.average averageCellInfo.height = (firstCellHeight + secondCellHeight) / 2 totalHeight = firstCellHeight + secondCellHeight + averageCellInfo.height * remainingCells For the sample project, this produces an estimated height of 189. This value then appears to be cached inside a LazyLayoutComputer node. Step 3 When GeometryReader is updated and its closure executes, geo.size.height appears to be resolved from the cached value stored by LazyLayoutComputer. As a result, the reported height is: 189 + 32 (padding) = 221 Step 4 My assumption is that LazyVStack subsequently validates the estimated layout against the actual layout results. It seems to compare: The maximum Y position of the last list's cell The cached sizeThatFits value If those values differ sufficiently, the transaction is not committed and another layout pass is triggered. During a later pass, the real sizes become available and GeometryReader eventually reports the final correct value. If this interpretation is correct, the behavior shown in the logs would be expected: followed later by: Possible solution I could not find a public SwiftUI API that provides an accurate content size during the initial layout pass. I tried the following options: LazyVStack + GeometryReader LazyVStack + ViewThatFits LazyVStack + .scrollBounceBehavior(...) At the same time, SwiftUI itself appears to have information about layout validity. For example, the layout logs contain entries such as: placed(...) -> ... invalid: true This suggests that SwiftUI can determine when an estimated layout result is no longer valid and requires additional layout passes. If SwiftUI knows that the current layout is invalid, is there a way to access this information from within a GeometryReader closure or by some other means? Otherwise, clients may perform layout calculations based on invalid geometry, which can result in a broken dependent layout. Have a good day!
Replies
0
Boosts
2
Views
35
Activity
2d
iOS27: Bar Marks in Swift Charts exhibit multiple severe issues
Bar Marks in Swift Charts exhibit multiple severe issues on iOS27. Tested on: iPad Pro M2, 13", iOS27 Beta 2. Feedback submitted: FB23354502 Charts form a visual backbone of our app, and these issues render the chart unusable. Without a fix, we will not be able to support iOS27. The issues we identified: (1) We arrange mutually exclusive BarMarks on a time-based x-axis, inside a vertically scrolling Chart. We use init(xStart:, xEnd:, yStart: yEnd:), creating a visual timeline. Everything renders correctly on iOS26. On iOS27, many BarMarks are missing. (2) When we tap on a BarMark, we increase its height so make it appear selected. This works nicely in iOS26. The BarMark does not animate or change size at all on iOS27. (3) We have an outline around a BarMark, as part of styling. This uses .annotation(position: .overlay). The outline renders nicely in iOS26. On iOS27, the outline is rendered as a small circle inside the BarMark.
Replies
3
Boosts
1
Views
278
Activity
2d
iOS 26 Beta bug - keyboard toolbar with bottom safe area inset
Hello! I have experienced a weird bug in iOS 26 Beta (8) and previous beta versions. The safe area inset is not correctly aligned with the keyboard toolbar on real devices and simulators. When you focus a new textfield the bottom safe area is correctly placed aligned the keyboard toolbar. On real devices the safe area inset view is covered slightly by the keyboard toolbar, which is even worse than on the simulator. Here's a clip from a simulator: Here's the code that reproduced the bug I experienced in our app. #Preview { NavigationStack { ScrollView { TextField("", text: .constant("")) .padding() .background(Color.secondary) TextField("", text: .constant("")) .padding() .background(Color.green) } .padding() .safeAreaInset(edge: .bottom, content: { Color.red .frame(maxWidth: .infinity) .frame(height: 40) }) .toolbar { ToolbarItem(placement: .keyboard) { Button {} label: { Text("test") } } } } }
Replies
4
Boosts
12
Views
1.3k
Activity
2d
Accessing SwiftData document package?
I would very much like to store some additional data in my SwiftData document package, outside of SwiftData. Metadata about the document that doesn't lend itself well to the underlying RDBMS nature of SwiftData. Is that possible?
Replies
1
Boosts
1
Views
944
Activity
2d
LazyVStack layout issue and potential fix
Hello! Over the last several months I've spent a lot of time investigating various LazyVStack issues. I’d like to share a one problem. Blank Screen + Cell trimming For reference, I've also submitted a Feedback Assistant report for this issue. Let's use the minimal example Copy the following code into a new project and run it: struct ScrollableLazyVStack: View { @StateObject private var viewModel: ScrollableLazyVStackViewModel init(count: Int) { self._viewModel = StateObject( wrappedValue: ScrollableLazyVStackViewModel(count: count) ) } var body: some View { ScrollView { LazyVStack(spacing: 8) { ForEach(viewModel.items, id: \.index) { CellView(by: $0) } } } .padding(.horizontal, 8) } } final class ScrollableLazyVStackViewModel: ObservableObject { @Published var items: [CellModel] = [] init(count: Int) { self.items = (0..<count).map { CellModel(index: $0) } } } private struct CellView: View { let item: CellModel init(by item: CellModel) { self.item = item } var body: some View { VStack(alignment: .leading, spacing: 2) { HStack(spacing: 4) { Image(systemName: item.icon) VStack(alignment: .leading, spacing: 4) { Text(item.title) .font(.body) Text(item.subtitle) .font(.caption) .foregroundColor(.secondary) if item.index < 100 { Text("Extra Text") .frame(height: 500) } } Spacer(minLength: .zero) } } .padding(16) .background( RoundedRectangle(cornerRadius: 14, style: .continuous) .fill(Color(.systemGray5)) ) } } struct CellModel: Hashable { private static let icons = [ "star.fill", "heart.fill", "bolt.fill", "flame.fill", "leaf.fill", "moon.fill", "cloud.fill", "paperplane.fill" ] private static let titles = [ "Random Item", "Sample Entry", "List Element", "Demo Cell", "Example Row", "Test Object" ] private static let subtitles = [ "Additional information", "Secondary description", "Some extra details", "Short explanation", "Supporting text" ] let index: Int let title: String let subtitle: String let icon: String init(index: Int) { self.index = index self.title = "\(index). \(Self.titles.randomElement()!)" self.subtitle = Self.subtitles.randomElement()! self.icon = Self.icons.randomElement()! } } Steps to Reproduce Fast-scroll to the bottom of the list using the scroll indicator. Then fast-scroll back to the top by dragging and holding the scroll indicator. Actual Results Blank Screen During fast scrolling, the visible content may temporarily disappear, resulting in a white screen. In most cases, the content eventually reappears. Truncated Cells After scrolling back toward the top, some of the topmost cells may become partially truncated. Possible Cause Blank Screen I do not want to rely on or investigate SwiftUI's non-public implementation details, but I would like to explain what I believe is happening internally. I'd like to assume what appears to be the underlying algorithm. When fast scrolling is active, the following sequence seems to occur: Step 1 SwiftUICore::resolveIndexAndPosition calculates an anchorIndex. For example: anchorIndex ≈ (scrollOffset / contentSize) * totalRows Step 2 SwiftUICore::resolveIndexAndPosition calculates an anchorPosition. For example: anchorPosition ≈ anchorIndex * (estimatedCellSize + spacing) Step 3 SwiftUI searches for the first visible cell and updates the position of the last laid-out cell (y_max_last_cell). Step 4 (the most important) The layout's correctness validation: Conceptually: d1 = abs(y_last_max - cachedSizeThatFits) d2 = 0.1 * min(y_last_max, cachedSizeThatFits) If: d1 < d2 -> the current CA::Transaction is committed. Otherwise: cachedSizeThatFits = sizeThatFits(...) and the process restarts from Step 1. If logging is enabled via: com.apple.SwiftUI.LazyStackLogging the behavior can be observed in the attached screenshot. Why this may fail The algorithm appears to assume that the estimated cell size does not change dramatically while fast scrolling. However, if the beginning of the list contains very tall cells and the remainder contains much smaller cells, the estimate may become significantly inaccurate. In that situation, comparing cachedSizeThatFits against the position of the last visible cell does not seem sufficient to guarantee a stable result, which may explain the temporary blank screen. Cell Truncation While scrolling upward, the following state can occur (see attached screenshot): SwiftUICore::resolveIndexAndPosition produces: anchorIndex = 2 anchorPosition = -562 The content is translated by 486.47, which corresponds approximately to: estimatedCellSize * 2 As a result, the effective anchor position becomes: -76.32 and the first 0...1 cells are truncated. Possible Solution Blank screen's problem My assumption is that the current layout validation criteria may not be sufficient in all cases. An alternative approach could be to always fill the visible region sequentially: top → bottom while scrolling downward bottom → top while scrolling upward With such a strategy, the layout result could potentially be committed immediately once the visible area is fully covered by realized cells. Cell's Truncation For the truncation issue, it seems that the current translation-based approach may be the source of the problem. One possible alternative would be to use a contentInset-based adjustment combined with clipping against the first visible cell's minY. In that model, negative inset values would not necessarily be problematic, and the visible content could remain correctly aligned. What do you think about this?
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
2
Views
428
Activity
3d
SwiftUI Button with Image view label has smaller hit target
[Also submitted as FB20213961] SwiftUI Button with a label: closure containing only an Image view has a smaller tap target than buttons created with a Label or the convenience initializer. The hit area shrinks to the image bounds instead of preserving the standard minimum tappable size. SCREEN RECORDING On a physical device, the difference is obvious—it’s easy to miss the button. Sometimes it even shows the button-tapped bounce animation but doesn’t trigger the action. SYSTEM INFO Xcode Version 26.0 (17A321) macOS 15.6.1 (24G90) iOS 26.0 (23A340) SAMPLE CODE The following snippet shows the difference in hit targets between the convenience initializer, a Label, and an Image (the latter two in a label: closure). // ✅ Hit target is entire button Button("Button 1", systemImage: "1.square.fill") { print("Button 1 tapped") } // ✅ Hit target is entire button Button { print("Button 2 tapped") } label: { Label("Button 2", systemImage: "2.square.fill") } // ❌ Hit target is smaller than button Button { print("Button 3 tapped") } label: { Image(systemName: "3.square.fill") }
Replies
7
Boosts
4
Views
772
Activity
3d
SwiftUI animation is laggy in NSStatusItem since macOS 26 Tahoe
My app is a bit of a special case and relies on a custom view in a NSStatusItem. I use a NSHostingView and add it as a subview to my NSStatusItem's .button property. Since macOS 26 Tahoe, even simple animations like a .frame change of a Circle won't animate smoothly even though the same SwiftUI animates normally in a WindowGroup. class AppDelegate: NSObject, NSApplicationDelegate { private let statusItem: NSStatusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) func applicationDidFinishLaunching(_ aNotification: Notification) { let subview = NSHostingView(rootView: AnimationView()) let view = self.statusItem.button view?.addSubview(subview) subview.translatesAutoresizingMaskIntoConstraints = false guard let view = view else { return } NSLayoutConstraint.activate([ subview.centerXAnchor.constraint(equalTo: view.centerXAnchor), subview.centerYAnchor.constraint(equalTo: view.centerYAnchor), subview.widthAnchor.constraint(equalToConstant: 22), subview.heightAnchor.constraint(equalToConstant: 22) ]) } } struct AnimationView: View { @State private var isTapped = false @State private var size: CGSize = .init(width: 4, height: 4) var body: some View { Circle() .fill(.pink) .frame(width: size.width, height: size.height) .frame(width: 20, height: 20) // .frame(maxHeight: .infinity) // .padding(.horizontal, 9) // .frame(height: 22) .contentShape(Rectangle()) // .background(Color.blue.opacity(0.5)) .onTapGesture { withAnimation(.interactiveSpring(response: 0.85, dampingFraction: 0.26, blendDuration: 0.45)) { // withAnimation(.spring()) { if isTapped { size = .init(width: 4, height: 4) } else { size = .init(width: 16, height: 16) } } isTapped.toggle() }} } Example project: https://app.box.com/s/q28upunrgkxyyd97ovslgud9yitqaxfk
Replies
1
Boosts
0
Views
205
Activity
4d
Tab bar animates from first tab to restored selection at launch with .tabBarMinimizeBehavior(.onScrollDown)
[Submitted as FB23998635] On iPhone, applying .tabBarMinimizeBehavior(.onScrollDown) to a SwiftUI TabView causes the native tab bar to briefly show the first declared tab before animating to the already-restored selection during app launch. The selected tab is persisted with @AppStorage and is already restored before the TabView is presented. The sample contains no explicit animations, transactions, navigation containers, loading states, asynchronous work, or post-launch selection changes. Removing .tabBarMinimizeBehavior(.onScrollDown) eliminates the launch animation entirely. Likewise, starting with .tabBarMinimizeBehavior(.never) and changing it to .onScrollDown after a one-second delay also eliminates the issue. The behavior reproduces with three simple tabs and a direct @AppStorage selection binding. ENVIRONMENT • iOS 26 & 27 REPRO STEPS Build and run the attached sample. Select the "Two" tab. Force-quit the app. Relaunch the app. Observe the tab bar during launch. ACTUAL The tab indicator initially appears on "One", the first declared tab, then animates to the correctly restored "Two" selection. EXPECTED The restored tab should be selected and stationary from the first visible frame, with no launch animation. SAMPLE CODE struct ContentView: View { private enum AppTab: String, Hashable { case one case two case three } @AppStorage("selectedGenericTab") private var selectedTab: AppTab = .three var body: some View { TabView(selection: $selectedTab) { Tab("One", systemImage: "1.circle", value: AppTab.one) { ReproTabContent(title: "One", color: .orange) } Tab("Two", systemImage: "2.circle", value: AppTab.two) { ReproTabContent(title: "Two", color: .blue) } Tab("Three", systemImage: "3.circle", value: AppTab.three) { ReproTabContent(title: "Three", color: .green) } } .tabBarMinimizeBehavior(.onScrollDown) } } private struct ReproTabContent: View { let title: String let color: Color var body: some View { ZStack { color.opacity(0.2) .ignoresSafeArea() Text(title) .font(.largeTitle) } } }
Replies
2
Boosts
0
Views
373
Activity
4d
popoverTips don't display for toolbar menu buttons in iOS 26.1
[Also submitted as FB20756013] A popoverTip does not display for toolbar menu buttons in iOS 26.1 (23B5073a). The same code displays tips correctly in iOS 18.6. The issue occurs both in the simulator and on a physical device. Repro Steps Build and run the Sample Code below on iOS 26.1. Observe that the popoverTip does not display. Repeat on iOS 18.6 to confirm expected behavior. Expected popoverTips should appear when attached to a toolbar menu button, as they do in iOS 18.6. Actual No tip is displayed on iOS 26.1. System Info macOS 15.7.1 (24G231) Xcode 26.1 beta 3 (17B5045g) iOS 26.1 (23B5073a) Screenshot Screenshot showing two simulators side by side—iOS 18.6 on the left (tip displayed) and iOS 26.1 on the right (no tip displayed). Sample code import SwiftUI import TipKit struct PopoverTip: Tip { var title: Text { Text("Menu Tip") } var message: Text? { Text("This tip displays on iOS 18.6, but NOT on iOS 26.1.") } } struct ContentView: View { var tip = PopoverTip() var body: some View { NavigationStack { Text("`popoverTip` doesn't display on iOS 26.1 but does in iOS 18.6") .padding() .toolbar { ToolbarItem(placement: .topBarTrailing) { Menu { Button("Dismiss", role: .cancel) { } Button("Do Nothing") { } } label: { Label("More", systemImage: "ellipsis") } .popoverTip(tip) } } .navigationTitle("Popover Tip Issue") .navigationBarTitleDisplayMode(.inline) } } }
Replies
6
Boosts
3
Views
882
Activity
4d
Inspector Panel Visual UI
How can I get the inspector panel on the Mac to be respected by the toolbar? In Xcode and Pages, the inspector panel runs the entire length of the app so that everything slides over when it's invoked. But in my testing, the toolbar always overlaps the inspector. import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { @State private var selectedItem: String? = "Page 1" @State private var inspectorVisible = true let items = ["Page 1", "Page 2", "Page 3"] var body: some View { NavigationSplitView { List(items, id: \.self, selection: $selectedItem) { item in Label(item, systemImage: "doc") } } detail: { Text(selectedItem ?? "Nothing selected") .font(.title2) .foregroundStyle(.secondary) .inspector(isPresented: $inspectorVisible) { List { Section("Properties") { LabeledContent("Width", value: "100") LabeledContent("Height", value: "200") } Section("Style") { LabeledContent("Color", value: "Blue") LabeledContent("Opacity", value: "100%") } } .scrollContentBackground(.hidden) } } .toolbar { ToolbarItemGroup(placement: .principal) { Button("Draw", systemImage: "pencil") {} Button("Shape", systemImage: "circle") {} Button("Text", systemImage: "textformat") {} } ToolbarItem () { Button("Share", systemImage: "square.and.arrow.up") {} } ToolbarItem() { Button("Inspector", systemImage: "sidebar.right") { inspectorVisible.toggle() } } } .navigationTitle("My Project") } } #Preview { ContentView() .frame(width: 1100, height: 600) }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
232
Activity
5d
Runtime crash from SwiftUI.State and variadic types from Xcode 27 Beta 3
I am seeing a weird crash from Xcode 27 Beta 3 when building a variadic type DynamicProperty that also needs SwiftUI.State. This does not crash from Xcode 26. Here is a repro: import SwiftUI struct Repeater<each Input>: DynamicProperty { @State private var storage = Storage() private var input: (repeat each Input) init(_ input: repeat each Input) { self.input = (repeat each input) } } extension Repeater { final class Storage { } } @main struct CrashDemoApp: App { private var repeater = Repeater(1) var body: some Scene { WindowGroup { EmptyView() } } } Here is the crash: Thread 1 Queue : com.apple.main-thread (serial) #0 0x000000019a93aec0 in swift::TargetMetadata<swift::InProcess>::isCanonicalStaticallySpecializedGenericMetadata () #1 0x000000019a946b38 in performOnMetadataCache<swift::MetadataResponse, swift_checkMetadataState::CheckStateCallbacks> () #2 0x000000019a8c85f0 in swift_checkMetadataState () #3 0x00000001004a2c78 in type metadata completion function for Repeater () #4 0x000000019a94cfe4 in swift::GenericCacheEntry::tryInitialize () #5 0x000000019a94c870 in swift::MetadataCacheEntryBase<swift::GenericCacheEntry, void const*>::doInitialization () #6 0x000000019a94f820 in swift::LockingConcurrentMap<swift::GenericCacheEntry, swift::LockingConcurrentMapStorage<swift::GenericCacheEntry, (unsigned short)14>>::getOrInsert<swift::MetadataCacheKey, swift::MetadataRequest&, swift::TargetTypeContextDescriptor<swift::InProcess> const*&, void const* const*&> () #7 0x000000019a93c714 in _swift_getGenericMetadata () #8 0x00000001004a4190 in __swift_instantiateGenericMetadata () #9 0x00000001004a2a5c in type metadata accessor for Repeater () #10 0x00000001004a5094 in type metadata accessor for Repeater<Pack{Int}> () #11 0x00000001004a4fcc in type metadata completion function for CrashDemoApp () #12 0x000000019a9543bc in swift::MetadataCacheEntryBase<(anonymous namespace)::SingletonMetadataCacheEntry, int>::doInitialization () #13 0x000000019a8d2ae0 in swift_getSingletonMetadata () #14 0x00000001004a479c in type metadata accessor for CrashDemoApp () #15 0x00000001004a473c in static CrashDemoApp.$main() () #16 0x00000001004a4a34 in main () #17 0x0000000186e47e00 in start () Here is a repo to demo: https://github.com/vanvoorden/2026-07-17 Please let me know if you have any ideas about that. Thanks!
Topic: UI Frameworks SubTopic: SwiftUI
Replies
3
Boosts
0
Views
194
Activity
1w
What's the preferred way enable scroll behind tab bar in nested ScrollView in SwiftUI
I am having a root TabView with tabs. One of the tabs has a TabBar with page style as the root view and each Page has a ScrollView. Unfortunately the scroll view get's clipped by the parent TabView size. But I want to make the ScrollView content to go behind the root TabView's tab bar like it would work if I would have the ScrollView as direct child to the root TabBar I tried using ignoreSafeArea on the page style TabView and there are other weird bugs, it stops reacting to the Binding pageIndex I am having as a State. The custom page index view disappears Sample code: https://github.com/BProg/TabViewScrollViewBug.git
Replies
2
Boosts
0
Views
98
Activity
1w
SwiftUI's `scrollTo(id:anchor:)` doesn't work if the ScrollView is scrolling
Can somebody tell me if I'm doing something wrong or SwiftUI's scrollTo(id:anchor:) just doesn't work if the ScrollView is scrolling? I have a trivial example that demonstrates the issue: import SwiftUI @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct Item: Identifiable { let id = UUID() let timestamp: Date let text: String } struct ContentView: View { @State private var items: [Item] = (0...10_000).map{ .init(timestamp: Date(), text: "Row \($0)") } @State private var scrollPosition = ScrollPosition(idType: Item.ID.self) @State private var newMessage: String = "" var body: some View { ScrollView { LazyVStack { ForEach(items) { item in ItemView(item: item) } }.scrollTargetLayout() } .defaultScrollAnchor(.bottom, for: .initialOffset) .scrollPosition($scrollPosition, anchor: .bottom) .safeAreaBar(edge: .bottom) { HStack { TextField("Type here", text: $newMessage, axis: .vertical) .textFieldStyle(.roundedBorder) Button("Send", action: { let trimmed = newMessage.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } newMessage = "" items.append(.init(timestamp: .now, text: trimmed)) withAnimation(.smooth) { scrollPosition.scrollTo(id: items.last!.id, anchor: .bottom) } }) }.padding() } } } struct ItemView: View { let item: Item var body: some View { VStack(alignment: .leading) { Text(item.text) Text(item.timestamp.formatted()) }.padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))) } } #Preview { ContentView() }
Replies
2
Boosts
0
Views
140
Activity
1w