I am using a Mac Catalyst with SwiftUI for our document-based app with DocumentGroup. The issue is that when we create a new document or open an existing one, the opened view is completely blank. It is only blank/empty when the "Optimzie for Mac" is checked. If it is "Scaled t oMatch iPad", then it works well.
Xcode 16.1
macOS 15.1
struct DocumentGroupTestApp: App {
var body: some Scene {
DocumentGroup(newDocument: WritingAppDocument()) { file in
TestView() // it is empty when it gets opened. It does not work if the option "Optimize for Mac" is checked. If it is scale iPad, then it works.
}
}
}
struct TestView: View {
var body: some View {
Text("Hello, World!")
}
}
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
I used to be able to display all app icon assets in the UI using UIImage(named: String)
However, it seems this method has been deprecated in Xcode 16 and the iOS 18.
How can I display app icon assets in the UI now, without duplicating the assets?
Hello,
I'm seeing a strange error on another user's device where the SwiftUI file importer doesn't do anything at all. When selecting multiple files and hitting "open", the importer just freezes. Here's a video showing the problem: https://streamable.com/u5grgy
I'm unable to replicate on my own device, so I'm not sure what could be going on. I have startAccessingSecurityScopedResource and stopAccessingSecurityResource everywhere I access a file from fileImporter as well.
I made a LockScreen ControlWidget with CameraCaptureIntent, but I found launch my main app from Control Widget, SceneDelegate will be called like below:
sceneWillEnterForeground
sceneDidBecomeActive
sceneWillResignActive
sceneDidBecomeActive
sceneWillResignActive be called, is it normal?
it make my app camera launch with a delay.
Hi,
In this year WWDC 2024 conference a new update to SwiftData allows it to connect to other kind of data sources or databases, at ;east this is what I understood, since then didn't see any tutorial or help document on how to do that for example connecting SwiftData to Firebase if possible ?
Kind Regards
Hi
When connecting a SwiftData module property to a SwiftUI view such as a text field and the field changes by user the property get updated in the SwiftData database, now suppose I want to run a validation code or delay updates to Database till use click a submit button how to do that ? delay those auto updates if we can name it ?
Kind Regards
Code Example
import SwiftUI
import SwiftData
struct GListSel2: View {
@Bindable var patient: Patient
var body: some View {
HStack {
TextField("Gender", text: $patient.gender)
}
}
}
Hello!
Is there any way to detect when the animation appearing in iOS 18 on switching tab items completes? This applies to TabView in SwiftUI.
I've got a List containing Colour objects. Each colour may have an associated Project Colour object.
What I'm trying to do is set it up so that you can tap a cell and it will add/remove a project colour.
The adding/removing is working, but each time I do so, it appears the whole view is reloaded, the scroll position is reset and any predicate is removed.
This code I have so far
List {
ForEach(colourList) { section in
let header : String = section.id
Section(header: Text(header)) {
ForEach(section) { colour in
HStack {
if checkIfProjectColour(colour: colour) {
Image(systemName: "checkmark")
}
VStack(alignment: .leading){
HStack {
if let name = colour.name {
Text(name)
}
}
}
Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
if checkIfProjectColour(colour: colour) {
removeProjectColour(colour: colour)
} else {
addProjectColour(colour: colour)
}
}
}
}
}
.onAppear() {
filters = appSetting.filters
colourList.nsPredicate = getFilterPredicate()
print("predicate: on appear - \(String(describing: getFilterPredicate()))")
}
.refreshable {
viewContext.refreshAllObjects()
}
}
.searchable(text: $searchText)
.onSubmit(of: .search) {
colourList.nsPredicate = getFilterPredicate()
}
.onChange(of: searchText) {
colourList.nsPredicate = getFilterPredicate()
}
The checkIfProjectColour function
func checkIfProjectColour(colour : Colour) -> Bool {
if let proCols = project.projectColours {
for proCol in proCols {
let proCol = proCol as! ProjectColour
if let col = proCol.colour {
if col == colour {
return true
}
}
}
}
return false
}
and the add/remove functions
func addProjectColour(colour : Colour) {
let projectColour = ProjectColour(context: viewContext)
projectColour.project = project
projectColour.colour = colour
colour.addToProjectColours(projectColour)
project.addToProjectColours(projectColour)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
func removeProjectColour(colour: Colour) {
if let proCols = project.projectColours {
for proCol in proCols {
let proCol = proCol as! ProjectColour
if let col = proCol.colour {
if col == colour {
viewContext.delete(proCol)
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
}
}
PLATFORM AND VERSION
iOS
Development environment: Xcode 16, macOS 15.0.1
Run-time configuration: iOS 18.1
DESCRIPTION OF THE PROBLEM
I am experiencing inconsistent behaviour in SwiftUI when attempting to hide the status bar programmatically. Specifically, I want the status bar to be hidden only during the AreaQuizView, but the behaviour is not persistent across different views. On some views, the status bar hides correctly, while on others, it does not. I am using .statusBarHidden() to achieve this.
STEPS TO REPRODUCE
In AreasView, apply .statusBarHidden() to the outermost VStack. The status bar hides as expected.
In AreaQuizView, apply the same .statusBarHidden() to the outermost stack. However, in this view, the status bar remains visible.
In the Build Targets, if I set Status bar is initially hidden to YES in the info.plist, I lose control over the status bar entirely.
EXPECTED BEHAVIOUR
I would expect that .statusBarHidden() would behave consistently across all views, allowing me to programmatically hide the status bar only during AreaQuizView and leave it visible for other views.
ADVICE REQUESTED
Could you please advise on how to achieve the desired behaviour, where the status bar is only hidden during AreaQuizView? I would also appreciate any guidance on ensuring consistent behaviour across different views.
Please advise.
I have a Map in SwiftUI using MapKit and the map has several annotations and MapCircles added to it. I need to have the ability to center the map on a specific latitude and longitude. The issue is that the map instead is centering so that all annotations and MapCircles etc. are visible.
How can I have it disregard items added to the map and center the map at a specific latitude and longitude and ideally, control the zoom level of the map also?
I figured it would be as simple as changing the selection variable back to nil, but that seemingly has no effect. The Table row stays selected, and if I press on it again, it won't trigger my .navigationDestination(). How can I resolve this? I found another post asking the same thing, with no resolution. The only way I can clear it is by clicking with my mouse somewhere else.
@State private var selectedID: UUID? = nil
Table(tableData, selection: $selectedID, sortOrder: $sortOrder)...
.navigationDestination(item: $selectedID, destination: { id in
if let cycle = cycles.first(where: {$0.id == id}) {
CycleDetailView(cycle: cycle)
.onDisappear(perform: {
selectedID = nil
})
}
})
I have an app for musicians that works with Songs and Setlists. The logical structure is as follows:
A Setlist contains Songs.
A Song has Sections, which include Lines (chords & lyrics).
I want to view my Setlist in a "Page View," similar to a book where I can swipe through pages. In this view, the Song Sections are wrapped into columns to save screen space. I use a ColumnsLayout to calculate and render the columns, and then a SplitToPages modifier to divide these columns into pages.
Problem: The TabView sometimes behaves unexpectedly when a song spans multiple pages during rendering. This results in a transition that is either not smooth or stops between songs.
Is there a better way to implement this behavior? Any advice would be greatly appreciated.
struct TestPageView: View {
struct SongWithSections: Identifiable {
var id = UUID()
var title: String
var section: [String]
}
var songSetlistSample: [SongWithSections] {
var songs: [SongWithSections] = []
//songs
for i in 0...3 {
var sections: [String] = []
for _ in 0...20 {
sections.append(randomSection() + "\n\n")
}
songs.append(SongWithSections(title: "Song \(i)", section: sections))
}
return songs
}
func randomSection() -> String {
var randomSection = ""
for _ in 0...15 {
randomSection.append(String((0..<Int.random(in: 3..<10)).map{ _ in "abcdefghijklmnopqrstuvwxyz".randomElement()! }) + " ")
}
return randomSection
}
var body: some View {
GeometryReader {geo in
TabView {
ForEach(songSetlistSample, id:\.id) {song in
let columnWidth = geo.size.width / 2
//song
ColumnsLayout(columns: 2, columnWidth: columnWidth, height: geo.size.height) {
Text(song.title)
.font(.largeTitle)
ForEach(song.section, id:\.self) {section in
Text(section)
}
}
.modifier(SplitToPages(pageWidth: geo.size.width, id: song.id))
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
}
}
}
public struct ColumnsLayout: Layout {
var columns: Int
let columnWidth: CGFloat
let height: CGFloat
let spacing: CGFloat = 10
public static var layoutProperties: LayoutProperties {
var properties = LayoutProperties()
properties.stackOrientation = .vertical
return properties
}
struct Column {
var elements: [(index: Int, size: CGSize, yOffset: CGFloat)] = []
var xOffset: CGFloat = .zero
var height: CGFloat = .zero
}
public func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> CGSize {
let columns = arrangeColumns(proposal: proposal, subviews: subviews, cache: &cache)
guard let maxHeight = columns.map({ $0.height}).max() else {return CGSize.zero}
let width = Double(columns.count) * self.columnWidth
return CGSize(width: width, height: maxHeight)
}
public func placeSubviews(in bounds: CGRect,
proposal: ProposedViewSize,
subviews: Subviews,
cache: inout Cache) {
let columns = arrangeColumns(proposal: proposal, subviews: subviews, cache: &cache)
for column in columns {
for element in column.elements {
let x: CGFloat = column.xOffset
let y: CGFloat = element.yOffset
let point = CGPoint(x: x + bounds.minX, y: y + bounds.minY)
let proposal = ProposedViewSize(width: self.columnWidth, height: proposal.height ?? 100)
subviews[element.index].place(at: point, anchor: .topLeading, proposal: proposal)
}
}
}
private func arrangeColumns(proposal: ProposedViewSize, subviews: Subviews, cache: inout Cache) -> [Column] {
var currentColumn = Column()
var columns = [Column]()
var colNumber = 0
var currentY = 0.0
for index in subviews.indices {
let proposal = ProposedViewSize(width: self.columnWidth, height: proposal.height ?? 100)
let size = subviews[index].sizeThatFits(proposal)
let spacing = size.height > 0 ? spacing : 0
if currentY + size.height > height {
currentColumn.height = currentY
columns.append(currentColumn)
colNumber += 1
currentColumn = Column()
currentColumn.xOffset = Double(colNumber) * (self.columnWidth)
currentY = 0.0
}
currentColumn.elements.append((index, size, currentY))
currentY += size.height + spacing
}
currentColumn.height = currentY
columns.append(currentColumn)
return columns
}
}
struct SplitToPages: ViewModifier {
let pageWidth: CGFloat
let id: UUID
@State private var pages = 1
func body(content: Content) -> some View {
let contentWithGeometry = content
.background(
GeometryReader { geometryProxy in
Color.clear
.onChange(of: geometryProxy.size) {newSize in
guard newSize.width > 0, pageWidth > 0 else {return}
pages = Int(ceil(newSize.width / pageWidth))
}
.onAppear {
guard geometryProxy.size.width > 0, pageWidth > 0 else {return}
pages = Int(ceil(geometryProxy.size.width / pageWidth))
}
})
Group {
ForEach(0..<pages, id:\.self) {p in
ZStack(alignment: .topLeading) {
contentWithGeometry
.offset(x: -Double(p) * pageWidth, y: 0)
.frame(width: pageWidth, alignment: .leading)
VStack {
Spacer()
HStack {
Spacer()
Text("\(p + 1) of \(pages)")
.padding([.leading, .trailing])
}
}
}
.id(id.description + p.description)
}
}
}
}
I have facing an above crash for many users device running on iOS 17.6.1 mostly on iPad devices. I'm not sure why this happening only in 17.X. In Xcode Organizer unable to see this crash in any devices running on OS 18.x. Our app crashes got spiked due to this. I am unable to fix or reproduce the same. The crash log is not pointing to our app code to find the root cause and fix this issue. Have attached the crash log in this post also the crash log roles have mixed values Background &amp; Foreground. But most of the crash is in background.
Is this any crash related to system and that only solved by OS update? I have updated the app using Xcode 16 and 16.1 still facing this crash unable to symbolicate the crash report as well.
Any ideas/solution how to solve this or how to proceed further. Have attached the entire crash log below.
RoleBackgroundCrash.crash
RoleForeGroundCrash.crash
This has been broken for over 5 years now. I see 2 different behaviors in 2 different SwiftUI apps. This makes SwiftUI not ready for prime time apps, but I just have tools right now.
The VStack { List } doesn't scroll to the item in a long list. The selection moves to the next item in the list, but can't see it. This is just basic UI functionality of a list. UIListView doesn't have this issue.
The NavigationView { List { NavigationLink }} wraps around back to the top of the list when pressing down arrow past the last visible item, but there are plenty more list items to visit.
Our company has developed a product available, which measures body composition. During the measurement process, lasting 40 seconds, we require the device screen to remain illuminated. We are actively using the "Always On" feature and have set the timer on the watch to 70 minutes to prevent the screen from dimming.
However, we are encountering issues where the screen may still turn off during the measurement. Could you please provide guidance on how to keep the screen active with backlighting across all Apple Watch models during measurements?
The new .navigationTransition feature introduced in SwiftUI for iOS 18+ offers an impressive animated screen transition. However, during the transition, the parent view shrinks, leaving a white margin (or black in dark mode) around the edges.
If the background color of the parent view matches this margin color, it appears seamless. However, as shown in the attached example, when using a custom color or gradient background, the margin becomes visually disruptive.
Is there a way to address this?
import SwiftUI
struct ContentView: View {
@Namespace var namespace
var body: some View {
NavigationStack {
Form {
NavigationLink {
ZStack {
Color.yellow.ignoresSafeArea()
Text("Detail View")
}
.navigationTitle("Transition")
.navigationTransition(.zoom(sourceID: "hellow", in: namespace))
} label: {
Text("Open")
.font(.largeTitle)
.matchedTransitionSource(id: "hellow", in: namespace)
}
}
.scrollContentBackground(.hidden)
.background(Color.mint.ignoresSafeArea())
}
}
}
#Preview {
ContentView()
}
Applying .ignoreSafeArea() to the background view doesn’t seem to resolve the issue, which suggests this margin might not be related to the safe area. Any insights or solutions would be greatly appreciated.
Hello!
Is it possible to disable animation which appeared in iOS 18 when switching tab items?
I only found a solution for UIKit: https://forums.developer.apple.com/forums/thread/763122.
In creating a sequenced gesture combining a LongPressGesture and a DragGesture, I found that the combined gesture exhibits two problems:
The @GestureState does not properly update as the gesture progresses through its phases. Specifically, the updating(_:body:) closure (documented here) is only ever executed during the drag interaction. Long presses and drag-releases do not call the updating(_:body:) closure.
Upon completing the long press gesture and activating the drag gesture, the drag gesture remains empty until the finger or cursor has moved. The expected behavior is for the drag gesture to begin even when its translation is of size .zero.
This second problem – the nonexistence of a drag gesture once the long press has completed – prevents access to the location of the long-press-then-drag. Access to this location is critical for displaying to the user that the drag interaction has commenced.
The below code is based on Apple's example presented here. I've highlighted the failure points in the code with // *.
My questions are as follows:
What is required to properly update the gesture state?
Is it possible to have a viable drag gesture immediately upon fulfilling the long press gesture, even with a translation of .zero?
Alternatively to the above question, is there a way to gain access to the location of the long press gesture?
import SwiftUI
import Charts
enum DragState {
case inactive
case pressing
case dragging(translation: CGSize)
var isDragging: Bool {
switch self {
case .inactive, .pressing:
return false
case .dragging:
return true
}
}
}
struct ChartGestureOverlay<Value: Comparable & Hashable>: View {
@Binding var highlightedValue: Value?
let chartProxy: ChartProxy
let valueFromChartProxy: (CGFloat, ChartProxy) -> Value?
let onDragChange: (DragState) -> Void
@GestureState private var dragState = DragState.inactive
var body: some View {
Rectangle()
.fill(Color.clear)
.contentShape(Rectangle())
.onTapGesture { location in
if let newValue = valueFromChartProxy(location.x, chartProxy) {
highlightedValue = newValue
}
}
.gesture(longPressAndDrag)
}
private var longPressAndDrag: some Gesture {
let longPress = LongPressGesture(minimumDuration: 0.2)
let drag = DragGesture(minimumDistance: .zero)
.onChanged { value in
if let newValue = valueFromChartProxy(value.location.x, chartProxy) {
highlightedValue = newValue
}
}
return longPress.sequenced(before: drag)
.updating($dragState) { value, gestureState, _ in
switch value {
case .first(true):
// * This is never called
gestureState = .pressing
case .second(true, let drag):
// * Drag is often nil
// * When drag is nil, we lack access to the location
gestureState = .dragging(translation: drag?.translation ?? .zero)
default:
// * This is never called
gestureState = .inactive
}
onDragChange(gestureState)
}
}
}
struct DataPoint: Identifiable {
let id = UUID()
let category: String
let value: Double
}
struct ContentView: View {
let dataPoints = [
DataPoint(category: "A", value: 5),
DataPoint(category: "B", value: 3),
DataPoint(category: "C", value: 8),
DataPoint(category: "D", value: 2),
DataPoint(category: "E", value: 7)
]
@State private var highlightedCategory: String? = nil
@State private var dragState = DragState.inactive
var body: some View {
VStack {
Text("Bar Chart with Gesture Interaction")
.font(.headline)
.padding()
Chart {
ForEach(dataPoints) { dataPoint in
BarMark(
x: .value("Category", dataPoint.category),
y: .value("Value", dataPoint.value)
)
.foregroundStyle(highlightedCategory == dataPoint.category ? Color.red : Color.gray)
.annotation(position: .top) {
if highlightedCategory == dataPoint.category {
Text("\(dataPoint.value, specifier: "%.1f")")
.font(.caption)
.foregroundColor(.primary)
}
}
}
}
.frame(height: 300)
.chartOverlay { chartProxy in
ChartGestureOverlay<String>(
highlightedValue: $highlightedCategory,
chartProxy: chartProxy,
valueFromChartProxy: { xPosition, chartProxy in
if let category: String = chartProxy.value(atX: xPosition) {
return category
}
return nil
},
onDragChange: { newDragState in
dragState = newDragState
}
)
}
.onChange(of: highlightedCategory, { oldCategory, newCategory in
})
}
.padding()
}
}
#Preview {
ContentView()
}
Thank you!
Hi everyone! I’m fairly new to Swift and currently working on a small iOS app in SwiftUI. The app is able to load a CSV file embedded in the Xcode project (using Bundle.main.path(forResource:)), and everything works well with that.
Now, I want to take it a step further by allowing the app to load an external CSV file located in the iPhone’s directories (like “Documents” or “Downloads”). However, I’m struggling to make it work. I tried using a DocumentPicker to select the CSV file, and I believe I’m passing the file URL correctly, but the app keeps reading only the embedded file instead of the one selected by the user.
Could anyone offer guidance on how to properly set up loading an external CSV file? I’m still learning, so any suggestions or examples would be really appreciated!
Thanks a lot in advance for the help!
Here’s the code that isn’t working as expected:
import Foundation
struct Product: Identifiable {
let id = UUID()
var codice: String
var descrizione: String
var prezzo: Double
var installazione: Double
var trasporto: Double
}
class ProductViewModel: ObservableObject {
@Published var products: [Product] = []
@Published var filteredProducts: [Product] = []
func loadCSV(from url: URL) {
products = []
do {
let data = try String(contentsOf: url)
let lines = data.components(separatedBy: "\n")
// Legge e processa ogni riga del CSV (saltando la prima riga se è l'intestazione)
for line in lines.dropFirst() {
let values = line.components(separatedBy: ";")
// Assicurati che ci siano abbastanza colonne e gestisci i valori mancanti
if values.count >= 5 {
let codice = values[0].trimmingCharacters(in: .whitespaces)
let descrizione = values[1].trimmingCharacters(in: .whitespaces)
let prezzo = parseEuropeanDouble(values[2]) ?? 0.0
let installazione = parseEuropeanDouble(values[3].isEmpty ? "0,00" : values[3]) ?? 0.0
let trasporto = parseEuropeanDouble(values[4].isEmpty ? "0,00" : values[4]) ?? 0.0
let product = Product(
codice: codice,
descrizione: descrizione,
prezzo: prezzo,
installazione: installazione,
trasporto: trasporto
)
products.append(product)
}
}
filteredProducts = products
} catch {
print("Errore nel caricamento del CSV: \(error)")
}
}
private func parseEuropeanDouble(_ value: String) -> Double? {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "it_IT")
formatter.numberStyle = .decimal
return formatter.number(from: value)?.doubleValue
}
}
struct ContentView: View {
@StateObject var viewModel = ProductViewModel()
@State private var showFilePicker = false
var body: some View {
VStack {
Button("Carica file CSV") {
showFilePicker = true
}
.fileImporter(isPresented: $showFilePicker, allowedContentTypes: [.commaSeparatedText]) { result in
switch result {
case .success(let url):
viewModel.loadCSV(from: url)
case .failure(let error):
print("Errore nel caricamento del file: \(error.localizedDescription)")
}
}
List(viewModel.filteredProducts) { product in
VStack(alignment: .leading) {
Text("Codice: \(product.codice)")
Text("Descrizione: \(product.descrizione)")
Text("Prezzo Lordo: €\(String(format: "%.2f", product.prezzo))")
Text("Installazione: €\(String(format: "%.2f", product.installazione))")
Text("Trasporto: €\(String(format: "%.2f", product.trasporto))")
}
}
}
.padding()
}
}
Hello, since the last version of iOS and WatchOS I have a problem with this code.
This is the minimal version of the code, it have two pickers inside a view of a WatchOS App.
The problem its with the focus, I can't change the focus from the first picker to the second one.
As I said before, it was working perfectly in WatchOS 10.0 but in 11 the problems started.
struct ParentView: View {
@FocusState private var focusedField: String?
var body: some View {
VStack {
ChildView1(focusedField: $focusedField)
ChildView2(focusedField: $focusedField)
}
}
}
struct ChildView1: View {
@FocusState.Binding var focusedField: String?
@State private var selectedValue: Int = 0
var body: some View {
Picker("First Picker", selection: $selectedValue) {
ForEach(0..<5) { index in
Text("Option \(index)").tag("child\(index)")
}
}.pickerStyle(WheelPickerStyle()).focused($focusedField, equals: "first")
}
}
struct ChildView2: View {
@FocusState.Binding var focusedField: String?
@State private var selectedValue: Int = 0
var body: some View {
Picker("Second Picker", selection: $selectedValue) {
ForEach(0..<5) { index in
Text("Option \(index)").tag("childTwo\(index)")
}
}.pickerStyle(WheelPickerStyle()).focused($focusedField, equals: "second")
}
}
When you do vertical scrolling on the second picker, the focus should be on it, but it dosnt anything.
I try even do manually, setting the focusState to the second one, but it sets itself to nil.
I hope that you can help me, thanks!