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

SwiftUI Documentation

Post

Replies

Boosts

Views

Activity

TextField("x", value: $y, format: .currency(code: "USD")) Behaves oddly
Hello, If we have the following code: import SwiftUI struct ContentView: View { @State private var usdAmount = 0.0 var body: some View { VStack { Text("Currency Form") Form { TextField("Dollar Amount", value: $usdAmount, format: .currency(code: "USD")) // 1 // TextField("Dollar Amount", value: $usdAmount, format: .number) // 2 .keyboardType(.numberPad) } Text("usdAmount = \(usdAmount)") } .padding() } } When the line marked // 1 is left uncommented (the TF has a .currency() formatter applied) the behaviour of the TF is rather odd. Upon display it shows US$0.00 Upon editing the value the US$ part remains. So if the user enters US$123 the value is not stored into the usdAmount property. The User must delete all the existing text and enter e.g. 456.78 and then the value is stored to the usdAmount property. When Line //1 is commented-out and line //2 is uncommented then the TF behaves correctly because the TF value is just 0 on first display. Should we not use the .currency() format? Or am I using it wrong? Thanks.
0
0
21
1h
.searchble Keyboard Incorrectly Showing After Presenting Popup
I have a list view that is accessed by pressing a button from a pervious view, all are which are in a NavigationView from the Main view. For this list view I have applied the .searchable tag to allow users to search through the list. When a user taps on an item in the list a .sheet will present to present a more detailed overview. The issue happens that if a user searches for an item in the list, and taps on the item to present the sheet the keyboard will disappear while the sheet presents but then pop back up despite still editing the search bar on the list view. Currently, my view is roughly set up like this. var body: some View { ThemesListView } .sheet(item: $presentedSheetTheme, onDismiss: { fetchedThemes.nsSortDescriptors = themeListSort.nsSortDescriptorObject }) {themeToPassOn in EditThemeView(themeToEdit: themeToPassOn) } var ThemesListView: some View { List { Button(action: { presentedSheetTheme = theme } } .searchable(text: query, placement: .navigationBarDrawer(displayMode: .always)) } I have tried many things to fix this, even finding this thread (https://forums.developer.apple.com/forums/thread/746826) however none of the solutions were any help. Here is a link (https://imgur.com/a/KKJ27iY) to a video of the issue. This issue seems to be fixed in some iOS versions such as 17.0 and 18.0 Beta 1 however many other versions such as 17.4 or 17.5 still experience this issue meaning I still need to fix it for these versions. Any help would be greatly appreciated, thanks!
0
0
42
9h
NavigationBar Back Button Color
I am trying to change the colour of the "Back" from blue to white but having difficulty do so, if anyone can suggest me a better way to do it would be grateful. "import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let appearance = UINavigationBarAppearance() appearance.configureWithOpaqueBackground() appearance.backgroundColor = UIColor(red: 0.216, green: 0.776, blue: 0.349, alpha: 1) appearance.titleTextAttributes = [.foregroundColor: UIColor.white] appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white] UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().tintColor = .white return true } }"
0
0
53
14h
No containerBackground content on Widget in iOS 18 tinted home screen style
The widget I have create for iOS 17 uses the containerBackground to display an image in the background. This works fine. But when I set the home screen to the iOS 18 tinted option the background disappears. I want the background to stay because it contains an image of that is meaningful to the user. I use the following code: @ViewBuilder var body: some View { if let memory = entry.memory, let uiImage = memory.image { Group { if entry.showCaption { memoryBody(with: memory) } else { Color.white.opacity(0.0000000001) } } .foregroundStyle(.white) .widgetBackground( Image(uiImage: uiImage) .resizable() .scaledToFill() ) } else if let memory = entry.memory { memoryBody(with: memory) .widgetBackground(Color.gray) } else { noMemoryBody() } } extension View { func widgetBackground(_ backgroundView: some View) -> some View { if #available(iOSApplicationExtension 17.0, *) { return containerBackground(for: .widget) { backgroundView } } else { return background(backgroundView) } } }
1
0
62
19h
selection multiple buttons
struct Question { var title: String var options : [String] } struct SimpleQuestions: View { @State var selectedOption = "" @State var question = Question(title: "question title", options: \["one", "two", "three"\]) var body: some View { VStack { Text(question.title) ForEach(question.options, id: \.self) { option in HStack { Button { selectedOption = option } label: { Circle() .foregroundStyle(selectedOption == option ? .blue : .gray.opacity(0.5)) .frame(width: 23, height: 23) }}}}}
2
0
43
20h
Live Activity compact timers
The Human Interface Guidelines for Live Activities provide several examples of compact presentations that have a timer counting down formatted like "3min" or "3m", similar to the timers in the Clock app. Such examples can be found in this Stack Overflow question: https://stackoverflow.com/questions/77551496/is-it-possible-to-make-an-only-minutes-1m-countdown-in-a-live-activity A Timer initialized with init(timerInterval:pauseTime:countsDown:showsHours:) has provided a live countdown timer in widgets and live activities, but the formatting is limited. And since live activities come with limitations that prevent us from using Timers, there's no real way to implement this kind of thing ourselves. What is Apple's official guidance for implementing this kind of timer for the compact presentation?
1
0
73
1d
How to disable exporting a content from the Quicklook preview in vision OS 2.0?
It is pretty cool that you can preview Spatial Photos and Videos with just PreviewApplication.push(...), but the issue is, you can disable the editing, but buttons to export content to Files still going to be there. So if my application uses this to showcase some proprietary or licensed content in my application, I cannot really disable an option of exporting it from my app. And another, more major issue, a lot of developers would not even know that user can save Previewed item, as this Save to Files button is not that easy to find.
0
0
64
1d
ShareLink Rich URL Previews in Messages
I am using ShareLink to share a URL. That URL is using the opengraph tags for audio (og:audio). If a users taps messages on the share sheet before the preview is rendered then sends the messages, they never see the rich URL preview. I don't know if this is a bug in the share sheet or if there is something I should be doing differently. If the user waits until the preview is rendered on the share sheet everything works fine. Also, if I go to Safari and share the same URL it works as expected.
3
0
72
1d
Specific Dynamic Type settings cause Spacer to stop spacing
I've filed FB13900806 for this but I'm looking for workarounds or guidance on better ways to do this. tl;dr: My view layout breaks when Dynamic Type is set to Medium, AX1, or AX2, because a Spacer() incorrectly collapses to zero height. Details: I'm trying to build out a vertically scrollable view with the following: A top header view that does not scroll. A scroll view, which contains a variable amount of content that will not always fill the screen A bottom view that is scrollable, but should be bottom aligned if the content doesn't fill the screen. Here's how it should look: I'm using the following logic to achieve this, which works for most Dynamic Type sizes: import SwiftUI struct CollapsingSpacerView: View { var body: some View { VStack { Text("Non scrollable, top aligned content") GeometryReader { geometry in ScrollView(.vertical) { VStack(spacing: 20) { Text("Scrollable, top aligned content") Color.red .frame(width: 200, height: 200) Spacer() Text("Scrollable, bottom aligned content") } .frame(minWidth: geometry.size.width, minHeight: geometry.size.height) .border(.blue) } .border(.green) } } } } #Preview("spacer") { CollapsingSpacerView() } However, when I change the Dynamic Type setting to one of: Medium, AX1, or AX2, the Spacer stops spacing and all of the scrollable content gets compressed around the vertical center, like this: Is there a better way to get this layout without relying on Spacer? One workaround I've found is to give the non-scrolling header a fixed height, but that's less than ideal and would love to hear other options.
1
0
49
1d
SwiftUI Crashes only on A-Chip device
Hey there, I am currently developing a SwiftUI app for iPad. However, I am currently experiencing a behavior, that I can not explain. My code creates a user-management view. This all works fine if I run it on my iPad Pro with Apple-M-Chip. However, if I try to run the exact same code on an older A-Chip iPad Air (4th Generation), my app crashes. I can recreate this behavior in the simulator on my Mac. When I run it on an iPad Pro Simulator, all works fine. When I run it in an iPad Air (4th Gen) simulator, the app becomes unresponsive. Unfortunatley Xcode does not throw any error that I could use to identify the problem. Currently I solved it with a work-around: As soon as I add a Spacer (even with a with of 0) the code works as expected and the UI is shown. As soon as I remove the Spacer, it crashes again. HStack(alignment: .top){ VStack{ if(data.ui_user.id != -1){ HStack{ Text("Employee-No.:") Spacer() TextField("Employee-No.", value: $data.ui_user.id, format: .number).textFieldStyle(.roundedBorder).disabled(true).containerRelativeFrame(.horizontal, count: 10, span: 3, spacing: 0) } } HStack{ Text("Family Name:") Spacer() TextField("Family Name", text: $data.ui_user.familyName).textFieldStyle(.roundedBorder).containerRelativeFrame(.horizontal, count: 10, span: 3, spacing: 0) } HStack{ Text("Given Name:") Spacer() TextField("Given Name", text: $data.ui_user.givenName).textFieldStyle(.roundedBorder).containerRelativeFrame(.horizontal, count: 10, span: 3, spacing: 0) } HStack{ Text("Role:") Spacer() Picker("Select role", selection: $data.selectedRole){ ForEach(0 ..< self.availableRoles.count, id:\.self){ Text(self.availableRoles[$0].name ?? "???").tag($0) } }.pickerStyle(.menu).containerRelativeFrame(.horizontal, count: 10, span: 3, spacing: 0).background(.drkBlueContainer).cornerRadius(5).foregroundColor(.drkOnBlueContainer).tint(.drkOnBlueContainer) //If I add this spacer, it works. If not, it crashes //Spacer().frame(width: 0.0) }.disabled(noEditAdmin && data.ui_user.id != -1) } Divider() VStack{ HStack{ Text("Created at:") Spacer() TextField("Created at", value: $data.ui_user.createdAt, format: .dateTime).textFieldStyle(.roundedBorder).disabled(true).containerRelativeFrame(.horizontal, count: 10, span: 3, spacing: 0) } HStack{ Toggle("Terminates:", isOn: $data.ui_user.expires) .padding(.trailing, 2.0) } if(data.ui_user.expires){ HStack{ DatePicker("Terminates at:", selection: $data.ui_user.expiresAt, displayedComponents: .date).pickerStyle(.automatic) }} } } If anyone has any ideas, I would be very grateful for a hint :-)
1
0
44
1d
How can I pass value from TextField to Alert?
Hello, I have a view has 4 textfields and a button. I want to show 4 values from textfields on popup messagebox. so I wrote a following codes. @State private var lender = "" @State private var amount = "" @State private var rate = "" @State private var duration = "" Button(action: {showingAlert = true}, label: { Text("Inputs") .padding() .background(RoundedRectangle(cornerRadius: 10).strokeBorder()) }).padding(10) .accentColor(.green) .alert("Inputs", isPresented: $showingAlert){ Text("Lender: \(lender)").font(.title3) Text("Amount:\(amount)").font(.title3) Text("Rate:\(rate)") Text("Duration:\(duration)") Button("OK", role: .cancel){} } When I run this code, Alert popped up but there's no values. If someone point my mistakes, I'd very appreciated. thanks, c00012
1
0
56
1d
Swift Charts: .hour annotations do not appear
I'm building a Swift Chart that displays locations in a horizontal timeline. The chart is scrollable. When the chart is zoomed in, I want to show an annotation for every 6 hours. Unfortunately, when axisMarks are set to .stride(by: .hour, count: 6), the annotations do not appear for the first several months in the timeline. I tried setting .stride(by: .minute, count: 360), but the result is the same. Is this a Swift Charts bug, or am I doing something wrong? A reproducible example is below. To reproduce: Run the code below See that annotations are missing at the leading edge of the chart. They only show up from a certain point on the chart's domain. Tested on various iPhone and iPad simulators and physical devices, the issue appears everywhere. P.S. I am aware that the example code below is not performant and that the annotations overlap when the chart is zoomed out. I have workarounds for that, but it's beyond the scope of my question and the minimum reproducible example. struct ChartAnnotationsBug: View { /// Sample data let data = SampleData.samples let startDate = SampleData.samples.first?.startDate ?? Date() let endDate = Date() /// Scroll position of the chart, expressed as Date along the x-axis. @State var chartPosition: Date = SampleData.samples.first?.startDate ?? Date() /// Sets the granularity of the shown view. @State var visibleDomain: VisibleDomain = .month var body: some View { Chart(data, id: \.id) { element in BarMark(xStart: .value("Start", element.startDate), xEnd: .value("End", element.endDate), yStart: 0, yEnd: 50) .foregroundStyle(by: .value("Type", element.type.rawValue)) .clipShape(.rect(cornerRadius: 8, style: .continuous)) } .chartScrollableAxes(.horizontal) // enable scroll .chartScrollPosition(x: $chartPosition) // track scroll offset .chartXVisibleDomain(length: visibleDomain.seconds) .chartXScale(domain: startDate...endDate) .chartXAxis { AxisMarks(values: .stride(by: .hour, count: 6)) { value in if let date = value.as(Date.self) { let hour = Calendar.current.component(.hour, from: date) if hour == 0 { // midnight AxisValueLabel(collisionResolution: .truncate) { VStack(alignment: .leading) { Text(date, format: .dateTime.hour().minute()) Text(date, format: .dateTime.weekday().month().day()) .bold() } } AxisTick(stroke: .init(lineWidth: 1)) } else if [6, 12, 18].contains(hour) { // period AxisValueLabel(collisionResolution: .truncate) { Text(date, format: .dateTime.hour().minute()) } AxisTick(length: .label) } } } } .frame(height: 100) .padding(.bottom, 40) // for overlay picker .overlay { Picker("", selection: $visibleDomain.animation()) { ForEach(VisibleDomain.allCases) { variant in Text(variant.label) .tag(variant) } } .pickerStyle(.segmented) .frame(width: 240) .padding(.trailing) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing) } //: overlay } //: body } //: struct // MARK: - Preview #Preview { ChartAnnotationsBug() } // MARK: - Data enum SampleDataType: String, CaseIterable { case city, wood, field var label: String { switch self { case .city: "City" case .wood: "Wood" case .field: "Field" } } } enum VisibleDomain: Identifiable, CaseIterable { case day case week case month var id: Int { self.seconds } var seconds: Int { switch self { case .day: 3600 * 24 * 2 case .week: 3600 * 24 * 10 case .month: 3600 * 24 * 40 } } var label: String { switch self { case .day: "Days" case .week: "Weeks" case .month: "Months" } } } struct SampleData: Identifiable { let startDate: Date let endDate: Date let name: String let type: SampleDataType var id: String { name } static let samples: [SampleData] = [ .init(startDate: Date.from(year: 2024, month: 3, day: 1, hour: 23, minute: 59), endDate: Date.from(year: 2024, month: 3, day: 10), name: "New York", type: .city), .init(startDate: Date.from(year: 2024, month: 3, day: 10, hour: 6), endDate: Date.from(year: 2024, month: 3, day: 20), name: "London", type: .city), .init(startDate: Date.from(year: 2024, month: 3, day: 20), endDate: Date.from(year: 2024, month: 4, day: 10), name: "Backcountry ABC", type: .field), .init(startDate: Date.from(year: 2024, month: 4, day: 10), endDate: Date.from(year: 2024, month: 4, day: 20), name: "Field DEF", type: .field), .init(startDate: Date.from(year: 2024, month: 4, day: 20), endDate: Date.from(year: 2024, month: 5, day: 10), name: "Wood 123", type: .wood), .init(startDate: Date.from(year: 2024, month: 5, day: 10), endDate: Date.from(year: 2024, month: 5, day: 20), name: "Paris", type: .city), .init(startDate: Date.from(year: 2024, month: 5, day: 20), endDate: Date.from(year: 2024, month: 6, day: 5), name: "Field GHI", type: .field), .init(startDate: Date.from(year: 2024, month: 6, day: 5), endDate: Date.from(year: 2024, month: 6, day: 10), name: "Wood 456", type: .wood), .init(startDate: Date.from(year: 2024, month: 6, day: 10), endDate: Date(), name: "Field JKL", type: .field) ] } extension Date { /** Constructs a Date from a given year (Int). Use like `Date.from(year: 2020)`. */ static func from(year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil) -> Date { let components = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute) guard let date = Calendar.current.date(from: components) else { print(#function, "Failed to construct date. Returning current date.") return Date() } return date } }
0
0
42
2d
Swift Charts: Changing chartXVisibleDomain changes chartScrollPosition
I'm building a Swift Chart that displays locations in a horizontal timeline. The chart is scrollable. Unfortunately, when chartScrollPosition is offset by some amount (i.e. the user has scrolled the chart), changing chartXVisibleDomain results in chartScrollPosition jumping backwards by some amount. This results in bad user experience. A minimum reproducible example is below. To reproduce: Run the code below Using the picker, change chartXVisibleDomain. ThechartScrollPosition remains the same, as expected. Scroll the chart on the horizontal axis. Using the picker, change chartXVisibleDomain. ThechartScrollPosition changes unexpectedly. You can verify this by watching the labels at the bottom of the chart. The chart simply ends up showing a different area of the domain. Tested on various iPhone and iPad simulators and physical devices, the issue appears everywhere. Is this a SwiftUI bug, or am I doing something wrong? struct ScrollableChartBug: View { /// Sample data let data = SampleData.samples let startDate = SampleData.samples.first?.startDate ?? Date() let endDate = Date() /// Scroll position of the chart, expressed as Date along the x-axis. @State var chartPosition: Date = SampleData.samples.first?.startDate ?? Date() /// Sets the granularity of the shown view. @State var visibleDomain: VisibleDomain = .year var body: some View { Chart(data, id: \.id) { element in BarMark(xStart: .value("Start", element.startDate), xEnd: .value("End", element.endDate), yStart: 0, yEnd: 50) .foregroundStyle(by: .value("Type", element.type.rawValue)) .clipShape(.rect(cornerRadius: 8, style: .continuous)) } .chartScrollableAxes(.horizontal) // enable scroll .chartScrollPosition(x: $chartPosition) // track scroll offset .chartXVisibleDomain(length: visibleDomain.seconds) .chartXScale(domain: startDate...endDate) .chartForegroundStyleScale { typeName in // custom colors for bars and for legend SampleDataType(rawValue: typeName)?.color ?? .clear } .chartXAxis { AxisMarks(values: .stride(by: .month, count: 1)) { value in if let date = value.as(Date.self) { AxisValueLabel { Text(date, format: .dateTime.year().month().day()) .bold() } AxisTick(length: .label) } } } .frame(height: 90) .padding(.bottom, 40) // for overlay picker .overlay { Picker("", selection: $visibleDomain.animation()) { ForEach(VisibleDomain.allCases) { variant in Text(variant.label) .tag(variant) } } .pickerStyle(.segmented) .frame(width: 240) .padding(.trailing) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing) } //: overlay } //: body } //: struct // MARK: - Preview #Preview { ScrollableChartBug() } // MARK: - Data enum SampleDataType: String, CaseIterable { case city, wood, field var color: Color { switch self { case .city: .gray case .wood: .green case .field: .brown } } var label: String { switch self { case .city: "City" case .wood: "Wood" case .field: "Field" } } } enum VisibleDomain: Identifiable, CaseIterable { case day case week case month case year var id: Int { self.seconds } var seconds: Int { switch self { case .day: 3600 * 24 * 2 case .week: 3600 * 24 * 10 case .month: 3600 * 24 * 40 case .year: 3600 * 24 * 400 } } var label: String { switch self { case .day: "Days" case .week: "Weeks" case .month: "Months" case .year: "Years" } } } struct SampleData: Identifiable { let startDate: Date let endDate: Date let name: String let type: SampleDataType var id: String { name } static let samples: [SampleData] = [ .init(startDate: Date.from(year: 2022, month: 3, day: 1), endDate: Date.from(year: 2022, month: 3, day: 10), name: "New York", type: .city), .init(startDate: Date.from(year: 2022, month: 3, day: 20, hour: 6), endDate: Date.from(year: 2022, month: 5, day: 1), name: "London", type: .city), .init(startDate: Date.from(year: 2022, month: 5, day: 4), endDate: Date.from(year: 2022, month: 7, day: 5), name: "Backcountry ABC", type: .field), .init(startDate: Date.from(year: 2022, month: 7, day: 5), endDate: Date.from(year: 2022, month: 10, day: 10), name: "Field DEF", type: .field), .init(startDate: Date.from(year: 2022, month: 10, day: 10), endDate: Date.from(year: 2023, month: 2, day: 10), name: "Wood 123", type: .wood), .init(startDate: Date.from(year: 2023, month: 2, day: 10), endDate: Date.from(year: 2023, month: 3, day: 20), name: "Paris", type: .city), .init(startDate: Date.from(year: 2023, month: 3, day: 21), endDate: Date.from(year: 2023, month: 10, day: 5), name: "Field GHI", type: .field), .init(startDate: Date.from(year: 2023, month: 10, day: 5), endDate: Date.from(year: 2024, month: 3, day: 5), name: "Wood 456", type: .wood), .init(startDate: Date.from(year: 2024, month: 3, day: 6), endDate: Date(), name: "Field JKL", type: .field) ] } extension Date { /** Constructs a Date from a given year (Int). Use like `Date.from(year: 2020)`. */ static func from(year: Int? = nil, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil) -> Date { let components = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute) guard let date = Calendar.current.date(from: components) else { print(#function, "Failed to construct date. Returning current date.") return Date() } return date } }
0
0
48
2d
How to stop current symbolEffect before contentTransition?
Currently I have a cloud syncing status icon that utilizes the new "rotate" symbol effect introduced with iOS 18. After the synchronization is complete, I want to replace the icon with another symbol using the "replace" contentTransition. These are the codes I used to achieve this: Image(systemName: coreDataStack.cloudKitUIStatus.symbolName) .foregroundColor(coreDataStack.cloudKitUIStatus.symbolColor) .symbolEffect(.rotate, isActive: coreDataStack.cloudKitUIStatus == .inProgress) .contentTransition(.symbolEffect(.replace)) However, the rotation effect continues after the replace transition. This means the new icon (checkmark.icloud.fill) continues the spinning animation from the previous symbol (arrow.triangle.2.circlepath.icloud) until it completes the full circle. How can I stop the rotation immediately before the contentTransition? Thanks!
0
1
39
2d