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

SwiftUI Documentation

Post

Replies

Boosts

Views

Activity

NavigationView Issues in iOS 18 Beta 3
I have a SwiftUI NavigationView that is presented inside of a UIHostingController. In iOS 17 and earlier everything looks and works okay. I opened up my project in iOS 18 Beta 3 via the iOS Simulator running on macOS Sequoia, the NavigationView just displays a blank view with a Sidebar Button in the top left outside of the safe area. If I comment out the NavigationView and just display my 'root view' everything looks as it should but navigation is broken as it uses NavigationLink which of course doesn't work without NavigationView. I still target iOS 14 as Minimum Version so swapping completely to NavigationStack isn't possible given that NavigationView is deprecated from iOS 16 onwards. I tried to implement a solution with NavigationStack, it works but my UI displays outside of the safe area. I have a button towards the bottom of the screen which now overlaps the system line at the bottom of the screen. The Back Button in the Navigation Stack now sits behind the time in the top left. I do not have 'ignoreSafeArea' applied anywhere in this View Hierarchy. It looks like that is being applied by 'NavigationStack' on it's own accord. Is this happening to anyone else? Is this a known bug? I could not see this in the release notes if it is known. Thanks!
0
0
18
1h
Question about using multiple NavigationLinks in same project
Hello, I want to write an app for following requirements: Four views(A,B,C and D) has navigationlink as a button to move to view E. so I wrote a navigationlink on view A as follows: NavigationLink(destination: DestinationView(result: iOSVM.add(a: (Int(numberA) ?? 0), b: (Int(numberB) ?? 0)))) { Text("Add?") } and another navigationlink on view B like this: NavigationLink(destination: DestinationView(result:iOSVM.subtract(a:number1,b:number2))){ Text("Result?") } after running these codes, navigationlink on view A works properly. but another one on view B remains diabled. help me to fix this. thx, c00012
0
0
24
3h
Is ObservableObject implicitly a MainActor in iOS 16 and later?
This is a question regarding the specification of ObservalObject. The following code does not cause a compilation error when the deployment target is set to iOS 16, but it does cause the following error when set to iOS 15: class Model: ObservableObject { let player: AVPlayer @Published var isPlaying = false var playerObserver: NSKeyValueObservation? init() { self.player = AVPlayer() self.playerObserver = self.player.observe(\.rate, options: [.new, .old]) { player, change in NSLog("changed rate: \(String(describing: change.newValue))") } } func setup() { let name = "sample" let url = Bundle.main.url(forResource: name, withExtension: "m4a")! let playerItem = AVPlayerItem(url: url) self.player.replaceCurrentItem(with: playerItem) } func play() { self.player.play() self.isPlaying = true } } The following error occurs in iOS 15: Cannot form key path to main actor-isolated property 'rate' Call to main actor-isolated instance method 'play()' in a synchronous nonisolated context Additionally, if the code is modified as follows, the error does not occur even in iOS 15. Specifically, adding @MainActor to the Model resolves the issue. @MainActor class Model: ObservableObject { ... } From this behavior, I guessed that ObservableObject is implicitly a MainActor in iOS 16 and later. Is this understanding correct?
1
0
71
22h
Unable to Display Text Fully in Text() SwiftUI
Hi! I am working on an app that requires displaying special characters and I have currently implemented it to just use Text() in swiftUI. Unfortunately, it can't display the full height of the text. Attached is one example of this happening. I have attempted to do .linespacing() but that method doesn't work. .frame also doesn't seem to make a change. Does anyone have a solution to this? Thanks!
6
0
83
23h
Text .onTapGesture Never Called When Shown with .onLongPressGesture
I'm showing a Text View when a button with an image is long-pressed. import SwiftUI struct ContentView: View { @Environment(\.colorScheme) var colorScheme var isDark: Bool { return colorScheme == .dark } @State private var showLabel = false var body: some View { Button(action: { }) { VStack { ZStack { Image(systemName: "swift") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 32) .padding(.horizontal, 40) .padding(.vertical, 6) .background(.gray.opacity(0.2), in: RoundedRectangle(cornerRadius: 10)) .onTapGesture { showLabel.toggle() } .onLongPressGesture(minimumDuration: 2) { print("Long pressed...") showLabel.toggle() } if showLabel { Text("Help Content") .font(.caption) .foregroundStyle(!isDark ? .white : .black) .padding(10) .background(!isDark ? .black : .white, in: Rectangle()) .onTapGesture { print("hey") showLabel.toggle() } .offset(x: 120) } } } } } } So a Text View will appear as shown in the image above. But its .onTapGesture is never called. I wonder why? Thanks.
0
0
65
1d
Keyboard pushes Custom TabBar Up
Hello, so I created a custom TabBar using the .overlay modifier on TabView. However, when clicking/pressing on a TextField, the keyboard causes the TabBar to be pushed up in the view. From Reddit, I found that this is caused by using the .overlay modifier. How should I go about fixing this issue or what is the standard way of implementing custom tab bars? Thanks in advance! -- Kohl J Screenshot TabView Code struct RootView: View { @State var selectedTab : Tabs = .home; var body: some View { TabView(selection: $selectedTab) { HomeView(selectedTab: $selectedTab) .tag(Tabs.home) TaskListView(selectedTab: $selectedTab) .tag(Tabs.taskList) CreateTaskView(selectedTab: $selectedTab) .tag(Tabs.create) TrophiesView(selectedTab: $selectedTab) .tag(Tabs.trophies) SettingsView(selectedTab: $selectedTab) .tag(Tabs.settings) } .overlay(alignment: .bottom) { TabBar(selectedTab: $selectedTab) .padding(.horizontal) } } } Custom TabBar Code struct TabBar: View { @Binding var selectedTab : Tabs var body : some View { HStack (alignment: .center) { Button(action: { // switch to home view selectedTab = Tabs.home }, label: { TabBarButton(buttonText: "Home", imageName: "house.fill", size: 24, isActive: selectedTab == .home, hasTopBar: true) }) Button(action: { // switch to task list view selectedTab = Tabs.taskList }, label: { TabBarButton(buttonText: "Task List", imageName: "list.bullet.clipboard.fill", size: 24, isActive: selectedTab == .taskList, hasTopBar: true) }) Button(action: { // switch to create view selectedTab = Tabs.create }, label: { TabBarButton(buttonText: "Create", imageName: "plus.circle.fill", size: 32, isActive: selectedTab == .create, hasTopBar: false) }) Button(action: { // switch to trophies view selectedTab = Tabs.trophies }, label: { TabBarButton(buttonText: "Trophies", imageName: "trophy.fill", size: 24, isActive: selectedTab == .trophies, hasTopBar: true) }) Button(action: { // switch to settings view selectedTab = Tabs.settings }, label: { TabBarButton(buttonText: "Settings", imageName: "gearshape.fill", size: 24, isActive: selectedTab == .settings, hasTopBar: true) }) } .frame(height: 60) } }
1
0
97
1d
SwiftUI Gestures prevent scrolling with iOS 18
I added gesture support to my app that supports iOS 16 and 17 and have never had issues with it. However, when I compiled my app with Xcode 16 I immediately noticed a problem with the app when I ran it in the simulator. I couldn't scroll up or down. I figured out it’s because of my gesture support. My gesture support is pretty simple. let myDragGesture = DragGesture() .onChanged { gesture in self.offset = gesture.translation } .onEnded { _ in if self.offset.width > threshold { ...some logic } else if self.offset.width < -threshold { ...some other logic } logitUI.debug("drag gesture width was \(self.offset.width)") self.offset = .zero } If I pass nil to .gesture instead of myDragGesture then scrolling starts working again. Here’s some example output when I’m trying to scroll down. These messages do NOT appear when I run my app on an iOS 16/17 simulator with Xcode 15. drag gesture width was 5.333328 drag gesture width was -15.333344 drag gesture width was -3.000000 drag gesture width was -24.333328 drag gesture width was -30.666656 I opened FB14205678 about this.
0
0
93
1d
Using KeyFrameTimeline as value for .keyFrameAnimator view modifier
I have several views I want to animate in concert. I plan to use keyframe animation. I want to keep the .keyFrameAnimator modifier code small; I have a lot of ...Keyframes inside several KeyframeTracks. It seems like I should be able to isolate the body of the keyframes parameter into a func or var. Builders are such a pain, I can't grok the right way to refactor their bodies out. I've tried to make a standalone @KeyframeTrackContentBuilder<MyValue> function but cannot figure out the right syntax/incantation to stuff it with KeyframeTracks. My latest attempt is to create a func that returns a KeyframeTimeline, but that's been a deadend too. let k: KeyframeTimeline<MyValue> = timeline(...) CartoonCardView(color: .yellow) .keyframeAnimator( initialValue: k.value(time: 0) ) { content, value in content .rotationEffect(value.angle) .scaleEffect(value.scale) .offset(value.offset) } keyframes: { _ in k } The error on k in the last line is "No exact matches in call to static method 'buildExpression'" with the sub-error "Candidate requires that 'KeyframeTimeline' conform to 'KeyframeTrackContent' (requirement specified as 'K' : 'KeyframeTrackContent') (SwiftUICore.KeyframesBuilder)"
1
0
60
1d
How to use the new Text timer formats?
I'm trying to get a countdown timer to work, and the way I currently do it in my watchOS 10 app is a complicated load of nonsense to get two Strings that look like "1w 1d" and "12:34:56", i.e. a String that shows components like year, week and day, and another String showing hours, minutes and seconds. The new Text formats seen here https://developer.apple.com/wwdc24/10144?time=645 look useful, but I can't get them to return the values I need. If I use this: let dateA = Date.now let dateB = Date.advanced(by: /* value for 8 days, 12 hours, 34 minutes and 56 seconds */) Text(dateA, format: .offset(to: dateB, allowedFields: [.year, .week, .day], maxFieldCount: 3)) I expect to see "1 week 1 day", but it always comes back as "8 days". I guess it's giving me the most concise result, but I don't want that. I'm not sure anyone would want that. Imagine you have an event coming up in 3 days 6 hours, do you want to see "in 78 hours" or do you want "in 3 days and 6 hours"? Why must we make the user calculate the days and hours in their head when we have the ability to give them the right information? While I'm on this, why does the resulting String have to have "in " at the beginning? I don't want that; it's not my use case, but it's forced on me. I've raised this a hundred times with Apple. I just want to see a String that shows the difference between two dates in a format of my choosing, i.e. "1w 1d", but they never give me what I need, so I have to write extremely complex - and fragile - code that figures this stuff out, and I still can't get that to work properly. Why can't we just have something like: Text(from: dateA, to: dateB, format: "%yy %ww %dd") // for "1 year 2 weeks 3 days", show parts with a value > 0 Text(from: dateA, to: dateB, format: "%0yy %0ww %0dd") // for "0 years 2 weeks 3 days", show all parts regardless of value Text(from: dateA, to: dateB, format: "%y %w %d") // for "1y 2w 3d", show parts with a value > 0 Text(from: dateA, to: dateB, format: "%0y %0w %0d") // for "0y 2w 3d", show all parts regardless of value
0
0
53
1d
iOS 18 beta bug: NavigationStack pushes the same view twice
Hello, community and Apple engineers. I need your help. Our app has the following issue: NavigationStack pushes a view twice if the NavigationStack is inside TabView and NavigationStack uses a navigation path of custom Hashable elements. Our app works with issues in Xcode 18 Beta 13 + iOS 18.0. The same issue happened on previous beta versions of Xcode 18. The issue isn’t represented in iOS 17.x and everything worked well before iOS 18.0 beta releases. I was able to represent the same issue in a clear project with two simple views. I will paste the code below. Several notes: We use a centralised routing system in our app where all possible routes for navigation path are implemented in a View extension called withAppRouter(). We have a enum RouterDestination that contains all possible routes and is resolved in withAppRouter() extension. We use Router class that contains @Published var path: [RouterDestination] = [] and this @Published property is bound to NavigationStack. In the real app, we need to have an access to this path property for programmatic navigation purposes. Our app uses @ObservableObject / @StateObject approach. import SwiftUI struct ContentView: View { @StateObject private var router = Router() var body: some View { TabView { NavigationStack(path: $router.path) { NavigationLink(value: RouterDestination.next, label: { Label("Next", systemImage: "plus.circle.fill") }) .withAppRouter() } } } } enum RouterDestination: Hashable { case next } struct SecondView: View { var body: some View { Text("Screen 2") } } class Router: ObservableObject { @Published var path: [RouterDestination] = [] } extension View { func withAppRouter() -> some View { navigationDestination(for: RouterDestination.self) { destination in switch destination { case .next: return SecondView() } } } } Below you can see the GIF with the issue: What I tried to do: Use iOS 17+ @Observable approach. It didn’t help. Using @State var path: [RouterDestination] = [] directly inside View seems to help. But it is not what we want as we need this property to be @Published and located inside Router class where we can get an access to it, and use for programmatic navigation if needed. I ask Apple engineers to help with that, please, and if it is a bug of iOS 18 beta, then please fix it in the next versions of iOS 18.0
1
1
99
1d
onContinueUserActivity not working on first app launch
HI, I'm trying to deeplink from my widget to a view in my app. I'm using the "SwiftUI app lifecycle". This the code I'm currently using: var body: some Scene { WindowGroup { RootView() .onContinueUserActivity("NextDeparturesWidgetConfigurationIntent") { userActivity in guard let configuration: NextDeparturesWidgetConfigurationIntent = userActivity .widgetConfigurationIntent(), let stationEntity = configuration.stationEntity else { return } NotificationCenter.default.post(name: .onOpenStation, object: stationEntity.id) } } } It is working fine when the app is already running (in the background). However when the app is "cold starting" (ie. not in memory) onContinueUserActivity is never called. I tried adding a UIApplicationDelegateAdaptor: func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions ) -> UISceneConfiguration { if let userActivity = options.userActivities.first { let configuration: NextDeparturesWidgetConfigurationIntent? = userActivity.widgetConfigurationIntent() // casting fails } return UISceneConfiguration( name: nil, sessionRole: connectingSceneSession.role ) } I can see that the activity is received but it is never casted to NextDeparturesWidgetConfigurationIntent. What am I missing ?
0
0
58
1d
SwiftUI URLRequest Warning: "Connection has no local endpoint"
I have a simple SwiftUI application that sends a URLRequest as shown in the code snippet below: import SwiftUI @main struct GOGODemoApp: App { var body: some Scene { WindowGroup { MyView() } } } struct MyView: View { var body: some View { Button("Click") { sendHTTPRequest(to: "https://www.google.com") { code, err in print("Finished, code: \(code ?? -1), err: \(String(describing: err))") } } } } func sendHTTPRequest(to urlString: String, completion: @escaping (Int?, Error?) -> Void) { guard let url = URL(string: urlString) else { completion(nil, NSError(domain: "InvalidURL", code: 0, userInfo: nil)) return } let task = URLSession.shared.dataTask(with: url) { _, resp, error in if let httpResponse = resp as? HTTPURLResponse { completion(httpResponse.statusCode, error) } else { completion(-1, error) } } task.resume() } However, Xcode prints the following warning messages: nw_connection_copy_connected_local_endpoint_block_invoke [C1] Connection has no local endpoint nw_connection_copy_connected_local_endpoint_block_invoke [C1] Connection has no local endpoint nw_connection_copy_connected_local_endpoint_block_invoke [C3] Connection has no local endpoint nw_connection_copy_connected_local_endpoint_block_invoke [C3] Connection has no local endpoint Finished, code: 200, err: nil What does the warning 'Connection has no local endpoint' mean? Thank you for your assistance!
0
0
69
2d
How to get Command Line Tool code to launch SwiftUI App code
For MacOS I wrote a Terminal Command Line Tool to help me do number crunching calculations. No need for UI. This works great as-is, but there are opportunities to make the work more interesting by giving it some graphical representation like a heat map of the data. To that end I want the terminal to launch a SwiftUI code to open a window given an optional command line parameter that would display this visual representation of data in a new window. It would be nice to interact with that window visualization in some ways using SwiftUI. I originally thought it'd just be as easy as creating a SwiftUI View file and then throwing what normally appears under @main into a launch function and then calling the launch function from my main.swift file: import SwiftUI struct SwiftUIView: View { var body: some View { Text("Hello, World!") } } #Preview { SwiftUIView() } func LaunchSwiftUIApp() { //@main struct SwiftUIApp: App { var body: some Scene { WindowGroup { SwiftUIView() } } } } Of course this doesn't work. I've already have the command line code just spit out various png files via command line so looking to make those visualization a little more interactive or readable/organized by coding some SwiftUI stuff around those current visualizations. Anyway. Looking around this doesn't seem to be a thing people normally do. I'm not sure how to setup the Terminal Command Line Tool code that I wrote to optionally launch into SwiftUI code.
0
0
50
2d
How to restore specific scenes in SwiftUI
I'm trying to implement multiple window support for my app, but the system restores the wrong scene when the app is relaunched. Here's my code: import SwiftUI @main struct TestApp: App { @Environment(\.openWindow) private var openWindow var body: some Scene { WindowGroup(id: "Navigator") { Button { openWindow(id: "Editor", value: "test") } label: { Text("Open Window") } } WindowGroup(id: "Editor", for: String.self) { id in Text(id.wrappedValue ?? "none") } } } If I run the app and tap the button, the Editor window opens. If I force quit and open the app again, the app still has two windows open but both are Navigator windows. I've tried adding handlesExternalEvents to the Scene and advertising an activity with .userActivity(isActive:), neither changes this behaviour. Any advice?
0
0
67
2d
Text misaligned when adding images to an HStack
Wasted about 3 hours on this yesterday. I add a Text item in a field of a Form. Well and good. It wraps if needed, alights to the left a fixed amount no matter how much or how little text there is. All expected. I added an image. It alll went to hell after that as the text just sort of drew where it wanted to. It looked like the parent tile was now wider then the display area. No amount of playing with Spacer(), padding, Frames. HStack alignments, Position fields, and so on did any good. as all I am trying to do a simple tile with a text block and image. And SwiftUI fights me every step of the way. It's for a simple restaurant menu item tile. Name of item, price and image. If I drop the image, then it formats in a predictable fashion. HStack{ VStack{ if let maindish = item["name"]{ Text("\(maindish)").foregroundColor(.primary).modifier(favoriteStyle()).fontWeight(.regular).font(.custom("Avenir",size:fontSize)) } if let formattedPrice = item["formatted_price"]{ Text("\(formattedPrice)").foregroundColor(.secondary).modifier(favoriteStyle()).fontWeight(.bold).font(.custom("Avenir",size:fontSize)) } } Spacer() if let imageURL = item["thumbnail_image"]{ if let url = URL(string:imageURL) { drawImage(url:url, size:85.0).padding(0.0).padding(.trailing,75.00) } } }
0
0
58
2d
Why does my SwiftUI app crash when opened from an intent?
I'm encountering a crash in my SwiftUI app when it is opened via an AppIntent. The app runs perfectly when launched by tapping the app icon, but it crashes when opened from an intent. Here is a simplified version of my code: import AppIntents import SwiftData import SwiftUI @main struct GOGODemoApp: App { @State private var state: MyController = MyController() var body: some Scene { WindowGroup { MyView() //.environment(state) // ok } .environment(state) // failed to start app, crash with 'Dispatch queue: com.apple.main-thread' } } struct MyView: View { @Environment(MyController.self) var stateController var body: some View { Text("Hello") } } @Observable public class MyController { } struct OpenIntents: AppIntent { static var title: LocalizedStringResource = "OpenIntents" static var description = IntentDescription("Open App from intents.") static var openAppWhenRun: Bool = true @MainActor func perform() async throws -> some IntentResult { return .result() } } Observations: The app works fine when launched by tapping the app icon. The app crashes when opened via an AppIntent. The app works if I inject the environment in MyView instead of in WindowGroup. Question: Why does injecting the environment in WindowGroup cause the app to crash when opened from an intent, but works fine otherwise? What is the difference when injecting the environment directly in MyView?
0
0
55
2d
SwiftData rollback not updating the UI
I'm building a simple App using SwiftData. In my app a user can create, remove and edit posts. When editing, I want them to be able to hit "Save" to persist the changes or "Cancel" to discard the changes. The approach I'm using is to disable autosave and call modelContext.save() when saving and modelContext.rollback() when discarding the changes. So my modelContainer is defined as follows: WindowGroup { ContentView() .modelContainer(for: [Post.self], isAutosaveEnabled: false) } and I Save and Cancel like this: PostForm(post: post) .toolbar { ToolbarItemGroup(placement: .cancellationAction) { Button("Cancel") { if modelContext.hasChanges { modelContext.rollback() } dismiss() } } ToolbarItemGroup(placement: .confirmationAction) { Button("Save") { do { if modelContext.hasChanges { try modelContext.save() } } catch { fatalError("Failed to save post: \(error.localizedDescription)") } callback?() dismiss() } } } The issue I am facing is that after calling modelContext.rollback() my Posts aren't updating in the UI, they still show the changes. Restarting the app shows the Posts without the changes so I'm guessing that modelContext.rollback() is in fact discarding the changes and not persisting them in the Storage, the UI is the one that is not reacting to the change. Am I doing something wrong here? Is this approach correct?
0
0
87
3d