For a number of complex views in my app, I need to embed scrollable objects (like List, Form or TextEditor) in a ScrollView. To do that, I need to apply a limit to the height of the embedded object.
What I would like to do is set that limit to the actual height of the content being displayed in the List, Form, TextEditor, etc. (Note that this height calculation should be dynamic, so that content changes are properly displayed.
I attempted the following:
@State listHeight: CGFloat = .zero
List {
... content here
}
.onScrollGeometryChange(for: CGFloat.self, of: { geometry in
return geometry.contentSize.height
}, action: { oldValue, newValue in
if oldValue != newValue {
listHeight = newValue
}
})
.frame(height: listHeight)
.scrollDisabled(true)
This does not work because geometry.contentSize.height is always 0. So it is apparent that .onScrollGeometryChangedoes not interact with the internal scrolling mechanism of List. (Note, however, that.scrollDisabled` does work.)
Does anyone have a suggestion on how I might get this to work?
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
In SwiftUI for macOS, when implementing a DragGesture inside a ScrollVIew, how can I implement auto-scrolling when the mouse is not actively moving?
In AppKit, this would normally be done with a periodic event so that auto-scrolling continues to take place even if the user isn't actively moving the mouse. This is essential behaviour when implementing something like a drag-to-select gesture.
NSView.autoscroll(with: NSEvent) -> Bool
Is there anything in SwiftUI or ScrollView to accomplish this behaviour?
If you create a NavigationSplitView, then the sidebar is automatically adorned with a sidebar material effect. This affects the views background as well as any controls that are in the view.
What is the correct way to disable this behaviour so that I can use a NavigationSplitView without the material effects being applied?
The best I've come up with so far is to explicitly set the background on the sidebar but I'm curious if that's the correct way or I'm just getting lucky.
struct ContentView: View {
var body: some View {
NavigationSplitView {
// This works, but is it correct?
SidebarView()
.background(.windowBackground)
} detail: {
DetailView()
}
}
}
Hello,
It's impossible to manage hit area for textfield and it's already too small by default (detected with Accessibility Inspector).
I'm tried to force with modifier "frame(minHeight: xxx)", the component change the height but the hit area is still in same size.
Have to tips to fix this issue?
Is it possible to add an ornament below a tab bar like this? Or does the whole side bar have to be an ornament to build this?
What is the correct way to track the number of items in a relationship using SwiftData and SwiftUI?
Imagine a macOS application with a sidebar that lists Folders and Tags. An Item can belong to a Folder and have many Tags. In the sidebar, I want to show the name of the Folder or Tag along with the number of Items in it.
I feel like I'm missing something obvious within SwiftData to wire this up such that my SwiftUI views correctly updated whenever the underlying modelContext is updated.
// The basic schema
@Model final class Item {
var name = "Untitled Item"
var folder: Folder? = nil
var tags: [Tag] = []
}
@Model final class Folder {
var name = "Untitled Folder"
var items: [Item] = []
}
@Model final class Tag {
var name = "Untitled Tag"
var items: [Item] = []
}
// A SwiftUI view to show a Folder.
struct FolderRowView: View {
let folder: Folder
// Should I use an @Query here??
// @Query var items: [Item]
var body: some View {
HStack {
Text(folder.name)
Spacer()
Text(folder.items.count.formatted())
}
}
}
The above code works, once, but if I then add a new Item to that Folder, then this SwiftUI view does not update. I can make it work if I use an @Query with an #Predicate but even then I'm not quite sure how the #Predicate is supposed to be written. (And it seems excessive to have an @Query on every single row, given how many there could be.)
struct FolderView: View {
@Query private var items: [Item]
private var folder: Folder
init(folder: Folder) {
self.folder = folder
// I've read online that this needs to be captured outside the Predicate?
let identifier = folder.persistentModelID
_items = Query(filter: #Predicate { link in
// Is this syntax correct? The results seem inconsistent in my app...
if let folder = link.folder {
return folder.persistentModelID == identifier
} else {
return false
}
})
}
var body: some View {
HStack {
Text(folder.name)
Spacer()
// This mostly works.
Text(links.count.formatted())
}
}
}
As I try to integrate SwiftData and SwiftUI into a traditional macOS app with a sidebar, content view and inspector I'm finding it challenging to understand how to wire everything up.
In this particular example, tracking the count, is there a "correct" way to handle this?
Right now, the traffic light buttons overlapped on my iPad app top corner on windows mode (full screen is fine).
How do I properly design my app to avoid the traffic light buttons? Detect that it is iPadOS 26?
Topic:
UI Frameworks
SubTopic:
SwiftUI
When performing custom layout in AppKit, it's essential that you pixel align frames using methods like backingAlignedRect. The alignment differs depending on the backingScaleFactor of the parent window.
When building custom Layouts in SwiftUI, how should you compute the alignment of a subview.frame in placeSubviews() before calling subview.place(...)?
Surprisingly, I haven't seen any mention of this in the WWDC videos. However, if I create a Rectangle of width 1px and then position it on fractional coordinates, I get a blurry view, as I would expect.
Rounding to whole numbers works, but on Retina screens you should be able to round to 0.5 as well.
func placeSubviews(
in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout Void
) {
// This should be backing aligned based on the parent window's backing scale factor.
var frame = CGRect(
x: 10.3,
y: 10.8,
width: 300.6,
height: 300.1
)
subview.place(
at: frame.origin,
anchor: .topLeading,
proposal: ProposedViewSize(frame.size)
)
}
It's related to the passByValue nature of structs. In the sample code below, I'm displaying a list of structs (and I can add instances to my list using Int.random(1..<3) to pick one of two possible predefined versions of the struct).
I also have a detail view that can modify the details of a single struct. However when I run this code, it will instead modify all the instances (ie either Sunday or Monday) in my list.
To see this behaviour, run the following code and:
tap New Trigger enough times that there are multiple of at least one of the sunday/monday triggers
tap one of the matching trigger rows
modify either the day, or the int
expected: only one of the rows will reflect the edit
actual: all the matching instances will be updated.
This suggests to me that my Sunday and Monday static instances are being passed by reference when they get added to the array. But I had thought structs were strictly pass by value. What am I missing?
thanks in advance for any wisdom,
Mike
struct ContentView: View {
@State var fetchTriggers: [FetchTrigger] = []
var body: some View {
NavigationView {
VStack {
Button("New Trigger") {
fetchTriggers.append(Int.random(in: 1..<3) == 1 ? .sunMorning : .monEvening)
}
List($fetchTriggers) { fetchTrigger in
NavigationLink(destination: FetchTriggerDetailView(fetchTrigger: fetchTrigger)
.navigationBarTitle("Back", displayMode: .inline))
{
Text(fetchTrigger.wrappedValue.description)
.padding()
}
}
}
}
}
}
struct FetchTrigger: Identifiable {
static let monEvening: FetchTrigger = .init(dayOfWeek: .monday, hour: 6)
static let sunMorning: FetchTrigger = .init(dayOfWeek: .sunday, hour: 3)
let id = UUID()
enum DayOfWeek: Int, Codable, CaseIterable, Identifiable {
var id: Int { self.rawValue }
case sunday = 1
case monday
case tuesday
var description: String {
switch self {
case .sunday: return "Sunday"
case .monday: return "Monday"
case .tuesday: return "Tuesday"
}
}
}
var dayOfWeek: DayOfWeek
var hour: Int
var description: String {
"\(dayOfWeek.description), \(hour):00"
}
}
struct FetchTriggerDetailView: View {
@Binding var fetchTrigger: FetchTrigger
var body: some View {
HStack {
Picker("", selection: $fetchTrigger.dayOfWeek) {
ForEach(FetchTrigger.DayOfWeek.allCases) { dayOfWeek in
Text(dayOfWeek.description)
.tag(dayOfWeek)
}
}
Picker("", selection: $fetchTrigger.hour) {
ForEach(1...12, id: \.self) { number in
Text("\(number)")
.tag(number)
}
}
}
}
}
I'm building a visionOS app where users can place canvases into the 3D environment. These canvases are RealityKit entities that render web content using a WKWebView. The web view is regularly snapshotted, and its image is applied as a texture on the canvas surface.
When the user taps on a canvas, a WindowGroup is opened that displays the same shared WKWebView instance. This works great: both the canvas in 3D and the WindowGroup reflect the same live web content.
The canvas owns the WKWebView and keeps a strong reference to it.
Problem:
Once the user closes the WindowGroup, the 3D canvas stops receiving snapshot updates. The snapshot loop task is still running (verified via logs), but WKWebView.takeSnapshot(...) never returns — the continuation hangs forever. No error is thrown, no image is returned.
If the user taps the canvas again (reopening the window), snapshots resume and everything works again.
@MainActor
private func getSnapshot() async -> UIImage? {
guard !webView.isLoading else { return nil }
let config = WKSnapshotConfiguration()
config.rect = webView.bounds
config.afterScreenUpdates = true
return await withCheckedContinuation { continuation in
webView.takeSnapshot(with: config) { image, _ in
continuation.resume(returning: image)
}
}
}
What I’ve already verified:
The WKWebView is still in memory.
The snapshot loop (Task) is still running; it just gets stuck inside takeSnapshot(...).
I tried keeping the WKWebView inside a hidden UIWindow (with .alpha = 0.01, .windowLevel = .alert + 1, and isHidden = false).
Suspicion
It seems that once a WKWebView is passed into SwiftUI and rendered (e.g., via UIViewRepresentable), SwiftUI takes full control over its lifecycle. SwiftUI tells WebKit "This view got closed" and WebKit reacts by stopping the WKWebView. Even though it still exists in memory and is being hold by a strong reference to it in the Canvas-Entity.
Right now, this feels like a one-way path:
Starting from the canvas, tapping it to open the WindowGroup works fine.
But once the WindowGroup is closed, the WebView freezes, and snapshots stop until the view is reopened.
Questions
Is there any way under visionOS to:
Keep a WKWebView rendering after its SwiftUI view (WindowGroup) is dismissed?
Prevent WebKit from suspending or freezing the view?
Embed the view in a persistent system-rendered context to keep snapshotting functional?
For context, here's the relevant SwiftUI setup
struct MyWebView: View {
var body: some View {
if let webView = WebViewManager.shared.getWebView() {
WebViewContainer(webView: webView)
}
}
}
struct WebViewContainer: UIViewRepresentable {
let webView: WKWebView
func makeUIView(context: Context) -> UIView {
return webView
}
func updateUIView(_ uiView: UIView, context: Context) {}
}
Given a View in SwiftUI for macOS, how can I tell if that view is hidden either because it, or any of its ancestor's opacity is 0.0 or the .hidden modifier has been applied?
Presumably I can manually do this with an Environment value on the ancestor view, but I'm curious if this can be done more idiomatically.
An example use case:
I have views that run long-running Tasks via the .task(id:) modifier. These tasks only need to be running if the View itself is visible to the user.
When the View is hidden, the task should stop. When the View reappears, the Task should restart. This happens automatically when Views are created and destroyed, but does not happen when a view is only hidden.
I have an app using TabView with multiple Tabs using tabViewStyle „sidebarAdaptable“.
Each Tab does have its own NavigationStack.
When I switch multiple times between the tabs and append a child view to one of my navigation stacks, it will stop working after a few tries with the following error
„A NavigationLink is presenting a value of type “HomeStack” but there is no matching navigationDestination declaration visible from the location of the link. The link cannot be activated.
Note: Links search for destinations in any surrounding NavigationStack, then within the same column of a NavigationSplitView.“
The same code is working fine on iOS, iPadOS but not working on macOS.
When I remove tabViewStyle of sidebarAdaptable it’s also working on macOS.
I shrinked it down to a minimal reproducible code sample.
Any idea if that is a bug or I'm doing something wrong?
struct ContentView: View {
@State private var appState: AppState = .init()
var body: some View {
TabView(selection: $appState.rootTab) {
Tab("Home", systemImage: "house", value: RootTab.home) {
NavigationStack(path: $appState.homeStack) {
Text("Home Stack")
NavigationLink("Item 1", value: HomeStack.item1)
.padding()
NavigationLink("Item 2", value: HomeStack.item2)
.padding()
.navigationDestination(for: HomeStack.self) { stack in
Text("Stack \(stack.rawValue)")
}
.navigationTitle("HomeStack")
}
}
Tab("Tab1", systemImage: "gear", value: RootTab.tab1) {
NavigationStack(path: $appState.tab1Stack) {
Text("Tab 1 Stack")
NavigationLink("Item 1", value: Tab1Stack.item1)
.padding()
NavigationLink("Item 2", value: Tab1Stack.item2)
.padding()
.navigationDestination(for: Tab1Stack.self) { stack in
Text("Stack \(stack.rawValue)")
}
.navigationTitle("Tab1Stack")
}
}
}
.tabViewStyle(.sidebarAdaptable)
.onChange(of: appState.rootTab) { _, _ in
appState.homeStack.removeAll()
appState.tab1Stack.removeAll()
}
}
}
@MainActor
@Observable
class AppState {
var rootTab: RootTab = .home
var homeStack: [HomeStack] = []
var tab1Stack: [Tab1Stack] = []
}
enum RootTab: Hashable {
case home
case tab1
case tab2
}
enum HomeStack: String, Hashable {
case home
case item1
case item2
}
enum Tab1Stack: String, Hashable {
case home
case item1
case item2
}
Hi! How can I create a toolbar animation in SwiftUI like the one shown at 16:54 in WWDC session?
Feedback ID: FB19846667
When dismissing a Menu view when the device is set to dark appearance, there is a flash of lightness that is distracting and feels unnatural. This becomes an issue for apps that rely on the user interacting with Menu views often.
When using the overflow menu on a toolbar, the effect of dismissing the menu is a lot more natural and there is less flashing. I expect a similar visual effect when creating Menu views outside of a toolbar.
Has anyone found a way around this somehow?
Comparison between dismissing a menu and a toolbar overflow: https://www.youtube.com/shorts/H2gUQOwos3Y
Slowed down version of dismissing a menu with a visible light flash: https://www.youtube.com/shorts/MBCCkK-GfqY
When I add a TextField with @FocusState to a toolbar, I noticed that setting focus = false doesn't cause the form to lose focus
If I move the TextField out of the toolbar setting focus = false works fine. How can I unfocus the text field when the cancel button is tapped?
Minimal example tested on Xcode Version 26.0 beta 6 (17A5305f):
import SwiftUI
struct ContentView: View {
@State private var text: String = ""
@FocusState private var focus: Bool
var body: some View {
NavigationStack {
List {
Text("Test List")
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
TextField("Test", text: $text)
.padding(.horizontal)
.focused($focus)
}
ToolbarItem(placement: .bottomBar) {
Button(role: .cancel) {
focus = false // THIS DOESN'T WORK!
}
}
}
}
}
}
#Preview {
ContentView()
}```
I’ve got the new SwiftUi webview in a sheet. When I pull down the contents from the top of the page I expect it to dismiss the sheet, but it does not.
Is there a workaround or modifier I’m missing?
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.
The following code:
struct ContentView: View {
@State private var email = ""
var body: some View {
VStack {
TextField("email", text: $email)
.textContentType(.emailAddress)
}
}
}
does not work as expected. What I expected is to run the app and have it suggest my actual email when I click into the field. Instead what it does is display "Hide My Email" twice. Selecting the first "Hide My Email" pastes the actual text "Hide My Email" into the field, the second one brings up an iCloud sheet to select a random email.
Trying some suggestions online for .emailAddress in general not working does not change anything:
TextField("Email", text: $email)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textContentType(.emailAddress)
.keyboardType(.emailAddress)
.autocorrectionDisabled()
.disableAutocorrection(true)
.textInputAutocapitalization(.never)
Topic:
UI Frameworks
SubTopic:
SwiftUI
since upgrading my dev phone to ios26 the pop alerts in my app no longer respond to user touch.
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hi everyone,
I’m encountering the following error when displaying a TextField inside a Form together with a ToolbarItem(placement: .keyboard):
Invalid frame dimension (negative or non-finite).
Environment
This issue reproduces on iPhone 12 mini (iOS 18.6).
The same code does not reproduce on iPhone 15.
I confirmed that explicitly constraining the size of the TextField or Text with .frame(width:height:) does not resolve the issue.
Minimal Reproducible Example
import SwiftUI
struct Test: View {
@State var value: Int = 0
var body: some View {
Form {
TextField("0", value: $value, format: .number)
}
.toolbar {
ToolbarItem(placement: .keyboard) {
Text("Close")
}
}
}
}
Has anyone else encountered this issue? Is this a known bug, or is there a recommended workaround for devices with smaller screen widths such as the iPhone 12 mini?
Thanks in advance for your help!
Best regards,
Naoya Ozawa
Topic:
UI Frameworks
SubTopic:
SwiftUI