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

Charts performance issue
Hi, I want to recreate a chart from Apple Health and I have code like this. When I scroll - especially the week and month charts, there are performance issues. If I remove .chartScrollPosition(x: $scrollChartPosition), it runs smoothly, but I need to know which part of the chart is currently displayed. Can you help me? import Charts import SwiftUI struct MacroChartView: View { var selectedRange: ChartRange var binnedPoints: [MacroBinPoint] @State private var scrollChartPosition: Date = .now var body: some View { VStack { Text("\(selectedRange.rangeLabel(for: scrollChartPosition))") Chart(binnedPoints) { point in BarMark( x: .value("Date", point.date, unit: selectedRange.binComponent), y: .value("Calories", point.calories) ) } .frame(height: 324) .chartXVisibleDomain(length: selectedRange.visibleDomainLength()) .chartScrollableAxes(.horizontal) .chartScrollPosition(x: $scrollChartPosition) .chartScrollTargetBehavior(.valueAligned(matching: selectedRange.scrollAlignmentComponents)) .chartXAxis { switch selectedRange { case .week: AxisMarks(values: .stride(by: .day)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.weekday(.abbreviated)) } case .month: AxisMarks(values: .stride(by: .weekOfYear)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.day()) } case .halfYear: AxisMarks(values: .stride(by: .month)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.month(.abbreviated)) } case .year: AxisMarks(values: .stride(by: .month)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.month(.abbreviated)) } } } } } } enum MeasurementHistoryMode { case macros case comparisons } enum MacroKindToDisplay { case protein, fat, carbs } enum MacrosDisplayMode: Equatable { case all case single(MacroKindToDisplay) } enum ChartRange: String, CaseIterable { case week = "T" case month = "M" case halfYear = "6M" case year = "R" var binComponent: Calendar.Component { switch self { case .week, .month: return .day case .halfYear: return .weekOfYear case .year: return .month } } var scrollAlignmentComponents: DateComponents { switch self { case .week: return DateComponents(hour: 0, minute: 0, second: 0) case .month: return DateComponents(hour: 0) case .halfYear: return DateComponents(weekday: 1) case .year: return DateComponents(day: 1) } } func visibleDomainLength() -> Int { switch self { case .week: return 7 * 24 * 60 * 60 case .month: return 31 * 24 * 60 * 60 case .halfYear: return 6 * 31 * 24 * 60 * 60 case .year: return 12 * 31 * 24 * 60 * 60 } } func start(for date: Date) -> Date { let cal = Calendar.current switch self { case .week, .month: return cal.startOfDay(for: date) case .halfYear: return cal.dateInterval(of: .weekOfYear, for: date)?.start ?? cal.startOfDay(for: date) case .year: return cal.dateInterval(of: .month, for: date)?.start ?? cal.startOfDay(for: date) } } func rangeLabel(for start: Date) -> String { let end = start.addingTimeInterval(TimeInterval(visibleDomainLength())) let f = DateFormatter() f.dateFormat = Calendar.current.isDate(start, inSameDayAs: end) ? "MMM d" : "MMM d" return Calendar.current.isDate(start, inSameDayAs: end) ? f.string(from: start) : "\(f.string(from: start)) – \(f.string(from: end))" } } struct MacrosPoint: Identifiable { var id: Date { date } let date: Date let calories: Double let proteinInGrams: Double let carbsInGrams: Double let fatInGrams: Double } struct MacroBinPoint: Identifiable { var id: Date { date } let date: Date let calories: Double let proteinKcal: Double let carbsKcal: Double let fatKcal: Double } func bin(points: [MacrosPoint], for period: ChartRange) -> [MacroBinPoint] { let grouped = Dictionary(grouping: points) { point in period.start(for: point.date) } let bins = grouped.map { (start, items) -> MacroBinPoint in var calories = items.reduce(0) { $0 + $1.calories } var proteinKcal = items.reduce(0) { $0 + $1.proteinInGrams * 4 } var carbsKcal = items.reduce(0) { $0 + $1.carbsInGrams * 4 } var fatKcal = items.reduce(0) { $0 + $1.fatInGrams * 9 } calories /= Double(items.count) proteinKcal /= Double(items.count) carbsKcal /= Double(items.count) fatKcal /= Double(items.count) return MacroBinPoint(date: start, calories: calories, proteinKcal: proteinKcal, carbsKcal: carbsKcal, fatKcal: fatKcal) } .sorted { $0.date < $1.date } return bins } struct ExampleData { static let macrosPoints: [MacrosPoint] = [ MacrosPoint(date: Date(timeIntervalSince1970: 1687949774), calories: 1895, proteinInGrams: 115, carbsInGrams: 192, fatInGrams: 72),... ]
2
1
432
1d
Programatically adding to a TextField and moving the TextSelection point in SwiftUI
Hi! I am trying to create a simple SwiftUI TextField, with an external button to add text to the field at the current insertion point (the cursor in the TextField). When I add the text, the cursor (I-Beam) remains at the original insertion point, so I want it to move over to the end of what I added. The trouble is, it sometimes moves further forward or to the end (visibly) but works as if it is still at the point I moved it to. This seems to possibly be due to emojis in the TextField (because, I assume, they are composed of more bytes). Further, sometimes the addition of the text can cause an emoji to appear unexpectedly, I assume because it is combining the bytes in an odd way. So moving the cursor seems to sometimes introduce weird behaviour. This comes from a much larger project, but I have distilled this down to the smallest example project I could. And I have a video to show how it behaves. Here's the main part of the code, and I'll attach an Xcode project: import SwiftUI struct ContentView: View { @State private var text: String = "abcdef🧁🧁🧁🧁ghijkl" @State private var selectedText: TextSelection? var body: some View { VStack { TextField("", text: $text, selection: $selectedText) .font(.title) Button("Add Z at Insertion Point in TextField") { // Get indices of any selection in the text field let from: String.Index, to: String.Index if let selectedText = selectedText { let indices = selectedText.indices switch indices { case .selection(let range): from = range.lowerBound to = range.upperBound case .multiSelection(let rangeSet): from = rangeSet.ranges.first!.lowerBound to = rangeSet.ranges.first!.upperBound default: from = self.text.endIndex to = self.text.endIndex } } else { from = self.text.endIndex to = self.text.endIndex } guard from <= to && from <= self.text.endIndex else { return } // Insert and update the cursor position self.text.replaceSubrange(from..<to, with: "Z") // Move cursor after the inserted character let newIndex = self.text.index(after: from) selectedText = TextSelection(insertionPoint: newIndex) } } .padding() } } STEPS TO REPRODUCE Run the app. Also view the video as it shows the steps. Put insertion point between c and d. Press the "Add Z" button. Note that Z is placed between c and d. This is great. Put insertion point between h and i. Press the "Add Z" button. Note that Z is placed between h and i. BUT, the insertion point I-beam moves to the end of the string. Press the "Add Z" button again. Z is added where you would have expected based on where the TextSelection insertion point was put, but the flashing I-Beam is still at the end. Press the "Add Z" button again. Same issue. insertion point is being shown at end, but to the button it is between Z and i. OF NOTE, if you use the keyboard and press delete, it deletes from end (where the I-beam is). Now put insertion point between the 4 cupcakes. Press "Add Z" two times. It behaves correctly. Press "Add Z" a third time. It adds a fairy emoji. So, any idea what I am doing wrong? I thought it might be an issue requiring me to update in a background thread, but I tried that, even delaying the update in the thread, but the issue remains. Thanks in advance. Here's a video: https://curmi.name/temp/SimpleTextField%20showing%20issues.mp4 And if it helps, here is the Xcode project: https://curmi.name/temp/SimpleTextfield.zip
0
0
168
1d
error: A NavigationLink is presenting a value of type “String” but there is no matching navigationDestination
please help, i am new to SWIFTUI getting an error: A NavigationLink is presenting a value of type “String” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated. struct debug_navigation_3: View { @State private var path = NavigationPath() @StateObject private var debug_Client_Lead_Data_ListVM = Debug_Client_Lead_Data_List_Model() var body: some View { NavigationStack(path:$path) { VStack { List(debug_Client_Lead_Data_ListVM.Debug_Client_Lead_Data_Rows, id: \.self) { curr_clientLeadRequest in NavigationLink(curr_clientLeadRequest.Client_Message, value: curr_clientLeadRequest.Client_Message) } } .navigationDestination(for:Debug_Client_Lead_Data.self) { curr_clientLeadRequest in debug_navigation_3_DetailView(rec_id: Int64(curr_clientLeadRequest.id)) } } .onAppear() { Task { await debug_Client_Lead_Data_ListVM.search(rec_id:Int64(0)) //bring all REST API } } } } // data model import Foundation struct Debug_Client_Lead_Data_Response: Decodable { let Debug_Client_Lead_Data_Rows: [Debug_Client_Lead_Data] private enum CodingKeys: String, CodingKey { case Debug_Client_Lead_Data_Rows = "rows" // root tag: REST API } } struct Debug_Client_Lead_Data: Decodable, Hashable, Identifiable { let id:Int32 let Client_Message: String private enum CodingKeys: String, CodingKey { case id = "id" case Client_Message = "Client_Message" } } //list view model import Foundation @MainActor class Debug_Client_Lead_Data_List_Model: ObservableObject { @Published var Debug_Client_Lead_Data_Rows: [Debug_Client_Lead_Data_ListViewModel] = [] func search(rec_id:Int64) async { do { let Debug_Client_Lead_Data_Rows = try await Webservice_debug_client_lead_data().getClientLeadRequestSummary(rec_id:rec_id) // '0' optional self.Debug_Client_Lead_Data_Rows = Debug_Client_Lead_Data_Rows.map(Debug_Client_Lead_Data_ListViewModel.init) } catch { print(error) } } } struct Debug_Client_Lead_Data_ListViewModel: Identifiable, Hashable { let debug_Client_Lead_Data: Debug_Client_Lead_Data var id:Int32 { debug_Client_Lead_Data.id } var Client_Message: String { debug_Client_Lead_Data.Client_Message } } //REST API import Foundation class Webservice_debug_client_lead_data { func getClientLeadRequestSummary(rec_id:Int64) async throws -> [Debug_Client_Lead_Data] { var components = URLComponents() components.scheme = Global_REST_API_URL_HTTP components.host = Global_REST_API_URL components.port = Global_REST_API_URL_port components.path = "/GetClientLeadRequest" components.queryItems = [ URLQueryItem(name: "rec_id", value: String(rec_id)) // Optional, pass '0' for all rows ] guard let url = components.url else { throw NetworkError.badURL } let (data, response) = try await URLSession.shared.data(from: url) guard (response as? HTTPURLResponse)?.statusCode == 200 else {throw NetworkError.badID } let Debug_Client_Lead_Data_Response = try? JSONDecoder().decode(Debug_Client_Lead_Data_Response.self, from: data) return Debug_Client_Lead_Data_Response?.Debug_Client_Lead_Data_Rows ?? [] } }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
156
2d
Why doesn't .glassEffect tint render on a Menu in an iOS 26 toolbar?
This is what I am trying to achieve (from the Phone app, similar one is also in Photos) I have a standard SwiftUI Menu in a toolbar with a glass tint applied to indicate the filter is active: Menu { // …filter options } label: { Label("Filter", systemImage: "line.3.horizontal.decrease") } .glassEffect(.regular.tint(.accentColor).interactive()) The glass effect doesn't render at all, no tint. The button looks completely unstyled. If I switch the label from Label to Image, the glass renders, but as a stretched oblong pill. I have tried several other combinations too: Also in the Apple's version during hover (iPad) the highlight aligns with the tint itself (see second image above) rather than outside it like in example 3 from the list above: Is there a way to get a Menu's trigger inset tint to look as in the Phone app example?
0
0
37
3d
"any View cannot conform to View"
Hello, What is the solution to this problem, assuming that you are not supposed to use AnyView, which I hear over and over again? Given this protocol: protocol MyProtocol { associatedtype V: View var content: () -> V { get } } if I want to store a heterogenous collection of MyProtocol, let collection: [any MyProtocol], then the underlying V type of each is erased to any View, which (supposedly) does not conform to View. Is there no way to "unbox" the existential any View to get the underlying View back? That unboxing idea is something I heard in a few WWDC videos but it seems like it does not apply to View.
Topic: UI Frameworks SubTopic: SwiftUI
0
0
37
3d
SwiftUI NavigationSplitView sidebar toolbar has excessive top inset when embedded in TabView since iPadOS 26.4
I’m seeing a layout regression in SwiftUI on iPadOS 26.4 involving NavigationSplitView inside a TabView. When a NavigationSplitView is embedded in a TabView, the sidebar toolbar appears to reserve too much vertical space. There is a large vertical gap between the top edge of the sidebar and the sidebar collapse/toggle icon. It looks as if the sidebar toolbar itself has become much taller than expected. The same NavigationSplitView layout is rendered correctly when it is shown directly without being embedded in a TabView. Environment: iPadOS 26.4 or later SwiftUI iPad TabView NavigationSplitView inside one tab Expected behavior The sidebar toolbar should use its normal height, as it does when the same NavigationSplitView is shown without a surrounding TabView. The sidebar collapse/toggle icon should appear close to the top of the sidebar, without a large empty gap above it. Actual behavior When the NavigationSplitView is hosted inside a TabView, the sidebar toolbar area becomes excessively tall. A large empty space appears above the sidebar collapse/toggle icon. This only happens in the TabView setup. Rendering the same NavigationSplitView directly does not show the issue. Feedback I also filed this as Feedback Assistant report: FB22645938 Has anyone else seen this behavior since iPadOS 26.4? Is this an intentional layout change, or is there a supported way to avoid this additional top inset when using NavigationSplitView inside TabView? Reproduction import SwiftUI struct ContentView: View { enum AppTab { case first case second } @State private var selectedTab: AppTab = .first var body: some View { TabView(selection: $selectedTab) { Tab("First", systemImage: "sidebar.leading", value: .first) { NavigationSplitView { List { Section("Sidebar Content") { ForEach(1...20, id: \.self) { index in Text("Item \(index)") } } } .navigationTitle("Sidebar") .toolbar { ToolbarItem(placement: .topBarLeading) { Button { // action } label: { Image(systemName: "plus") } } } } detail: { Text("Detail") } } Tab("Second", systemImage: "doc", value: .second) { Text("Second tab") } } } }
0
0
101
3d
HSplitView resize cursor missing when split view is conditionally rendered (FB22712819)
Hey all, Ran into a fun one on macOS 26.4.1 / Xcode 26.4.1, and I think I've narrowed it down enough to be worth sharing. Symptom: HSplitView's divider drags fine, but the cursor never changes to the resize cursor on hover. Just stays as the regular arrow. The cause (in my case): the HSplitView was conditionally rendered via an outer if let: struct PlayerDetailsView: View { let theme: Theme var playerVM = PlayerViewModel.shared var body: some View { let grayscaleCM = ColorMap(table: .grayscale) ZStack { VStack(spacing: 0) { Rectangle() .fill(LinearGradient(colors: [Color.black.opacity(0.45), Color.black.opacity(0)], startPoint: .top, endPoint: .bottom)) .frame(height: 8) Spacer() Rectangle() .fill(LinearGradient(colors: [Color.black.opacity(0.45), Color.black.opacity(0)], startPoint: .bottom, endPoint: .top)) .frame(height: 8) } .allowsHitTesting(false) if let disc = playerVM.disc { HSplitView { DiscInfoView( disc: disc, theme: theme, ) .frame(minWidth: 720, alignment: .topLeading) .buttonShadow() .focusable(false) TrackListView( theme: theme, playbackSequence: playerVM.tracksInPlaybackSequence, bonusTrackStartIndex: playerVM.player.disc?.releaseMetadata?.firstBonusTrackIndex, bonusTracksTitle: playerVM.player.disc?.releaseMetadata?.bonusTracksTitle, bonusTracksDisabled: playerVM.bonusTracksDisabled, onBonusIconTap: { playerVM.player.bonusTracksDisabled.toggle() } ) .frame(minWidth: 512) .focusable(false) } } } .background( LinearGradient( colors: [ grayscaleCM.color(for: 0.23), grayscaleCM.color(for: 0.17), ], startPoint: .top, endPoint: .bottom ) ) } } The fix: move the conditional inside the HSplitView, so the split view itself is always present in the hierarchy (swap line 21 and 22 of code above): HSplitView { if let disc = playerVM.disc { DiscInfoView( disc: disc, theme: theme, ) .frame(minWidth: 720, alignment: .topLeading) .buttonShadow() .focusable(false) TrackListView( theme: theme, playbackSequence: playerVM.tracksInPlaybackSequence, bonusTrackStartIndex: playerVM.player.disc?.releaseMetadata?.firstBonusTrackIndex, bonusTracksTitle: playerVM.player.disc?.releaseMetadata?.bonusTracksTitle, bonusTracksDisabled: playerVM.bonusTracksDisabled, onBonusIconTap: { playerVM.player.bonusTracksDisabled.toggle() } ) .frame(minWidth: 512) .focusable(false) } } I asked Claude AI about it and its guess is that conditionally inserting the HSplitView causes its underlying NSSplitView to get torn down and rebuilt on view updates, and the tracking areas responsible for the resize cursor never get a chance to settle. But that would be great to have a confirmation from someone at Apple who knows what's happening under the hood. Is this a known issue or an Expected behavior? Filed as FB22712819 in case anyone wants to duplicate. I posted this in case anyone had the same very specific issue... Cheers!
Topic: UI Frameworks SubTopic: SwiftUI
0
0
95
4d
How can I intercept Shift+Tab in SwiftUI on macOS?
Hi everyone, I'm building a macOS SwiftUI app and I'm trying to intercept both: Tab Shift + Tab to perform custom actions (similar to how text editors indent/outdent items). Right now, plain Tab works fine, but Shift + Tab never reaches my .onKeyPress(.tab) handler. Here's what I'm currently trying: import SwiftUI struct ShiftTabNotIntercepted: View { @State private var shiftKeyPressed = false var body: some View { Text("Hello") .focusable() .onKeyPress(.tab) { print("tab pressed with shift: \(shiftKeyPressed)") return .handled } .onModifierKeysChanged(mask: .shift) { old, new in shiftKeyPressed = new.contains(.shift) } } } Behavior: Pressing Tab prints: tab pressed with shift: false Pressing Shift + Tab does nothing — .onKeyPress(.tab) never fires. I also noticed: if a sidebar is visible, Shift + Tab moves focus to the sidebar if no sidebar is visible, it still doesn't trigger the handler So it seems macOS is intercepting Shift + Tab for focus navigation before SwiftUI sees it. My goal is to fully own this keyboard behavior for a custom outline/tree editor UI. Questions: Is there a SwiftUI-native way to intercept Shift + Tab? Is .onKeyPress fundamentally unable to capture this combination? Do I need to drop down to AppKit (NSViewRepresentable, keyDown, etc.) to reliably handle it? Thanks!
1
0
88
4d
.buttonStyle(.glass) background changes abruptly between 50pt and 51pt in dark mode
[Submitted as FB22612121] A SwiftUI Button using .buttonStyle(.glass) with .buttonBorderShape(.capsule) changes its background abruptly when its size goes from 50×50 to 51×51 points in dark mode. This appears to be a threshold in opacity/material rather than a smooth size-based change. The sample shows identical buttons at 40, 50, 51, and 60 points, with a clear jump between 50 and 51. Measured RGB values shift from 19,19,19 to 30,30,30. The effect also varies with the background, which points to a material/opacity change rather than a fixed fill. ENVIRONMENT iOS 26.4.1 (23E254a) iOS 26.5 (23F5059e) REPRO STEPS Create a new iOS SwiftUI project. Replace ContentView with the sample code below. Run the app or open ContentView in SwiftUI Preview (dark mode). Observe the buttons at 40×40, 50×50, 51×51, and 60×60. Compare the 50pt and 51pt buttons. ACTUAL The background changes abruptly between 50pt and 51pt. The 51pt button uses a noticeably different opacity/material, producing a visible jump in dark mode. EXPECTED The glass background should remain visually consistent or change smoothly as size changes by 1 point. 50pt and 51pt buttons should not have a discontinuous difference. SCREENSHOT SAMPLE CODE struct ContentView: View { private let sizes: [CGFloat] = [40, 50, 51, 60] var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { Text("Glass button dark-mode size jump") .font(.headline) Text("All buttons use .buttonStyle(.glass). Only the label frame changes.") .font(.footnote) .foregroundStyle(.secondary) ForEach(Array(sizes.enumerated()), id: \.offset) { index, size in HStack(spacing: 14) { Button { } label: { Text("\(index + 1)") .font(.system(size: size * 0.42, weight: .medium)) .frame(width: size, height: size) } .buttonStyle(.glass) .buttonBorderShape(.capsule) Text("label frame: \(Int(size)) x \(Int(size))") .font(.callout.monospacedDigit()) .foregroundStyle(.secondary) } } } .padding(24) } .preferredColorScheme(.dark) } }
Topic: UI Frameworks SubTopic: SwiftUI
1
0
188
4d
How to handle all the core AppleEvents
Glancing through the APIs, SwiftUI can handle the open app, reopen app, open doc, and quit core events (with the “aevt” suite ID). What about the print doc and open content events? If there are no hooks (yet), how can I implement them the traditional way without clashing with SwiftUI?
0
0
47
5d
SF Symbols .replace animation is partially missing
In SF Symbols 7, I'm observing a discrepancy between the .replace animation previewed in the SF Symbols app and the result when using the code template the app provides. Expected behavior: When previewing the .replace animation in the SF Symbols app (with Magic Replace preferred), the transition looks like: Actual behavior: When I implement the animation using the exact code template generated by the SF Symbols app, the result looks like: My code: struct PlaygroundSwiftUIView: View { @State var name = "folder.circle" var body: some View { Image(systemName: name) .font(.system(size: 60)) .contentTransition(.symbolEffect(.replace)) .onTapGesture { name = "tree" } } } #Preview { PlaygroundSwiftUIView() } The animation rendered in my app does not match the preview shown in the SF Symbols app. The drawOff animation is partially missing. Is there something missing from the code template, or is there an additional configuration step required to achieve the correct Replace effect? or is this a bug?
1
0
148
5d
iOS 18.6.2 issue with "let"
I am facing an issue in SwiftUI on iOS 18.6.2 where passing a value to a destination view during navigation is not working as expected. In my implementation, I pass a billerId as a constant (let) to the destination view (BillersItemView) using NavigationLink. This approach works correctly across all previous iOS versions. However, on iOS 18.6.2, the destination view does not receive the updated value properly. Before triggering navigation, the value is correctly updated in the ViewModel, but the destination view seems to receive an incorrect or stale value, which affects further API calls and UI rendering. This pattern of passing immutable (let) values is used throughout the app and has always worked reliably, so this behavior appears inconsistent and possibly related to changes in SwiftUI navigation handling in iOS 18.6.2. Could you please confirm if this is a known issue or if there are any recommended changes or workarounds to ensure correct data passing in this scenario? It can be very likely a SwiftUI navigation state timing issue that became more visible in iOS 18.x, especially with NavigationLink(isActive:) + external @ObservedObject updates. But i need to know why this is working in others. I have attached the relevant code for your reference. import SwiftUI struct CategoryBillersView: View { @ObservedObject var billPaymentsVM: BillPaymentsViewModel @State private var goToBillerItem = false var body: some View { NavigationView { VStack { NavigationLink(isActive: $goToBillerItem) { BillersItemView( billPaymentsVM: billPaymentsVM, billerId: billPaymentsVM.selectedBillerId ) } label: { EmptyView() } Button("Go to Biller") { billPaymentsVM.selectedBillerId = 123 goToBillerItem = true } } } } } struct BillersItemView: View { @ObservedObject var billPaymentsVM: BillPaymentsViewModel let billerId: Int var body: some View { Text("Biller ID: \(billerId)") .onAppear { billPaymentsVM.fetchBiller(billerId: billerId) } } } class BillPaymentsViewModel: ObservableObject { @Published var selectedBillerId: Int = 0 }
Topic: UI Frameworks SubTopic: SwiftUI
0
0
48
5d
How to achieve this vibrant background effect on iOS?
Hi everyone, I'm trying to reproduce an effect that Apple has created for the Contacts app on iOS, iPadOS and MacOS v26.4 where the contact card is displayed with a nice material frosted background that blends well with the underlying background. I love this effect and I want to use it in my app to display different information. As you can see in the screenshot, the list rows have a frosted background (probably thinMaterial) combined with a vibrant effect that adapts to the underlying view background. Additionally, the fields labels and icons also have a nice effect. I've tried everything but I'm unable to accurately recreate this effect. I tried applying a Rectangle with a material background listRowBackground but this resulted in something different than what Apple is using (either too light, too dark, or simply not vibrant). I've even tried SwiftUIVisualEffects swift package which can apply a vibrancy effect. However the result is still not the same as the one you see in the screenshot, with the correct contrast between background and labels. Any guidance would be appreciated. Thank you 🙏🏻
4
0
349
6d
Right Way of Positioning an Always Visible Overlay on Top of Navigation Bar That Aligns With Other Navigation Items
Starting with Liquid Glass, the navigation items seem not to be vertically centered within the navigation bar. This makes it very challenging to position an overlay on top of the navigation bar so that it aligns naturally with other elements such as the back button, dismiss button, and others. We want to achieve this for a progress bar so that it remains visible regardless of which views are pushed underneath. Therefore, we cannot add it as a navigation item on each screen, as doing so would cause the progress bar to animate repeatedly as new screens are pushed onto the stack. This used to be easier pre liquid glass since navigation items were centered vertically within the navigation bar. The approach I tried to center the progress bar in the navigation bar: Get access to the top safe area insets through GeometryReader Get access to the the status bar height through UIWindowScene's statusBarManager Subtract status bar height from top safe area inset to calculate the navigation bar height Update the padding of the progress bar accordingly to make sure it is centered within the navigation bar This works, but as I mentioned, now the navigation items are not centered, and the amount of vertical offset they have seems to differ from one screen to another, making it impossible to come up with an additional padding value to align across devices. See how the navigation item looks like within the navigation bar in the view debugger (doesn't matter if it is UINavigationController or NavigationStack the behaviour is the same, also please note that the positioning is the same for a view without an explicit leading toolbar item, where the default back button is provided by the system when a view is pushed): Existing code (without any hacky solutions) to add a progress bar on top as overlay: struct ContentView: View { @State var shouldShowOverlay = false var body: some View { NavigationStack { NavigationLink("Go to View2") { View2() } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button { } label: { Image(systemName: "chevron.left") } } } } .overlay(alignment: .top) { ProgressView(value: 0.5) .frame(width: 200) // what to add as padding here // .padding(.top, 16.0) } } How it looks: Some additional observations for the navigation bar item here: There seems to be 4 _UINavigationBarPlatterAnimationViews in the view stack, prior to the bar button item:
 The first two seems to be fine, they both have (0, 0, 44, 44) for both frame and bounds
 The third one’s frame has height and width of 48.2, and x, y values of -2.1. The last one’s frame has 40,17 for height and width and 1.92 for x,y values. Both views have the following bounds: (0, 0, 44, 44). I also tried to access to the origin of the back bar button item so that I could calculate where the position the overlay, but that also didn't yield to something useful, not to mention it would also be a super hacky solution. So my ultimate question is, is there a clean way to position an overlay on top of the navigation bar that vertically aligns with other navigation bar items, or should we just position it elsewhere and do not mess with navigation bar anymore? Any input would be greatly appreciated.
0
1
136
6d
apply .activityBackgroundTint modifier in smartstack
On iOS 26, when using .activityBackgroundTint modifiers to change the background color in the Activity Kit, the changes do not appear to apply correctly in Smart Stack. While the color changes as expected, the background is rendered as simply transparent without a glass effect which reduces readability. The glass effect is applied only when activityBackgroundTint is not used.
0
0
155
1w
Having trouble with RawRespresentable "Expected to decode String but found a dictionary instead."
I want to use AppStorage for a custom struct I am using struct Activities { var name: String var age: Int } struct ContentView: View { @AppStorage("key") private var activities: Activities = .init(name: "Albert", age: 42) var body: some View { VStack { TextField("Activity Name", text: $activities.name) } } } The above code generates a compiler warning, recommending I add RawRepresentable conformance. So I've added it like this: extension Activities: RawRepresentable { public init?(rawValue: String) { guard let data = rawValue.data(using: .utf8) else { return nil } do { let result = try JSONDecoder().decode(Activities.self, from: data) self = result } catch { print(error) return nil } } var rawValue: String { guard let data = try? JSONEncoder().encode(self), let result = String(data: data, encoding: .utf8) else { return "{}" } return result } } This leads to a stack overflow because calling encode from rawValue calls rawValue. :-( I overcame this by declaring Codable conformance and overriding the default Encodable implementation: extension Activities: Codable { enum CodingKeys: String, CodingKey { case name case age } func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(age, forKey: .age) } } This solves the stack overflow, but now init?(rawValue: String) is failing and I'm not sure why. When I set a breakpoint in my catch block I see the following: (lldb) po error ▿ DecodingError ▿ typeMismatch : 2 elements - .0 : Swift.String ▿ .1 : Context - codingPath : 0 elements - debugDescription : "Expected to decode String but found a dictionary instead." - underlyingError : nil (lldb) po rawValue {"name":"Albert2","age":42} (lldb) po data ▿ 27 bytes - count : 27 ▿ bytes : 27 elements - 0 : 123 - 1 : 34 - 2 : 110 - 3 : 97 - 4 : 109 - 5 : 101 - 6 : 34 - 7 : 58 - 8 : 34 (truncated to save space for posting :-)
2
0
393
1w
SwiftUI Text rendering with too small height / one line missing causing unexpected text truncation on iPhone devices
FB: FB22577211 The following trivial SwiftUI Text rendering causes wrong text layout and truncated text. The text should take the required height to render the text without truncation. Adding fixedSize does also not solve this. This bug only happens on devices and not on the simulator. Confirmed with iPhone 15 and iOS 26.4.1 but my colleague used another iPhone so it’s multiple iPhone devices. import SwiftUI let txt = """ Es sollte die erste Japan-Tournee von vielen werden, kein anderes Land – abgesehen von Österreich und der Schweiz – bereisten die Berliner Philharmoniker häufiger. Wie kam es zu dem überschäumend herzlichen Empfang, der dem Orchester bei seinem ersten Gastspiel in Tokio bereitet wurde und wie wurde das Land zu einer »zweiten Heimat« für die Berliner? Ein konkreter historischer Grundstein für das hohe Ansehen klassischer Musik »made in Germany« in Japan wurde bereits im 19. Jahrhunderts gelegt: Als Teil von umfassenden gesellschaftlichen Modernisierungsmaßnahmen vergab die Regierung ab 1868 Stipendien an junge japanische Intellektuelle, damit diese an den besten internationalen Instituten studieren konnten. Berlin wurde – neben Wien – als globales Zentrum der Musik betrachtet, und so erhielten viele japanische Studierende um die Jahrhundertwende die Gelegenheit, von Komponisten wie etwa Max Bruch zu lernen. Zurück in der Heimat, teilten sie ihre Begeisterung für die europäische Kunstmusik sowie das Wissen um die instrumentale und kompositorische Praxis der klassisch-romantischen Tradition. """ struct ContentView: View { var body: some View { VStack { Text(txt) } .padding(.leading, 20) .padding(.trailing, 20) .frame(maxWidth: .infinity) } } This is also enough: Text(txt) .padding(.horizontal, 20) .fixedSize(horizontal: false, vertical: true) Expected: Text is rendered without truncation / ellipsis. Actual: Text is rendered with too small height / missing one line so it’s truncated / with ellipsis.
10
0
556
1w
tabViewBottomAccessory does not inherit tab bar vibrancy / foregroundStyle behavior
Hi, I’m experimenting with the new tabViewBottomAccessory API and I’m running into an inconsistency with how colors are handled compared to the native tab bar items. Context When using a TabView, the system tab bar automatically adapts its foreground color (icons and labels) based on the background content (light/dark, images, etc.). This is the expected “vibrancy” behavior. However, when adding content via .tabViewBottomAccessory, the foreground color does not seem to follow the same logic. Minimal example : import SwiftUI struct ContentView: View { var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea() } Tab("New", systemImage: "squareshape.split.2x2") { Text("New") .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundStyle(.white) .background(Color.black) .ignoresSafeArea() } } .tabViewBottomAccessory { HStack { Image(systemName: "command.square") Text("Message test") Spacer() Image(systemName: "play.fill") } .padding(.horizontal, 20) .foregroundStyle(.primary) } } } Observed behavior Tab bar icons and labels correctly switch between dark and light depending on the background. Content inside .tabViewBottomAccessory remains black, even when .foregroundStyle(.primary) is used. Expected behavior I would expect .tabViewBottomAccessory content to: Either inherit the same vibrancy/foreground adaptation as the tab bar items Or have a documented way to match that behavior I want to reuse the Apple Music Navigation
Topic: UI Frameworks SubTopic: SwiftUI
0
0
71
1w
Charts performance issue
Hi, I want to recreate a chart from Apple Health and I have code like this. When I scroll - especially the week and month charts, there are performance issues. If I remove .chartScrollPosition(x: $scrollChartPosition), it runs smoothly, but I need to know which part of the chart is currently displayed. Can you help me? import Charts import SwiftUI struct MacroChartView: View { var selectedRange: ChartRange var binnedPoints: [MacroBinPoint] @State private var scrollChartPosition: Date = .now var body: some View { VStack { Text("\(selectedRange.rangeLabel(for: scrollChartPosition))") Chart(binnedPoints) { point in BarMark( x: .value("Date", point.date, unit: selectedRange.binComponent), y: .value("Calories", point.calories) ) } .frame(height: 324) .chartXVisibleDomain(length: selectedRange.visibleDomainLength()) .chartScrollableAxes(.horizontal) .chartScrollPosition(x: $scrollChartPosition) .chartScrollTargetBehavior(.valueAligned(matching: selectedRange.scrollAlignmentComponents)) .chartXAxis { switch selectedRange { case .week: AxisMarks(values: .stride(by: .day)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.weekday(.abbreviated)) } case .month: AxisMarks(values: .stride(by: .weekOfYear)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.day()) } case .halfYear: AxisMarks(values: .stride(by: .month)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.month(.abbreviated)) } case .year: AxisMarks(values: .stride(by: .month)) { date in AxisGridLine() AxisTick() AxisValueLabel(format: .dateTime.month(.abbreviated)) } } } } } } enum MeasurementHistoryMode { case macros case comparisons } enum MacroKindToDisplay { case protein, fat, carbs } enum MacrosDisplayMode: Equatable { case all case single(MacroKindToDisplay) } enum ChartRange: String, CaseIterable { case week = "T" case month = "M" case halfYear = "6M" case year = "R" var binComponent: Calendar.Component { switch self { case .week, .month: return .day case .halfYear: return .weekOfYear case .year: return .month } } var scrollAlignmentComponents: DateComponents { switch self { case .week: return DateComponents(hour: 0, minute: 0, second: 0) case .month: return DateComponents(hour: 0) case .halfYear: return DateComponents(weekday: 1) case .year: return DateComponents(day: 1) } } func visibleDomainLength() -> Int { switch self { case .week: return 7 * 24 * 60 * 60 case .month: return 31 * 24 * 60 * 60 case .halfYear: return 6 * 31 * 24 * 60 * 60 case .year: return 12 * 31 * 24 * 60 * 60 } } func start(for date: Date) -> Date { let cal = Calendar.current switch self { case .week, .month: return cal.startOfDay(for: date) case .halfYear: return cal.dateInterval(of: .weekOfYear, for: date)?.start ?? cal.startOfDay(for: date) case .year: return cal.dateInterval(of: .month, for: date)?.start ?? cal.startOfDay(for: date) } } func rangeLabel(for start: Date) -> String { let end = start.addingTimeInterval(TimeInterval(visibleDomainLength())) let f = DateFormatter() f.dateFormat = Calendar.current.isDate(start, inSameDayAs: end) ? "MMM d" : "MMM d" return Calendar.current.isDate(start, inSameDayAs: end) ? f.string(from: start) : "\(f.string(from: start)) – \(f.string(from: end))" } } struct MacrosPoint: Identifiable { var id: Date { date } let date: Date let calories: Double let proteinInGrams: Double let carbsInGrams: Double let fatInGrams: Double } struct MacroBinPoint: Identifiable { var id: Date { date } let date: Date let calories: Double let proteinKcal: Double let carbsKcal: Double let fatKcal: Double } func bin(points: [MacrosPoint], for period: ChartRange) -> [MacroBinPoint] { let grouped = Dictionary(grouping: points) { point in period.start(for: point.date) } let bins = grouped.map { (start, items) -> MacroBinPoint in var calories = items.reduce(0) { $0 + $1.calories } var proteinKcal = items.reduce(0) { $0 + $1.proteinInGrams * 4 } var carbsKcal = items.reduce(0) { $0 + $1.carbsInGrams * 4 } var fatKcal = items.reduce(0) { $0 + $1.fatInGrams * 9 } calories /= Double(items.count) proteinKcal /= Double(items.count) carbsKcal /= Double(items.count) fatKcal /= Double(items.count) return MacroBinPoint(date: start, calories: calories, proteinKcal: proteinKcal, carbsKcal: carbsKcal, fatKcal: fatKcal) } .sorted { $0.date < $1.date } return bins } struct ExampleData { static let macrosPoints: [MacrosPoint] = [ MacrosPoint(date: Date(timeIntervalSince1970: 1687949774), calories: 1895, proteinInGrams: 115, carbsInGrams: 192, fatInGrams: 72),... ]
Replies
2
Boosts
1
Views
432
Activity
1d
Programatically adding to a TextField and moving the TextSelection point in SwiftUI
Hi! I am trying to create a simple SwiftUI TextField, with an external button to add text to the field at the current insertion point (the cursor in the TextField). When I add the text, the cursor (I-Beam) remains at the original insertion point, so I want it to move over to the end of what I added. The trouble is, it sometimes moves further forward or to the end (visibly) but works as if it is still at the point I moved it to. This seems to possibly be due to emojis in the TextField (because, I assume, they are composed of more bytes). Further, sometimes the addition of the text can cause an emoji to appear unexpectedly, I assume because it is combining the bytes in an odd way. So moving the cursor seems to sometimes introduce weird behaviour. This comes from a much larger project, but I have distilled this down to the smallest example project I could. And I have a video to show how it behaves. Here's the main part of the code, and I'll attach an Xcode project: import SwiftUI struct ContentView: View { @State private var text: String = "abcdef🧁🧁🧁🧁ghijkl" @State private var selectedText: TextSelection? var body: some View { VStack { TextField("", text: $text, selection: $selectedText) .font(.title) Button("Add Z at Insertion Point in TextField") { // Get indices of any selection in the text field let from: String.Index, to: String.Index if let selectedText = selectedText { let indices = selectedText.indices switch indices { case .selection(let range): from = range.lowerBound to = range.upperBound case .multiSelection(let rangeSet): from = rangeSet.ranges.first!.lowerBound to = rangeSet.ranges.first!.upperBound default: from = self.text.endIndex to = self.text.endIndex } } else { from = self.text.endIndex to = self.text.endIndex } guard from <= to && from <= self.text.endIndex else { return } // Insert and update the cursor position self.text.replaceSubrange(from..<to, with: "Z") // Move cursor after the inserted character let newIndex = self.text.index(after: from) selectedText = TextSelection(insertionPoint: newIndex) } } .padding() } } STEPS TO REPRODUCE Run the app. Also view the video as it shows the steps. Put insertion point between c and d. Press the "Add Z" button. Note that Z is placed between c and d. This is great. Put insertion point between h and i. Press the "Add Z" button. Note that Z is placed between h and i. BUT, the insertion point I-beam moves to the end of the string. Press the "Add Z" button again. Z is added where you would have expected based on where the TextSelection insertion point was put, but the flashing I-Beam is still at the end. Press the "Add Z" button again. Same issue. insertion point is being shown at end, but to the button it is between Z and i. OF NOTE, if you use the keyboard and press delete, it deletes from end (where the I-beam is). Now put insertion point between the 4 cupcakes. Press "Add Z" two times. It behaves correctly. Press "Add Z" a third time. It adds a fairy emoji. So, any idea what I am doing wrong? I thought it might be an issue requiring me to update in a background thread, but I tried that, even delaying the update in the thread, but the issue remains. Thanks in advance. Here's a video: https://curmi.name/temp/SimpleTextField%20showing%20issues.mp4 And if it helps, here is the Xcode project: https://curmi.name/temp/SimpleTextfield.zip
Replies
0
Boosts
0
Views
168
Activity
1d
error: A NavigationLink is presenting a value of type “String” but there is no matching navigationDestination
please help, i am new to SWIFTUI getting an error: A NavigationLink is presenting a value of type “String” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated. struct debug_navigation_3: View { @State private var path = NavigationPath() @StateObject private var debug_Client_Lead_Data_ListVM = Debug_Client_Lead_Data_List_Model() var body: some View { NavigationStack(path:$path) { VStack { List(debug_Client_Lead_Data_ListVM.Debug_Client_Lead_Data_Rows, id: \.self) { curr_clientLeadRequest in NavigationLink(curr_clientLeadRequest.Client_Message, value: curr_clientLeadRequest.Client_Message) } } .navigationDestination(for:Debug_Client_Lead_Data.self) { curr_clientLeadRequest in debug_navigation_3_DetailView(rec_id: Int64(curr_clientLeadRequest.id)) } } .onAppear() { Task { await debug_Client_Lead_Data_ListVM.search(rec_id:Int64(0)) //bring all REST API } } } } // data model import Foundation struct Debug_Client_Lead_Data_Response: Decodable { let Debug_Client_Lead_Data_Rows: [Debug_Client_Lead_Data] private enum CodingKeys: String, CodingKey { case Debug_Client_Lead_Data_Rows = "rows" // root tag: REST API } } struct Debug_Client_Lead_Data: Decodable, Hashable, Identifiable { let id:Int32 let Client_Message: String private enum CodingKeys: String, CodingKey { case id = "id" case Client_Message = "Client_Message" } } //list view model import Foundation @MainActor class Debug_Client_Lead_Data_List_Model: ObservableObject { @Published var Debug_Client_Lead_Data_Rows: [Debug_Client_Lead_Data_ListViewModel] = [] func search(rec_id:Int64) async { do { let Debug_Client_Lead_Data_Rows = try await Webservice_debug_client_lead_data().getClientLeadRequestSummary(rec_id:rec_id) // '0' optional self.Debug_Client_Lead_Data_Rows = Debug_Client_Lead_Data_Rows.map(Debug_Client_Lead_Data_ListViewModel.init) } catch { print(error) } } } struct Debug_Client_Lead_Data_ListViewModel: Identifiable, Hashable { let debug_Client_Lead_Data: Debug_Client_Lead_Data var id:Int32 { debug_Client_Lead_Data.id } var Client_Message: String { debug_Client_Lead_Data.Client_Message } } //REST API import Foundation class Webservice_debug_client_lead_data { func getClientLeadRequestSummary(rec_id:Int64) async throws -> [Debug_Client_Lead_Data] { var components = URLComponents() components.scheme = Global_REST_API_URL_HTTP components.host = Global_REST_API_URL components.port = Global_REST_API_URL_port components.path = "/GetClientLeadRequest" components.queryItems = [ URLQueryItem(name: "rec_id", value: String(rec_id)) // Optional, pass '0' for all rows ] guard let url = components.url else { throw NetworkError.badURL } let (data, response) = try await URLSession.shared.data(from: url) guard (response as? HTTPURLResponse)?.statusCode == 200 else {throw NetworkError.badID } let Debug_Client_Lead_Data_Response = try? JSONDecoder().decode(Debug_Client_Lead_Data_Response.self, from: data) return Debug_Client_Lead_Data_Response?.Debug_Client_Lead_Data_Rows ?? [] } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
156
Activity
2d
Why doesn't .glassEffect tint render on a Menu in an iOS 26 toolbar?
This is what I am trying to achieve (from the Phone app, similar one is also in Photos) I have a standard SwiftUI Menu in a toolbar with a glass tint applied to indicate the filter is active: Menu { // …filter options } label: { Label("Filter", systemImage: "line.3.horizontal.decrease") } .glassEffect(.regular.tint(.accentColor).interactive()) The glass effect doesn't render at all, no tint. The button looks completely unstyled. If I switch the label from Label to Image, the glass renders, but as a stretched oblong pill. I have tried several other combinations too: Also in the Apple's version during hover (iPad) the highlight aligns with the tint itself (see second image above) rather than outside it like in example 3 from the list above: Is there a way to get a Menu's trigger inset tint to look as in the Phone app example?
Replies
0
Boosts
0
Views
37
Activity
3d
"any View cannot conform to View"
Hello, What is the solution to this problem, assuming that you are not supposed to use AnyView, which I hear over and over again? Given this protocol: protocol MyProtocol { associatedtype V: View var content: () -> V { get } } if I want to store a heterogenous collection of MyProtocol, let collection: [any MyProtocol], then the underlying V type of each is erased to any View, which (supposedly) does not conform to View. Is there no way to "unbox" the existential any View to get the underlying View back? That unboxing idea is something I heard in a few WWDC videos but it seems like it does not apply to View.
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
37
Activity
3d
SwiftUI NavigationSplitView sidebar toolbar has excessive top inset when embedded in TabView since iPadOS 26.4
I’m seeing a layout regression in SwiftUI on iPadOS 26.4 involving NavigationSplitView inside a TabView. When a NavigationSplitView is embedded in a TabView, the sidebar toolbar appears to reserve too much vertical space. There is a large vertical gap between the top edge of the sidebar and the sidebar collapse/toggle icon. It looks as if the sidebar toolbar itself has become much taller than expected. The same NavigationSplitView layout is rendered correctly when it is shown directly without being embedded in a TabView. Environment: iPadOS 26.4 or later SwiftUI iPad TabView NavigationSplitView inside one tab Expected behavior The sidebar toolbar should use its normal height, as it does when the same NavigationSplitView is shown without a surrounding TabView. The sidebar collapse/toggle icon should appear close to the top of the sidebar, without a large empty gap above it. Actual behavior When the NavigationSplitView is hosted inside a TabView, the sidebar toolbar area becomes excessively tall. A large empty space appears above the sidebar collapse/toggle icon. This only happens in the TabView setup. Rendering the same NavigationSplitView directly does not show the issue. Feedback I also filed this as Feedback Assistant report: FB22645938 Has anyone else seen this behavior since iPadOS 26.4? Is this an intentional layout change, or is there a supported way to avoid this additional top inset when using NavigationSplitView inside TabView? Reproduction import SwiftUI struct ContentView: View { enum AppTab { case first case second } @State private var selectedTab: AppTab = .first var body: some View { TabView(selection: $selectedTab) { Tab("First", systemImage: "sidebar.leading", value: .first) { NavigationSplitView { List { Section("Sidebar Content") { ForEach(1...20, id: \.self) { index in Text("Item \(index)") } } } .navigationTitle("Sidebar") .toolbar { ToolbarItem(placement: .topBarLeading) { Button { // action } label: { Image(systemName: "plus") } } } } detail: { Text("Detail") } } Tab("Second", systemImage: "doc", value: .second) { Text("Second tab") } } } }
Replies
0
Boosts
0
Views
101
Activity
3d
HSplitView resize cursor missing when split view is conditionally rendered (FB22712819)
Hey all, Ran into a fun one on macOS 26.4.1 / Xcode 26.4.1, and I think I've narrowed it down enough to be worth sharing. Symptom: HSplitView's divider drags fine, but the cursor never changes to the resize cursor on hover. Just stays as the regular arrow. The cause (in my case): the HSplitView was conditionally rendered via an outer if let: struct PlayerDetailsView: View { let theme: Theme var playerVM = PlayerViewModel.shared var body: some View { let grayscaleCM = ColorMap(table: .grayscale) ZStack { VStack(spacing: 0) { Rectangle() .fill(LinearGradient(colors: [Color.black.opacity(0.45), Color.black.opacity(0)], startPoint: .top, endPoint: .bottom)) .frame(height: 8) Spacer() Rectangle() .fill(LinearGradient(colors: [Color.black.opacity(0.45), Color.black.opacity(0)], startPoint: .bottom, endPoint: .top)) .frame(height: 8) } .allowsHitTesting(false) if let disc = playerVM.disc { HSplitView { DiscInfoView( disc: disc, theme: theme, ) .frame(minWidth: 720, alignment: .topLeading) .buttonShadow() .focusable(false) TrackListView( theme: theme, playbackSequence: playerVM.tracksInPlaybackSequence, bonusTrackStartIndex: playerVM.player.disc?.releaseMetadata?.firstBonusTrackIndex, bonusTracksTitle: playerVM.player.disc?.releaseMetadata?.bonusTracksTitle, bonusTracksDisabled: playerVM.bonusTracksDisabled, onBonusIconTap: { playerVM.player.bonusTracksDisabled.toggle() } ) .frame(minWidth: 512) .focusable(false) } } } .background( LinearGradient( colors: [ grayscaleCM.color(for: 0.23), grayscaleCM.color(for: 0.17), ], startPoint: .top, endPoint: .bottom ) ) } } The fix: move the conditional inside the HSplitView, so the split view itself is always present in the hierarchy (swap line 21 and 22 of code above): HSplitView { if let disc = playerVM.disc { DiscInfoView( disc: disc, theme: theme, ) .frame(minWidth: 720, alignment: .topLeading) .buttonShadow() .focusable(false) TrackListView( theme: theme, playbackSequence: playerVM.tracksInPlaybackSequence, bonusTrackStartIndex: playerVM.player.disc?.releaseMetadata?.firstBonusTrackIndex, bonusTracksTitle: playerVM.player.disc?.releaseMetadata?.bonusTracksTitle, bonusTracksDisabled: playerVM.bonusTracksDisabled, onBonusIconTap: { playerVM.player.bonusTracksDisabled.toggle() } ) .frame(minWidth: 512) .focusable(false) } } I asked Claude AI about it and its guess is that conditionally inserting the HSplitView causes its underlying NSSplitView to get torn down and rebuilt on view updates, and the tracking areas responsible for the resize cursor never get a chance to settle. But that would be great to have a confirmation from someone at Apple who knows what's happening under the hood. Is this a known issue or an Expected behavior? Filed as FB22712819 in case anyone wants to duplicate. I posted this in case anyone had the same very specific issue... Cheers!
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
95
Activity
4d
Disabling font Anti-Aliasing in a Text in SwiftUI
Hi, I'm using one ttf font that simulate a bitmap look in my app. However, macOS renders all font with anti-aliasing. On those kind of font, that introduce some artefacts. Is there a way to disable anti-aliasing or some tricks that would make like there were no anti-aliasing? Thanks.
Replies
7
Boosts
0
Views
507
Activity
4d
How can I intercept Shift+Tab in SwiftUI on macOS?
Hi everyone, I'm building a macOS SwiftUI app and I'm trying to intercept both: Tab Shift + Tab to perform custom actions (similar to how text editors indent/outdent items). Right now, plain Tab works fine, but Shift + Tab never reaches my .onKeyPress(.tab) handler. Here's what I'm currently trying: import SwiftUI struct ShiftTabNotIntercepted: View { @State private var shiftKeyPressed = false var body: some View { Text("Hello") .focusable() .onKeyPress(.tab) { print("tab pressed with shift: \(shiftKeyPressed)") return .handled } .onModifierKeysChanged(mask: .shift) { old, new in shiftKeyPressed = new.contains(.shift) } } } Behavior: Pressing Tab prints: tab pressed with shift: false Pressing Shift + Tab does nothing — .onKeyPress(.tab) never fires. I also noticed: if a sidebar is visible, Shift + Tab moves focus to the sidebar if no sidebar is visible, it still doesn't trigger the handler So it seems macOS is intercepting Shift + Tab for focus navigation before SwiftUI sees it. My goal is to fully own this keyboard behavior for a custom outline/tree editor UI. Questions: Is there a SwiftUI-native way to intercept Shift + Tab? Is .onKeyPress fundamentally unable to capture this combination? Do I need to drop down to AppKit (NSViewRepresentable, keyDown, etc.) to reliably handle it? Thanks!
Replies
1
Boosts
0
Views
88
Activity
4d
.buttonStyle(.glass) background changes abruptly between 50pt and 51pt in dark mode
[Submitted as FB22612121] A SwiftUI Button using .buttonStyle(.glass) with .buttonBorderShape(.capsule) changes its background abruptly when its size goes from 50×50 to 51×51 points in dark mode. This appears to be a threshold in opacity/material rather than a smooth size-based change. The sample shows identical buttons at 40, 50, 51, and 60 points, with a clear jump between 50 and 51. Measured RGB values shift from 19,19,19 to 30,30,30. The effect also varies with the background, which points to a material/opacity change rather than a fixed fill. ENVIRONMENT iOS 26.4.1 (23E254a) iOS 26.5 (23F5059e) REPRO STEPS Create a new iOS SwiftUI project. Replace ContentView with the sample code below. Run the app or open ContentView in SwiftUI Preview (dark mode). Observe the buttons at 40×40, 50×50, 51×51, and 60×60. Compare the 50pt and 51pt buttons. ACTUAL The background changes abruptly between 50pt and 51pt. The 51pt button uses a noticeably different opacity/material, producing a visible jump in dark mode. EXPECTED The glass background should remain visually consistent or change smoothly as size changes by 1 point. 50pt and 51pt buttons should not have a discontinuous difference. SCREENSHOT SAMPLE CODE struct ContentView: View { private let sizes: [CGFloat] = [40, 50, 51, 60] var body: some View { ScrollView { VStack(alignment: .leading, spacing: 16) { Text("Glass button dark-mode size jump") .font(.headline) Text("All buttons use .buttonStyle(.glass). Only the label frame changes.") .font(.footnote) .foregroundStyle(.secondary) ForEach(Array(sizes.enumerated()), id: \.offset) { index, size in HStack(spacing: 14) { Button { } label: { Text("\(index + 1)") .font(.system(size: size * 0.42, weight: .medium)) .frame(width: size, height: size) } .buttonStyle(.glass) .buttonBorderShape(.capsule) Text("label frame: \(Int(size)) x \(Int(size))") .font(.callout.monospacedDigit()) .foregroundStyle(.secondary) } } } .padding(24) } .preferredColorScheme(.dark) } }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
1
Boosts
0
Views
188
Activity
4d
Prominent glass button in SwiftUI incorrect text style
How do you create a prominent glass button in SwiftUI? In UIKit it’s let button = UIButton(configuration: .prominentGlass()) button.configuration?.title = "Agree" I tried Button("Agree") {} .buttonStyle(.glassProminent) but the title text is white not glassified 🤨
Replies
0
Boosts
0
Views
57
Activity
4d
How to handle all the core AppleEvents
Glancing through the APIs, SwiftUI can handle the open app, reopen app, open doc, and quit core events (with the “aevt” suite ID). What about the print doc and open content events? If there are no hooks (yet), how can I implement them the traditional way without clashing with SwiftUI?
Replies
0
Boosts
0
Views
47
Activity
5d
SF Symbols .replace animation is partially missing
In SF Symbols 7, I'm observing a discrepancy between the .replace animation previewed in the SF Symbols app and the result when using the code template the app provides. Expected behavior: When previewing the .replace animation in the SF Symbols app (with Magic Replace preferred), the transition looks like: Actual behavior: When I implement the animation using the exact code template generated by the SF Symbols app, the result looks like: My code: struct PlaygroundSwiftUIView: View { @State var name = "folder.circle" var body: some View { Image(systemName: name) .font(.system(size: 60)) .contentTransition(.symbolEffect(.replace)) .onTapGesture { name = "tree" } } } #Preview { PlaygroundSwiftUIView() } The animation rendered in my app does not match the preview shown in the SF Symbols app. The drawOff animation is partially missing. Is there something missing from the code template, or is there an additional configuration step required to achieve the correct Replace effect? or is this a bug?
Replies
1
Boosts
0
Views
148
Activity
5d
iOS 18.6.2 issue with "let"
I am facing an issue in SwiftUI on iOS 18.6.2 where passing a value to a destination view during navigation is not working as expected. In my implementation, I pass a billerId as a constant (let) to the destination view (BillersItemView) using NavigationLink. This approach works correctly across all previous iOS versions. However, on iOS 18.6.2, the destination view does not receive the updated value properly. Before triggering navigation, the value is correctly updated in the ViewModel, but the destination view seems to receive an incorrect or stale value, which affects further API calls and UI rendering. This pattern of passing immutable (let) values is used throughout the app and has always worked reliably, so this behavior appears inconsistent and possibly related to changes in SwiftUI navigation handling in iOS 18.6.2. Could you please confirm if this is a known issue or if there are any recommended changes or workarounds to ensure correct data passing in this scenario? It can be very likely a SwiftUI navigation state timing issue that became more visible in iOS 18.x, especially with NavigationLink(isActive:) + external @ObservedObject updates. But i need to know why this is working in others. I have attached the relevant code for your reference. import SwiftUI struct CategoryBillersView: View { @ObservedObject var billPaymentsVM: BillPaymentsViewModel @State private var goToBillerItem = false var body: some View { NavigationView { VStack { NavigationLink(isActive: $goToBillerItem) { BillersItemView( billPaymentsVM: billPaymentsVM, billerId: billPaymentsVM.selectedBillerId ) } label: { EmptyView() } Button("Go to Biller") { billPaymentsVM.selectedBillerId = 123 goToBillerItem = true } } } } } struct BillersItemView: View { @ObservedObject var billPaymentsVM: BillPaymentsViewModel let billerId: Int var body: some View { Text("Biller ID: \(billerId)") .onAppear { billPaymentsVM.fetchBiller(billerId: billerId) } } } class BillPaymentsViewModel: ObservableObject { @Published var selectedBillerId: Int = 0 }
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
48
Activity
5d
How to achieve this vibrant background effect on iOS?
Hi everyone, I'm trying to reproduce an effect that Apple has created for the Contacts app on iOS, iPadOS and MacOS v26.4 where the contact card is displayed with a nice material frosted background that blends well with the underlying background. I love this effect and I want to use it in my app to display different information. As you can see in the screenshot, the list rows have a frosted background (probably thinMaterial) combined with a vibrant effect that adapts to the underlying view background. Additionally, the fields labels and icons also have a nice effect. I've tried everything but I'm unable to accurately recreate this effect. I tried applying a Rectangle with a material background listRowBackground but this resulted in something different than what Apple is using (either too light, too dark, or simply not vibrant). I've even tried SwiftUIVisualEffects swift package which can apply a vibrancy effect. However the result is still not the same as the one you see in the screenshot, with the correct contrast between background and labels. Any guidance would be appreciated. Thank you 🙏🏻
Replies
4
Boosts
0
Views
349
Activity
6d
Right Way of Positioning an Always Visible Overlay on Top of Navigation Bar That Aligns With Other Navigation Items
Starting with Liquid Glass, the navigation items seem not to be vertically centered within the navigation bar. This makes it very challenging to position an overlay on top of the navigation bar so that it aligns naturally with other elements such as the back button, dismiss button, and others. We want to achieve this for a progress bar so that it remains visible regardless of which views are pushed underneath. Therefore, we cannot add it as a navigation item on each screen, as doing so would cause the progress bar to animate repeatedly as new screens are pushed onto the stack. This used to be easier pre liquid glass since navigation items were centered vertically within the navigation bar. The approach I tried to center the progress bar in the navigation bar: Get access to the top safe area insets through GeometryReader Get access to the the status bar height through UIWindowScene's statusBarManager Subtract status bar height from top safe area inset to calculate the navigation bar height Update the padding of the progress bar accordingly to make sure it is centered within the navigation bar This works, but as I mentioned, now the navigation items are not centered, and the amount of vertical offset they have seems to differ from one screen to another, making it impossible to come up with an additional padding value to align across devices. See how the navigation item looks like within the navigation bar in the view debugger (doesn't matter if it is UINavigationController or NavigationStack the behaviour is the same, also please note that the positioning is the same for a view without an explicit leading toolbar item, where the default back button is provided by the system when a view is pushed): Existing code (without any hacky solutions) to add a progress bar on top as overlay: struct ContentView: View { @State var shouldShowOverlay = false var body: some View { NavigationStack { NavigationLink("Go to View2") { View2() } .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { Button { } label: { Image(systemName: "chevron.left") } } } } .overlay(alignment: .top) { ProgressView(value: 0.5) .frame(width: 200) // what to add as padding here // .padding(.top, 16.0) } } How it looks: Some additional observations for the navigation bar item here: There seems to be 4 _UINavigationBarPlatterAnimationViews in the view stack, prior to the bar button item:
 The first two seems to be fine, they both have (0, 0, 44, 44) for both frame and bounds
 The third one’s frame has height and width of 48.2, and x, y values of -2.1. The last one’s frame has 40,17 for height and width and 1.92 for x,y values. Both views have the following bounds: (0, 0, 44, 44). I also tried to access to the origin of the back bar button item so that I could calculate where the position the overlay, but that also didn't yield to something useful, not to mention it would also be a super hacky solution. So my ultimate question is, is there a clean way to position an overlay on top of the navigation bar that vertically aligns with other navigation bar items, or should we just position it elsewhere and do not mess with navigation bar anymore? Any input would be greatly appreciated.
Replies
0
Boosts
1
Views
136
Activity
6d
apply .activityBackgroundTint modifier in smartstack
On iOS 26, when using .activityBackgroundTint modifiers to change the background color in the Activity Kit, the changes do not appear to apply correctly in Smart Stack. While the color changes as expected, the background is rendered as simply transparent without a glass effect which reduces readability. The glass effect is applied only when activityBackgroundTint is not used.
Replies
0
Boosts
0
Views
155
Activity
1w
Having trouble with RawRespresentable "Expected to decode String but found a dictionary instead."
I want to use AppStorage for a custom struct I am using struct Activities { var name: String var age: Int } struct ContentView: View { @AppStorage("key") private var activities: Activities = .init(name: "Albert", age: 42) var body: some View { VStack { TextField("Activity Name", text: $activities.name) } } } The above code generates a compiler warning, recommending I add RawRepresentable conformance. So I've added it like this: extension Activities: RawRepresentable { public init?(rawValue: String) { guard let data = rawValue.data(using: .utf8) else { return nil } do { let result = try JSONDecoder().decode(Activities.self, from: data) self = result } catch { print(error) return nil } } var rawValue: String { guard let data = try? JSONEncoder().encode(self), let result = String(data: data, encoding: .utf8) else { return "{}" } return result } } This leads to a stack overflow because calling encode from rawValue calls rawValue. :-( I overcame this by declaring Codable conformance and overriding the default Encodable implementation: extension Activities: Codable { enum CodingKeys: String, CodingKey { case name case age } func encode(to encoder: any Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name) try container.encode(age, forKey: .age) } } This solves the stack overflow, but now init?(rawValue: String) is failing and I'm not sure why. When I set a breakpoint in my catch block I see the following: (lldb) po error ▿ DecodingError ▿ typeMismatch : 2 elements - .0 : Swift.String ▿ .1 : Context - codingPath : 0 elements - debugDescription : "Expected to decode String but found a dictionary instead." - underlyingError : nil (lldb) po rawValue {"name":"Albert2","age":42} (lldb) po data ▿ 27 bytes - count : 27 ▿ bytes : 27 elements - 0 : 123 - 1 : 34 - 2 : 110 - 3 : 97 - 4 : 109 - 5 : 101 - 6 : 34 - 7 : 58 - 8 : 34 (truncated to save space for posting :-)
Replies
2
Boosts
0
Views
393
Activity
1w
SwiftUI Text rendering with too small height / one line missing causing unexpected text truncation on iPhone devices
FB: FB22577211 The following trivial SwiftUI Text rendering causes wrong text layout and truncated text. The text should take the required height to render the text without truncation. Adding fixedSize does also not solve this. This bug only happens on devices and not on the simulator. Confirmed with iPhone 15 and iOS 26.4.1 but my colleague used another iPhone so it’s multiple iPhone devices. import SwiftUI let txt = """ Es sollte die erste Japan-Tournee von vielen werden, kein anderes Land – abgesehen von Österreich und der Schweiz – bereisten die Berliner Philharmoniker häufiger. Wie kam es zu dem überschäumend herzlichen Empfang, der dem Orchester bei seinem ersten Gastspiel in Tokio bereitet wurde und wie wurde das Land zu einer »zweiten Heimat« für die Berliner? Ein konkreter historischer Grundstein für das hohe Ansehen klassischer Musik »made in Germany« in Japan wurde bereits im 19. Jahrhunderts gelegt: Als Teil von umfassenden gesellschaftlichen Modernisierungsmaßnahmen vergab die Regierung ab 1868 Stipendien an junge japanische Intellektuelle, damit diese an den besten internationalen Instituten studieren konnten. Berlin wurde – neben Wien – als globales Zentrum der Musik betrachtet, und so erhielten viele japanische Studierende um die Jahrhundertwende die Gelegenheit, von Komponisten wie etwa Max Bruch zu lernen. Zurück in der Heimat, teilten sie ihre Begeisterung für die europäische Kunstmusik sowie das Wissen um die instrumentale und kompositorische Praxis der klassisch-romantischen Tradition. """ struct ContentView: View { var body: some View { VStack { Text(txt) } .padding(.leading, 20) .padding(.trailing, 20) .frame(maxWidth: .infinity) } } This is also enough: Text(txt) .padding(.horizontal, 20) .fixedSize(horizontal: false, vertical: true) Expected: Text is rendered without truncation / ellipsis. Actual: Text is rendered with too small height / missing one line so it’s truncated / with ellipsis.
Replies
10
Boosts
0
Views
556
Activity
1w
tabViewBottomAccessory does not inherit tab bar vibrancy / foregroundStyle behavior
Hi, I’m experimenting with the new tabViewBottomAccessory API and I’m running into an inconsistency with how colors are handled compared to the native tab bar items. Context When using a TabView, the system tab bar automatically adapts its foreground color (icons and labels) based on the background content (light/dark, images, etc.). This is the expected “vibrancy” behavior. However, when adding content via .tabViewBottomAccessory, the foreground color does not seem to follow the same logic. Minimal example : import SwiftUI struct ContentView: View { var body: some View { TabView { Tab("Home", systemImage: "house") { Text("Home") .frame(maxWidth: .infinity, maxHeight: .infinity) .ignoresSafeArea() } Tab("New", systemImage: "squareshape.split.2x2") { Text("New") .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundStyle(.white) .background(Color.black) .ignoresSafeArea() } } .tabViewBottomAccessory { HStack { Image(systemName: "command.square") Text("Message test") Spacer() Image(systemName: "play.fill") } .padding(.horizontal, 20) .foregroundStyle(.primary) } } } Observed behavior Tab bar icons and labels correctly switch between dark and light depending on the background. Content inside .tabViewBottomAccessory remains black, even when .foregroundStyle(.primary) is used. Expected behavior I would expect .tabViewBottomAccessory content to: Either inherit the same vibrancy/foreground adaptation as the tab bar items Or have a documented way to match that behavior I want to reuse the Apple Music Navigation
Topic: UI Frameworks SubTopic: SwiftUI
Replies
0
Boosts
0
Views
71
Activity
1w