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

Showing .subscriptionPromotionalOffer() does not render any available offers if user already subscribed
Hi, I working with the new .subscriptionPromotionalOffer() modifier of SubscriptionStoreView, with the goal of presenting available promotional offers to users that have or have not yet subscribed to one of my subscriptions. In the former case, the promotional offers are rendered correctly along with the relevant subscription plan. But in the latter, if the user has already subscribed to the subscription, the promotional offers are not rendered at all, and the UI displays a grayed out "Current Plan" button. I expect the user to be able to enjoy this promotional offer, even it he's subscribed to the plan and he hasn't been offered the offer before. Why is it not showing for this user? Should not existing promotional offers be available to existing subscribers? struct SubscriptionsView: View { var subscriptions: [String] var offerId: String? var body: some View { SubscriptionStoreView(productIDs: subscriptions) { // UI content } .subscriptionPromotionalOffer( offer: { product, subscriptionInfo in return subscriptionInfo.promotionalOffers.first(where: { $0.id == offerId }) }, signature: { product, subscriptionInfo, promotionalOffer in return try await store.getSignature(product: product,subscriptionInfo: subscriptionInfo, promotionalOffer: promotionalOffer ) }) } }
2
0
523
Aug ’24
Migrating SwiftUI lifecycle to UIKit's
Hi, everyone I have an app already in production that uses SwiftUI's lifecycle (paired with an AppDelegate). Due to some specific behaviour of the app, we decided to migrate the app to use UIKit's lifecycle, adding the corresponding SceneDelegate to the app, as well as modifying the Info.plist file accordingly to accommodate to these new changes. Although everything seems to work when installing the app from zero, when installing it on top of another version, the screen goes black and the user cannot interact with the app at all unless they reinstall it completely. As I've read online, iOS is reusing the window configuration from the previous execution of the app. I know this because the AppDelegate's application(application:connectingSceneSession:options) is not being called when coming from a previous version of the app. I would love to know what can I do to make this work because, as you may understand, we cannot ask our user base to reinstall the application. Thank you very much.
7
2
1.6k
Aug ’24
Ignore accented rendering mode?
In iOS18 the user can change their Home Screen customization to choose either light, dark, or tinted. If they choose tinted the widgets are rendered using the "accented" rendering mode and without a background. Is there some way to override so that the tinted mode is ignore completely and the widgets render as full color? I know about WidgetAccentedRenderingMode (https://developer.apple.com/documentation/widgetkit/widgetaccentedrenderingmode) but that's only for images, not the whole control and doesn't help with the background also being removed in the tinted mode.
2
0
925
Aug ’24
SwiftUI Picker not trigger `onChange` first time when inside Menu in MacOS
code import SwiftUI @main struct swiftuiTestApp: App { @State private var value = "a"; var body: some Scene { WindowGroup { Menu("menu") { Picker("", selection: $value) { Text("A").tag("a") Text("B").tag("b") Text("C").tag("c") } .pickerStyle(.inline) .onChange(of: value) { oldValue, newValue in print("onChange", newValue) } } } } } on MacOS, onChange is not triggered when an option is selected for the first time, but subsequent option selections are triggered。 on MacOS, when Picker is not inside Menu, it works fine on iOS, both works fine with inside or not inside menu
6
0
1k
Sep ’24
Customising TabView styling
The default design of TabView is 90% close to what I want. However, the icons, to my eye, lack a sufficient amount of spacing at the top, and slightly too much white space at the bottom. Is there a way to give more top padding to the icons? To move the icons down without changing the height of the TabView itself? Also, I find it weird that .labelStyle(.titleOnly) works on Tabs but .labelStyle(.iconOnly) doesn't work. Is that a bug? I had to use Image instead of Label: TabView { Tab { MailView() } label: { Image(systemName: "envelope") .padding(20) } Tab { SettingsView() } label: { Image(systemName: "gearshape") .padding(20) } }
2
0
492
Sep ’24
How to create a custom TimeDataSource?
Hello. I am working with the iOS 18b8 and Xcode 16b6 betas, updating a Live Activity Widget to adopt the new FormatStyle variants of the SwiftUI Text views. We used to use: Text(workoutStartDate, style: .relative) and it seems with iOS 18 we can replace it with: Text(workoutStartDate, format: .relative(presentation: .numeric, unitsStyle: .wide)) The former code would auto-increment, allowing a user to look at their Live Activity and see the duration of their workout so far ticking by. The new code does provide a nice relative duration string, but it doesn't auto-increment in the Live Activity's View – I need that functionality. I also updated other Texts in the Live Activity's View to adopt the .timer and .stopwatch FormatStyles. Those auto-increment and update no problem – once I realized I needed to provide a TimeDataSource<Date>.currentDate and not a simple Date. But in this .relative case, there is no auto-incrementing, and I'm guessing it may be due to providing a Date (workoutStartDate) and not a TimeDataSource<Date> as TimeDataSource seems to be the magic to make the Text auto-increment (or it's possible this format simply doesn't support auto-increment? bummer if so since the prior way did). How can I have a TimeDataSource<Date> that vends my arbitrary date (workoutStartDate)? I dug around, didn't find anything, but I wouldn't be surprised if I was overlooking something. PS: I have tried other approaches to solve this, such as simply using a .timer or .stopwatch format. That would change the under experience under iOS 18, and we think degrade it (the relative textual representation is nicer and provides distinction from the .timer also in the same View). We could keep the former approach, despite the minor layout issues under iOS 18. I have considered additional approaches as well – and am totally open to more! Here tho, I am truly curious how one might provide a custom TimeDataSource anchored at a date we provide (perhaps dynamically). Thank you.
1
1
649
Sep ’24
Button Touch Not Canceled in ScrollView on Modal in SwiftUI for iOS 18
When displaying a view with a Button inside a ScrollView using the sheet modifier, if you try to close the sheet by swiping and your finger is touching the Button, the touch is not canceled. This issue occurs when building with Xcode 16 but does not occur when building with Xcode 15. Here is screen cast. https://drive.google.com/file/d/1GaOjggWxvjDY38My4JEl-URyik928iBT/view?usp=sharing Code struct ContentView: View { @State var isModalPresented: Bool = false var body: some View { ScrollView { Button { debugPrint("Hello") isModalPresented.toggle() } label: { Text("Hello") .frame(height: 44) } Button { debugPrint("World") } label: { Text("World") .frame(height: 44) } Text("Hoge") .frame(height: 44) .contentShape(Rectangle()) .onTapGesture { debugPrint("Hoge") } } .sheet(isPresented: $isModalPresented) { ContentView() } } }
13
18
2.2k
Sep ’24
SwiftUI.Stepper bug from `onIncrement` and `onDecrement`?
Ok… I'm baffled here… this is very strange. Here is a SwiftUI app: import SwiftUI @main struct StepperDemoApp: App { func onIncrement() { print(#function) } func onDecrement() { print(#function) } var body: some Scene { WindowGroup { Stepper { Text("Stepper") } onIncrement: { self.onIncrement() } onDecrement: { self.onDecrement() } } } } When I run in the app in macOS (Xcode 16.0 (16A242) and macOS 14.6.1 (23G93)), I see some weird behavior from these buttons. My experiment is tapping + + + - - -. Here is what I see printed: onIncrement() onIncrement() onIncrement() onIncrement() onDecrement() What I expected was: onIncrement() onIncrement() onIncrement() onDecrement() onDecrement() onDecrement() Why is an extra onIncrement being called? And why is one onDecrement dropping on the floor? Deploying the app to iPhone Simulator does not repro this behavior (I see the six "correct" logs from iPhone Simulator).
9
0
685
Sep ’24
SwiftUI bug using .sheet(item:) and button action not called
import SwiftUI extension Int: Identifiable { public var id: Int { return self } } struct ContentView: View { @State var shareSheet: Bool = false @State var selectedNo : Int? var body: some View { VStack { ForEach(0..<2, id: \.self, content: { rowNo in Text("world \(rowNo)") Button(action: { shareSheet.toggle() selectedNo = rowNo }) { Text("Go to world\(rowNo)") } }) } .padding() .sheet(item: $selectedNo) { row in // BUG: when replacing 'row' below with 'selectedNo' nil is passed. ShareView(message:"Hello \(row)" ) } } struct ShareView: View { let message: String var body: some View { Text(message) } } }
Topic: UI Frameworks SubTopic: SwiftUI
2
0
298
Sep ’24
Live Activities Random Loading Spinner
Somewhat rarely, but often enough to be a problem, our live activities will entirely lock up with a frozen progress spinner and a dimmed appearance. We cannot figure out what this spinner means nor how to avoid it as it is not documented anywhere. No logs appear in Xcode nor in Console.app. We have more frequent updates enabled and are sending an update maybe once every 2 minutes, nothing that should cause us to exceed our quota. We cannot reproduce in a debugger Can we get some guidance on how to avoid this?
9
4
1.2k
Sep ’24
Scroll to Top gesture doesn't work when pressed in Status Bar.
I have created a paging app With SwiftUI TabView with TabViewStyle: PageTabViewStyle. In every page there is an UICollectionView holding a list of rows. Now I have used UIViewControllerRepresentable to show the view inside TabView. It works okay, but the Scroll to top gesture doesn't work here when pressed in the iPhones StatusBar. Is it a fault of TabView or I am missing Something? It may happen because of TabViews Scroll property. In UIKIT Views We needed to disable the scrollToTop property of scrollviews (Other then the desired one) is there any public API's for SwiftUI Views in replacement for setting scrollsToTop property?
4
2
527
Sep ’24
WKHaptic Issue when headphones are connected
Info watchOS: 11.0 (22R5348a) *Though has been present since watchOS 10. Issue: Other apps playing music cancel out WKHaptics from firing (low volume and no vibrations) Description When another app is playing music (ex: spotify) in the background while using my app, that uses WKHaptics. The WKHaptics vibrations are non existent as long as headphones are connected. When the headphones are disconnected the vibrations return. Test MVP test app >> https://github.com/mazefest/AppleCodeSupportTestApp
1
0
517
Sep ’24
SwiftUI Document-based apps crash in iOS 18
I am trying to integrate the new iOS 18 document launching experience with my app, but opening or saving files fails with the RC version of Xcode 16. The error I receive is "Publishing changes from background threads is not allowed." I downloaded Apple's new sample code ("Writing app"), and it fails with the same error. Is there an easy fix for this?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
3
0
762
Sep ’24
Struggling with Swift Gesture/Observation
I'm trying to create an equivalent to TabView, but with the difference that the 2nd View slides in over the top of the primary view. Maybe there's a more elegant way of coding this (suggestions appreciated), but I've almost succeeded using the dragGesture. When a user swipes right to left the observed variable showTab2 is set to true, and the 2nd tab glides in over the top of tab 1 and displays 🥳. The only problem is, that when a user happens to start the swipe over a button, the observed status (showTab2) does change as expected but the main view does not catch this change and does not display tab2. And that despite the showTab2 being an @Observable. Any clues what I've missed? Or how to capture that the start of a swipe gesture starts over the top of a button and should be ignored. According to the code in SwipeTabView this screenshot 👆 should never occur. Here's the code: @Observable class myclass { var showTab2 = false } struct SwipeTabView: View { @State var myClass = myclass() @State var dragAmount: CGSize = CGSize.zero var body: some View { VStack { ZStack { GeometryReader { geometryProxy in VStack { tab(tabID: 1, selectedTab: myClass.showTab2) .zIndex(/*@START_MENU_TOKEN@*/1.0/*@END_MENU_TOKEN@*/) .background(.black) .transition(.identity) .swipeable(stateOfViewAdded: $myClass.showTab2, dragAmount: $dragAmount, geometryProxy: geometryProxy, insertion: true) } if myClass.showTab2 || dragAmount.width != 0 { tab(tabID: 2, selectedTab: myClass.showTab2) .zIndex(2.0) .drawingGroup() .transition(.move(edge: .trailing)) .offset(x: dragAmount.width ) .swipeable(stateOfViewAdded: $myClass.showTab2, dragAmount: $dragAmount, geometryProxy: geometryProxy, insertion: false) } } } } } } extension View { func swipeable(stateOfViewAdded: Binding<Bool>, dragAmount: Binding<CGSize>, geometryProxy: GeometryProxy, insertion: Bool) -> some View { self.gesture( DragGesture() .onChanged { gesture in // inserting must be minus, but removing must be positive - hence the multiplication. if gesture.translation.width * (insertion ? 1 : -1 ) < 0 { if insertion { dragAmount.wrappedValue.width = geometryProxy.size.width + gesture.translation.width } else { dragAmount.wrappedValue.width = gesture.translation.width } } } .onEnded { gesture in if abs(gesture.translation.width) > 100.0 && gesture.translation.width * (insertion ? 1 : -1 ) < 0 { withAnimation(.easeOut.speed(Double(gesture.velocity.width))) { stateOfViewAdded.wrappedValue = insertion } } else { withAnimation(.easeOut.speed(Double(gesture.velocity.width))) { stateOfViewAdded.wrappedValue = !insertion } } withAnimation(.smooth) { dragAmount.wrappedValue = CGSize.zero } } ) } } struct tab: View { var tabID: Int var selectedTab: Bool var body: some View { ZStack { Color(tabID == 1 ? .yellow : .orange) VStack { Text("Tab \(tabID) ").foregroundColor(.black) Button(action: { print("Tab2 should display - \(selectedTab.description)") }, label: { ZStack { circle label } }) Text("Tab2 should display - \(selectedTab.description)") } } } var circle: some View { Circle() .frame(width: 100, height: 100) .foregroundColor(.red) } var label: some View { Text("\(tabID == 1 ? ">>" : "<<")").font(.title).foregroundColor(.black) } }
4
0
745
Sep ’24
LongPressGesture does not work as expected in Xcode Version 16.0 (16A242) and iOS 18
When I copy and paste example code in apple developer documentation, LongPressGesture does not work as expected in Xcode Version 16.0 (16A242) and iOS 18. It seems updating(_:body:) method does not work when used with LongPressGesture. When I make a breakpoint in updating(_:body:) method and long press the blue circle on the screen of simulator(or device), it is expected to be caught in breakpoint or it is expected that color of circle turns from blue to red to green. However, it is not caught in breakpoint and never turns to red. Question of Stackoverflow is about same issue and I can not use onLongPressGesture method to implement required feature of my app. Development environment: Xcode Version 16.0 (16A242), macOS 14.5 Run-time configuration: iOS 18.0
15
18
2.4k
Sep ’24
Lag When Dismissing Keyboard in ScrollView Inside NavigationStack in SwiftUI
I’m experiencing a lag in SwiftUI when dismissing the keyboard inside a ScrollView that’s within a NavigationStack. When the keyboard is opened and then dismissed, the view seems to lag as it resizes back to its original state. This issue occurs when the content is in a ScrollView. I’m using iOS 17, and the resizing of the content feels choppy after the keyboard interaction. Here’s a simplified version of my code: VStack { NavigationStack { ScrollView { createAllForm } .onAppear { if #available(iOS 17.0, *) { Self.addTripOpen.sendDonation() } } .toolbar { ToolbarItem(placement: .topBarLeading) { Button(action: { presentationMode.wrappedValue.dismiss() }, label: { Image(systemName: IconsEnum.closeIcon).foregroundColor(.gray) }) } } .toolbar { ToolbarItem(placement: .topBarTrailing) { Button(LocalizedText.create, action: { focusedField = nil withAnimation { addTripViewModel.creatingTrip = true addTripViewModel.addTripToFirebase(presentationMode: presentationMode) } }).disabled(addTripViewModel.disableCreate).foregroundColor(Color(addTripViewModel.disableCreate ? ColorsEnum.greyColor : ColorsEnum.tripBlue)) } } .navigationTitle(LocalizedText.createTrip) .navigationBarTitleDisplayMode(.inline) .isLoadingView(isLoading: addTripViewModel.creatingTrip) .alert(addTripViewModel.alertMessage, isPresented: $addTripViewModel.showingAlert) { Button(LocalizedText.acceptLabel, role: .cancel) { } } .onDisappear { if addTripViewModel.isCreated { processCompletedCount += 1 } if let currentAppVersion = Bundle.currentAppVersion, processCompletedCount >= 2, currentAppVersion != lastVersionPromptedForReview, addTripViewModel.isCreated { presentReview() lastVersionPromptedForReview = currentAppVersion } onDismiss(addTripViewModel.isCreated) } .sheet(isPresented: $addTripViewModel.showingAddUsers) { AddUsers().environmentObject(addTripViewModel) } } }
2
0
429
Sep ’24
Tab button's ax identifier is missing when using `.sidebarAdaptable` TabViewStyle
Hello, I found that if you apply the new .sidebarAdaptable tab view style, the accessibility identifiers of tab bar buttons are missing. import SwiftUI struct ContentView: View { var body: some View { TabView { Tab("Received", systemImage: "tray.and.arrow.down.fill") { Text("Received") } .accessibilityIdentifier("tab.received") // 👀 Tab("Sent", systemImage: "tray.and.arrow.up.fill") { Text("Sent") } .accessibilityIdentifier("tab.sent") // 👀 Tab("Account", systemImage: "person.crop.circle.fill") { Text("Account") } .accessibilityIdentifier("tab.account") // 👀 } .tabViewStyle(.sidebarAdaptable) // 👈 if remove this, ax identifiers are ok } } #Preview { ContentView() } The identifiers automatically appear after a few seconds. But this behaviour breaks a lot of the UI test cases.
2
0
453
Sep ’24
Is @ObservationIgnored necessary for private var?
In SwiftUI's ViewModel class that are @Observable, is it necessary to annotate private fields as @ObservationIgnored? I'm not sure if adding @ObservationIgnored to these fields will get performance gains, since there are no SwiftUI structs referencing these fields because they're private. However, I'd like to know what's the recommended approach here? While this might not seem obvious for the example below, however, sometimes I have private fields that are changing pretty frequently. For these frequently changed fields, I think the performance gains will be larger. Example: @Observable class UserProfileViewModel { var userName: String? var userPhoneNumber: String? private var isFetchingData = false } vs @Observable class UserProfileViewModel { var userName: String? var userPhoneNumber: String? @ObservationIgnored private var isFetchingData = false }
2
0
1.6k
Sep ’24
AppIntent - Widget & ControlWidget
Hey all, iOS 18 - RC I have an app that supports both Widgets and ControlWidget, which resides on the same AppIntent. The following sync works fine when any action is taken on any of the three players - App - Widget - both directions - works as expected App - Control Widget - both directions - works as expected However - Widget - ControlWidget - the UI not always sync in real time (the values are ok) So if for instance I increase a counter on the widget from 1 to 2, the comtrolwidget will still show 1, but if I tap it, it will increase to 3. For any update/action taken on the AppInten, I call - WidgetCenter.shared.reloadAllTimelines() ControlCenter.shared.reloadAllControls() Any idea how to ensure this sync? Thanks a lot! Dudi
2
0
651
Sep ’24