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.
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
Hi,
When passing a SwftData Module to a view that receives it in a @Binadable property variable I get an error that it doesn't conform to Observation, I don't know how to solve this issue, even adding @Observable type to the generic in the structure definition won't solve it, any suggestions ?
Kind Regards
I have drag-and-drop functionality in the macOS app built with SwiftUI.
Since macOS 15 there is an issue with it, because as I found out, the completion block of loadObject method on NSItemProvider is not called until dropExited delegate method is called (see simplified code example below).
It causes very strange behavior in my app, for one of the most important features, and I am looking for a way to fix it as soon as possible.
Is anyone seeing the same issue?
I saw there was a bug with drag and drop on iOS 18, which seems to be fixed in 18.1. Anyone from Apple can say anything about this change in behaviour?
@Observable // Because it is injected via environment.
final class DragAndDropDelegate<T: Codable>: DropDelegate {
func dropEntered(info: DropInfo) {
// Is called as expected.
guard
let itemProvider = info.itemProviders(for: [UTType.data]).first
else { return }
itemProvider.loadObject(ofClass: DraggableObject<T>.self) { [weak self] (object, error) in
// This is only called after dropExited delegate method is called !!!
// Before macOS 15.0 it is called quickly after the loadObject method invokation.
asyncOnMainThread {
guard
let self,
let draggableObject = object as? DraggableObject<T>
else { return }
self.onEnter?(draggableObject.object, info.location)
}
}
}
func dropExited(info: DropInfo) {
// Is called as expected.
}
}
I've run into an issue with my app that I've been able to narrow down to a small reproducer.
Any time there is a task associated with the DetailView and you "pop to top", onAppear is called again and the task is re-run. Why is that? Is this a SwiftUI bug? It doesn't happen on iOS 17, only 18.
import SwiftUI
@Observable
class Store {
var shown: Bool = true
}
@main
struct MyApp: App {
@State private var store = Store()
var body: some Scene {
WindowGroup {
if store.shown {
ContentView()
} else {
EmptyView()
}
}
.environment(store)
}
}
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: DetailView()) {
Text("Go to Detail View")
}
}
}
}
struct DetailView: View {
@Environment(Store.self) private var store
init() {
print("DetailView initialized")
}
var body: some View {
Button("Pop to top") {
store.shown = false
}
.task {
print("DetailView task executed")
}
.onAppear {
print("DetailView appeared")
}
.onDisappear {
print("DetailView disappeared")
}
}
}
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 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?
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
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?
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?
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
})
}
})
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.
If I drag something into my SwiftUI Mac app the .dropDestination gets an array of URLs that I can do with what I want.
If I use .fileImporter to get an identical array of URLs I should wrap start/stop securityScopedResource() calls around each URL before I do anything with it.
Can anyone explain the logic behind that? Is there some reason I'm not seeing? It is especially annoying in that the requirement for security scoping also doesn't exist if I use an NSOpenPanel instead of .fileImporter.
I have an immersive space with a RealityKit view which is running an ARKitSession to access main camera frames.
This frame is processed with custom computer vision algorithms (and deep learning models).
There is a 3D Entity in the RealityKit view which I'm trying to place in the world, but I want to debug my (2D) algorithms in an "attached" view (display images in windows).
How to I send/share data or variables between the views (and and spaces)?
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!
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
When the text is set to non-accentable, it always appears white. The background is accentable, but when the user changes the tint color to white, the text becomes invisible.
How can I make it work like in the Shortcuts widget, where the text color automatically changes based on the tint of the background, ensuring the text is always visible?
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()
}
}
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.
I've got an array of items that I use as a source of truth, and I copy that array into another array (filteredItems) and use that to power a NavigationSplitView (on MacOS).
I can sort the items in filteredItems by create date, title, last modified date, forward and reverse, no problem. And I have a text filter field that I can use to filter out items in the filteredItems field and that works great.
So far so good.
But if I reduce the filter text or remove it altogether—thus increasing the number of items in filteredItems or returning it to its original state—weird things happen.
Clicking on items in the navigation portion of the view doesn't bring up the detail view as it did before the reduction and re-adding of items.
After clicking on several of the non-responsive nav items, it freezes up.
I know there are different ways to do this, but my view is set up like this:
NigationSplitView {
// filter tool, etc.
List {
ForEach(ascending ? filteredItems : filteredItems.reversed()) { item in
NavigationLink {
ItemView(item: item)
} label: {
// yadda yadda
}
}
}
(ascending is just a boolean state variable)
I know there's not much detail here, but I should be able to change filteredItems as much as I want, right? Or is this construct wrong?