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

SwiftUI Documentation

Post

Replies

Boosts

Views

Activity

Wrapping views with a VStack breaks animation, but using an extension to do the same thing fixes it.
I was having issues with views transitioning between being visible and not visible inside of List. They would appear, but the animation would be jerky. I switched to using a Section, and the animation looked much better. However, the spacing between views and padding wasn't what I wanted. So I wrapped some of the views inside the Section with a VStack. But first, this is how my view SectionView looks: struct SectionView<Content: View>: View { let title: String @ViewBuilder var content: () -> Content var body: some View { Section { Text(title) .font(.title3.bold()) content() } .listRowInsets(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20)) .listRowBackground(Color.clear) .listRowSeparator(.hidden) .contentShape(.rect) } } Below was in a separate view: SectionView(title: "Title") { Group { HStack { // Stuff here } if let selectedMonth { VStack(alignment: .leading, spacing: 10) { // Stuff here } } } .animation(.smooth, value: selectedMonth) } This, however, broke the animations again when the VStack appears. It went back to being jerky. So instead of wrapping the content inside of a VStack, I created an extension to do the same thing. extension View { @ViewBuilder func condensed() -> some View { VStack(alignment: .leading, spacing: 10, content: { self }) } } So now instead of wrapping it in a VStack, I do this: if let selectedMonth { Group { // Stuff here } .condensed() } The animations look good again. I just can't figure out why that is. Can anyone help me understand this?
0
0
137
2w
Animated Custom Transitions Crash using Swift 6
When you apply an animation to a custom Transition in Swift 6, it is likely that that the app will crash with a SwiftUI.AsyncRenderer dispatch_assert_queue_fail error. Non-animated Transitions do not crash nor do animated system transitions. If you use ViewModifiers to create an AnyTransition with the .modifier(active:, identity:) static method, there is no problem. I used the example Transition in the docs for Transition to illustrate this problem. I'm using Xcode 16.2 RC and Swift 6, running iOS 18.1.1 on an iPhone 16 Pro. I've created two separate Previews that illustrate what specifically crashes the app and what doesn't as well as a workaround for this bug. func generateRandomString() -> String { let characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" return String((0..<10).compactMap { _ in characters.randomElement() }) } // MARK: Works extension AnyTransition { struct RotatingFadeTransitionModifier: ViewModifier { let opacity: CGFloat let rotation: Angle func body(content: Content) -> some View { content .opacity(opacity) .rotationEffect(rotation) } } static var rotatingFade: AnyTransition { .asymmetric( insertion: .modifier( active: RotatingFadeTransitionModifier(opacity: 0, rotation: .degrees(30)), identity: RotatingFadeTransitionModifier(opacity: 1, rotation: .zero) ), removal: .modifier( active: RotatingFadeTransitionModifier(opacity: 0, rotation: .degrees(-30)), identity: RotatingFadeTransitionModifier(opacity: 1, rotation: .zero) ) ) } } struct WorkingTransitionView: View { @State private var text: String = "some string" var body: some View { VStack(spacing: 32) { Text("system transition: \(text)") .id(text) .transition(.slide) // Gets the explicit Button animation applied instead of // the transition animation Text("animated system transition: \(text)") .id(text) .transition(.slide.animation(.bouncy(duration: 0.5))) Text("custom transition: \(text)") .id(text) .transition(.rotatingFade) Text("animated custom transition: \(text)") .id(text) .transition(.rotatingFade.animation(.bouncy( extraBounce: 0.5))) Button("animated randomize - safe") { withAnimation(.smooth(duration: 5.45, extraBounce: 0.15)) { text = generateRandomString() } } } } } // MARK: Crashes struct RotatingFadeTransition: Transition { func body(content: Content, phase: TransitionPhase) -> some View { content .opacity(phase.isIdentity ? 1.0 : 0.0) .rotationEffect(phase.rotation) } } extension TransitionPhase { fileprivate var rotation: Angle { switch self { case .willAppear: .degrees(30) case .identity: .zero case .didDisappear: .degrees(-30) } } } struct CrashingTransitionView: View { @State private var text: String = "some string" @State private var presentCustomTransitionText: Bool = false @State private var presentAnimatedCustomTransitionText: Bool = false var body: some View { VStack(spacing: 32) { Text("on 1-5 attempts generally, animated custom Transitions will crash with a SwiftUI.AsyncRenderer dispatch_assert_queue_fail") Divider() textWithSafeSystemTransition if presentCustomTransitionText { textWithCustomTransition } if presentAnimatedCustomTransitionText { textWithAnimatedCustomTransition } Divider() Text("Randomization") Button("randomize - won't crash non-animated custom transition text") { text = generateRandomString() } Button("animated randomize - will crash any custom transition text") { withAnimation(.smooth(duration: 0.45, extraBounce: 0.15)) { text = generateRandomString() } } Divider() Text("Text Presentation") Button("present non-animated custom transition text") { presentCustomTransitionText = true } Button("present animated custom transition text") { presentAnimatedCustomTransitionText = true } } } private var textWithSafeSystemTransition: some View { Text("safe, system transition: \(text)") .id(text) .transition(.slide) } private var textWithCustomTransition: some View { Text("safe text, custom transition: \(text)") .id(text) .transition(RotatingFadeTransition()) } private var textWithAnimatedCustomTransition: some View { Text("crashing text: \(text)") .id(text) .transition(RotatingFadeTransition().animation(.smooth)) } } #Preview("Working Code") { WorkingTransitionView() } #Preview("Crashing Code") { CrashingTransitionView() }
0
0
166
2w
SwiftUI: Picker text color not changing until reloading view
My picker looks like this: Picker("Color", selection: $backgroundColor.animation(), content: { ForEach(TransactionCategory.Colors.allCases, id: \.self) { color in Text(color.rawValue) .tag(color) .foregroundStyle(color.getColor()) } }) This changes a tint color which is applied to the entire tabview. I can see that it's working because the buttons in the top tab bar are changing color as I change the picker value. However, the color of the text inside the picker is not changing until I go back one view and then come back to this view. I tried setting an ID on the picker and then updating it when the picker value changes, but that didn't work. Any ideas?
0
1
148
2w
Abnormal behavior .confirmationDialog/.alert inside lazyVstack
This is a reproducible issue, create a blank project and put a cell inside a scrollview lazyVstack, I need to have the confirmation dialog on the button or else on iPad it will crash but having the .confirmationDialog inside the lazyVstack leads to unexpected behavior. If you try to click through the cells from 1-10 the .confirmationDialog will stop working after a few taps on the cells. Not sure what the workaround is I'm trying to do something similar in my app with post cells and it's just not working well. I've also noticed that a similar thing happens with .alert if you have it inside the LazyVStack. struct ContentView: View { var body: some View { ScrollView { LazyVStack { ForEach(0..<10) { number in cell(number: number) } } } } } struct cell: View { @State private var isShowingDialog: Bool = false let number : Int var body : some View { Button { print("Tapped Cell") isShowingDialog.toggle() } label: { Text("\(number)") .frame(height: 200) .frame(maxWidth: .infinity) .border(.black) } .padding(.horizontal, 16) .confirmationDialog("Options", isPresented: $isShowingDialog, titleVisibility: .visible) { Button("Some Button", role: .destructive) { print("Did Tap Option Button") } Button("Some Other Button") { print("Did Tap Other Option Button") } } } }
0
0
134
2w
Nested TimelineView and List cause hang issue
Hello. I’ve encountered a strange issue with TimelineView and wanted to report it. The following code runs without any problems: // Example 1. struct ContentView: View { var body: some View { TimelineView(.everyMinute) { _ in List { TimelineView(.everyMinute) { _ in Text("Hello") } } } } } However, the code below causes the CPU usage to spike to 100%, and the screen displays a blank view: // Example 2. struct ContentView: View { var body: some View { TimelineView(.everyMinute) { _ in List { TimelineView(.everyMinute) { _ in text } } } } var text: some View { Text("Hello") } } The same issue occurs with the following code: // Example 3. struct MyTextView: View { var body: some View { Text("Hello") } } struct ContentView: View { var body: some View { TimelineView(.everyMinute) { _ in List { TimelineView(.everyMinute) { _ in MyTextView() } } } } } Replacing List with LazyVStack and ForEach resolves the hang issue, but I need to use List because I rely on the swipeActions(). Does anyone have insights or suggestions on how to address this issue?
1
0
130
2w
Help with SwiftUI and UIKit Interjection
Hi, need some help with an iOS application we are trying to make future safe. Basically, we know that our app would require SwiftUI so the app is made in that framework, however we require some important elements that are available only in UIKit, so we've made a bridge that allows us to pass UIKit views to SwiftUI to display them. So most of the app actually has UI made in UIKit, however, we now need to use the Charts framework present in SwiftUI, we've used SwiftUI buttons in our UIKit before by passing them through a HostingController (Passing SwiftUI buttons to UIKit to use). And we are currently considering to the same for SwiftUI Charts. Just to recap, it's a SwiftUI iOS app, that is mostly made in UIKit (through a bridge) but also has other SwiftUI elements injected into it. What we want to know that, is this the best way to do this? Or is there a better way to have UIKit and SwiftUI work more comfortably with eachother. The reason for such looping around is also because we interoping our C++ code to Swift for making this application, since we are making it for many other platforms and the business logic is in C++. Let me know if there are better ways to go about this!
1
0
138
2w
SwiftUI.Entry macro only creating computed default values instead of stored?
https://gist.github.com/vanvoorden/37ff2b2f9a2a0d0657a3cc5624cc9139 Hi! I'm experimenting with the Entry macro in a SwiftUI app. I'm a little confused about how to stored a defaultValue to prevent extra work from creating this more than once. A "legacy" approach to defining an Environment variable looks something like this: struct StoredValue { var value: String { "Hello, world!" } init() { print("StoredValue.init()") } } extension EnvironmentValues { var storedValue: StoredValue { get { self[StoredValueKey.self] } set { self[StoredValueKey.self] = newValue } } struct StoredValueKey: EnvironmentKey { static let defaultValue = StoredValue() } } The defaultValue is a static stored property. Here is a "modern" approach using the Entry macro: struct ComputedValue { var value: String { "Hello, world!" } init() { print("ComputedValue.init()") } } extension EnvironmentValues { @Entry var computedValue: ComputedValue = ComputedValue() } From the perspective of the product engineer, it looks like I am defining another stored defaultValue property… but this actually expands to a computed property: extension EnvironmentValues { var computedValue: ComputedValue { get { self[__Key_computedValue.self] } set { self[__Key_computedValue.self] = newValue } } private struct __Key_computedValue: SwiftUICore.EnvironmentKey { static var defaultValue: ComputedValue { get { ComputedValue() } } } } If I tried to use both of these Environment properties in a SwiftUI component, it looks like I can confirm the computedValue is computing its defaultValue several times: @main struct EnvironmentDemoApp: App { var body: some Scene { WindowGroup { ContentView() } } } struct ContentView: View { @Environment(\.computedValue) var computedValue @Environment(\.storedValue) var storedValue var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() } } And then when I run the app: ComputedValue.init() StoredValue.init() ComputedValue.init() ComputedValue.init() ComputedValue.init() ComputedValue.init() ComputedValue.init() ComputedValue.init() ComputedValue.init() Is there any way to use the Entry macro in a way that we store the defaultValue instead of computing it on-demand every time?
0
0
139
2w
Deriving Apple Place ID from CLPlacemark or MKMapItem
I am trying to derive the Apple Place ID from a CLPlacemark (or via a MKMapItem derived from it) created via either CLGeocoder().reverseGeocodeLocation() or CLGeocoder().geocodeAddressString(). In many cases, the placemark returned from these functions contains detailed information (name, address, coordinates, etc), implying that the Apple Place ID is known, but the identifier is not present. The only way I have found to get a Place ID is via MKLocalSearch. Wondering if I am missing something here.
1
0
132
2w
Guidelines for ViewThatFits to avoid run-time crashes
TLDR: What rules ensure you won't have sporadic run-time crashes when using ViewThatFits? My app crashes - luckily reproducible. But the code appeared syntacticly and logically correct. Simplified excerpt: https://github.com/alanrick/Experiment3 The crash is caused by ViewThatFits being overwhelmed by concurrent changes in other views, exacerbated by animation effects. In the original code the problem was even worse because I'd gone overboard and used ViewThatFits in sub-views making the whole thing too dynamic. 

 My first rule is: Do not use nested ViewThatFits. 

 But this alone is not sufficient. What other rules can I apply to ensure I won't have hard-to-detect run-time crashes when using ViewThatFits?
0
0
124
2w
Modifying SwiftUI view added as a subview to UIKit View
I have a UIKit application and I have some swiftUI views(like button widget etc) that I m using in this application which are added as a subview using UIHostingController. I wanted to understand what is the right way as per the apple recommendation on how to perform some updates on these views, since the UIKit and SwiftUI have a different way of operating. In a pure swiftUI application we use the @State variables which when modified the view are re-rendered. However, in an UIKit application we can directly modify the widget property like color or font from the object. So, my question is should I get the hostingController object from the swiftUI view and then perform any update on that UIKit view. Is this the right way? If not, what is the correct way? can someone provide a detailed explanation?
0
0
127
2w
Changing focus state in onSubmit causes keyboard to bounce
Is there any way to prevent the keyboard from bouncing when changing the focus state in onSubmit? Or is it not recommended to change focus in onSubmit? The following view is setup so that pressing return on the keyboard should cause focus to move between the TextFields. struct TextFieldFocusState: View { enum Field { case field1 case field2 } @FocusState var focusedField: Field? var body: some View { Form { TextField("Field 1", text: .constant("")) .focused($focusedField, equals: .field1) .onSubmit { focusedField = .field2 } TextField("Field 2", text: .constant("")) .focused($focusedField, equals: .field2) .onSubmit { focusedField = .field1 } } } } I would expect that when pressing return, the keyboard would say on screen. What actually happens is the keyboard appears to bounce when the return key is pressed (first half of gif). I assume this is because onSubmit starts dismissing the keyboard then setting the focus state causes the keyboard to be presented again. The issue doesn't occur when tapping directly on the text fields to change focus (second half of gif).
0
1
162
2w
Replacing array in Observed object won't update UI.
Hello, following is the issue: I have a @Observable view model which has an array of @Observable Items. Tapping an item leads to a detail like view to submit a value to the item. This work thru bindings. However I have the need to replace the contents of the array entirely with a fresh version loaded from the network. It will contain the "same" objects with the same id but some values might have changed. So replacing the entire array seems to not update the UI because the IDs are the same as before. Also this seems to break the bindings because when replacing the array, editing no longer updates the UI. How to test the behavior: Launch the app in simulator. Add some values to the items by tapping on an item and then on add. Notice how changes are updated. Tap the blue button to sync fresh data to the array. (Not replacing the actual array) Confirm everything is still working Replace the array with the red button. Editing and UI updates are broken from now on. What is the proper way to handle this scenario? Project: https://github.com/ChristianSchuster/DTS_DataReplaceExample.git
2
0
213
2w
SwiftUI: Rotate symbolEffect not working
Hello, I have the following code: struct SettingsButton: View { var action: () -> Void @State private var rotate: Bool = false var body: some View { Button(action: { rotate.toggle() action() }, label: { Image(systemName: "gearshape") }) .symbolEffect(.rotate, value: rotate) } } For some reason, my button is not rotating. Other effects such as pulse and bounce work as expected. I applied the .clockwise direction thinking it needed a direction set, but that didn't work either. I also tried using the symbolEffect with isActive, and that didn't work. Lastly, I thought there may be an issue with Xcode so I closed that and reopened, but still not working. Any ideas?
1
0
166
2w
Button in navigation bar using UIHostingController appears after push animation
I'm trying to push a SwiftUI view from UIKit using UIHostingController. In the new view there is a button in the right side of the navigation bar, but it pops after the push animation. I can't make it appear with an animation like in a normal UIViewController. I tried adding the button in the navigationItem of the hosting controller and in the toolbar of the SwiftUI but none gives a smooth animation. I've made a small test and this are the results. This is the code: class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() title = "Home" } @IBAction func buttonNavPressed(_ sender: Any) { let vc = UIHostingController(rootView: ContentView()) vc.navigationItem.title = "NavItem Button" vc.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(sayHello)) navigationController?.pushViewController(vc, animated: true) } @IBAction func buttonSwiftUIPressed(_ sender: Any) { let vc = UIHostingController(rootView: ContentViewWithButton()) navigationController?.pushViewController(vc, animated: true) } @objc func sayHello() { print("Hello") } } struct ContentView: View { var body: some View { Text("No button") } } struct ContentViewWithButton: View { var body: some View { Text("With button") .navigationTitle("SwuitUI W Button") .toolbar { ToolbarItem(placement: .topBarTrailing) { Button(action: { print("Hello") }, label: { Image(systemName: "camera") } ) } } } } There is any workaround to this problem?
0
0
104
2w
Can I tell when my iOS Widget is running on MacOS (when Use IPhone Widgets is on)
I have an iOS Widget that also can load on the Mac when the Use iPhone Widgets setting is turned on on the Mac in Desktop & Dock. I want to use a different url scheme to open video clips from the widget if it is being clicked on iOS or the Mac. I tried using ProcessInfo.processInfo.isiOSAppOnMac but it always thinks it is on iOS. I also tried looking for the user document path to see if it was /var/mobile/ or /Users/. but it always thinks it is /var/mobile. I assume this is as it is not really a catalyst app but a WidgetKit extension from the phone. Is there anyway I can figure out when the widget is running on the mac? Thanks!
5
0
142
2w
iOS 18 Button with accented image is broken(cannot tap) in desktop widget.
In iOS 18 widget, button is broken when it's has an accented desaturated image as content, the button's AppIntent will not trigger perform function. checkout the code below: ` struct WidgetExtEntryView : View { var entry: Provider.Entry var body: some View { VStack { Text("count:") Text("\(WidgetExtAppIntent.count)") HStack { // button can not be tapped Button(intent: WidgetExtAppIntent(action: "+1")) { VStack { Image(systemName: "plus.square.fill").resizable() .widgetAccentedRenderingMode(.accentedDesaturated) // <-- here .frame(width: 50, height: 50) Text("Broken") } }.tint(.red) // button can be tapped Button(intent: WidgetExtAppIntent(action: "+1")) { VStack { Image(systemName: "plus.square.fill").resizable() .widgetAccentedRenderingMode(.fullColor) // <-- here .frame(width: 50, height: 50) Text("OK").frame(width: 50, alignment: .center) } }.tint(.green) } .minimumScaleFactor(0.5) } } } ` check out the full demo project: ButtonInWidgetBrokenIOS18
1
0
132
2w
Tip.statusUpdates doesn't fire on event donation
I have a tip that's supposed to be shown after a certain event: extension Tips { static let somethingHappened = Tip.Event(id: "somethingHappened") } struct TestTip: Tip { let title = Text("Test tip") let message = Text("This is a description") var rules: [Rule] { #Rule(Tips.somethingHappened) { $0.donations.count > 0 } } } I would like to present this tip when its status becomes .available. To do this, I'm observing statusUpdates in a task: tipStatusObserverTask = Task { [tip, weak self] in print("observing \(tip.id)") for await status in tip.statusUpdates { guard let self, !Task.isCancelled else { return } print("tip status: \(status)") if case .available = status { print("will present \(tip.id)") displayTip() } } print("done observing \(tip.id)") } then I'm donating the event on a button press: @objc func didPressButton() { Tips.somethingHappened.sendDonation { print("donated Tips.somethingHappened") } } The event is donated, but statusUpdates doesn't fire, so the tip never gets shown. The tip appears after I restart the app. What's happening? What am I doing wrong? statusUpdates fires just fine if the rule is based on a @Parameter change, so I would expect this approach to work for event-based rules. Here's a project that reproduces the issue in case anyone wants to try: https://www.icloud.com/iclouddrive/085FxcgTPStDSSPoXwh7dx1pQ#TipKitTest
3
0
173
2w
How to set the ZPriority of a MapKit Annotation with SwiftUI
Am displaying a number of annotations on a map which vary their location over time and can inherently cluster together. I'd like to be able to set the Z order on the annotations as I have simple logic how to order them in the Z direction. I see the ZPriority property on UIKit Annotation view, but unclear how I can do that in SwiftUI. Is this exposed in any manner? I thought I could create a viewModifier and using the content parameter I'd be able to drop down into the UIKIt view to apply the value to the property, but alas I'm not seeing a clear way to do so. Is this at all possible without dropping down entirely to creating a NSViewRepresentable/UIViewRepresentable implementation of Map?
0
0
115
2w
Need help with objc_fatalv crash
My app's top crash is a mysterious one and I can't seem to figure it out. It always crashes on _objc_fatalv(unsigned long long, unsigned long long, char const*, char*) But the stack traces include a few different possible culprits like NavigationBridge_PhoneTV.pushTarget(isDetail:) UIKitNavigationBridge.update(environment:) ViewRendererHost.updateGraph() UIScrollView(SwiftUI) _swiftui_adjustsContentInsetWhenScrollDisabled Crash reports here: 2024-12-02_21-37-21.7864_-0600-1e78918e5586309b96a1c2986ff722778dec8a77.crash 2024-12-02_19-18-29.1251_-0500-a2fc5513683cd647b4adbbe03cc59e4a09237b5f.crash 2024-12-01_11-59-09.8888_-0500-9eb224ab3d37e76d0b966ea83473f584ac3bbe18.crash 2024-11-28_17-17-38.4808_+0100-46208989f016fbefd16c30873a88c2ef61dd91a1.crash Hopefully someone here can shed some light. For context we use a lot of UIHostingController's to bridge our SwiftUI views.
1
0
213
2w