I'm still on the onramp of the steep SwiftUI learning curve, so excuse me if this is a dumb question. I HAVE tried everything I could think of to work around this issue I'm having.
The problem I'm encountering is that after setting a @State var's value in my view, using that var in a call to create a new view that's displayed as a sheet fails to pass the updated var's value.
From what I have read in the Apple documentation and other online sources, it's my understanding that I should be able to use the base value of a @State var to pass to another View to initialize it. But as you can see from the comments in the code below, I am updating the var's value as expected, but it is not retaining that value when used to initialize the second view.
I'm experiencing this problem in Xcode 12.3 running in Big Sur.
I boiled it down to as simple a demo as I could with the following two view structs:
struct SharingPresenterView: View {
@State private var showImageSheet = false
@State private var imageURL = URL(fileURLWithPath: "") {
didSet {
print("Set imageURL property to: (imageURL.path)") // prints file URL as expected
}
}
// "map" is name of an image asset added to project
private let mapImage = UIImage(named: "map")
var body: some View {
Spacer()
if let img = mapImage {
// create an Image and show it in this view
Image(uiImage: img)
}
Spacer()
Button("Show Map") {
// save the map image to disk and get its URL for passing on
if let img = mapImage, let imgData = img.pngData() {
if let url = saveItem(imgData, fileName: "Map.png") {
imageURL = url
showImageSheet = true
}
}
}
.sheet(isPresented: $showImageSheet, onDismiss: {
print("Dismiss after showing sheet for: (imageURL)") // prints file URL as expected
}) {
ImageView(imageURL: imageURL)
}
}
// a helper method for saving a shareable item to disk
private func saveItem(_ data: Data, fileName: String) -> URL? {
let fileMgr = FileManager.default
let fileURL = fileMgr.temporaryDirectory.appendingPathComponent(fileName)
if fileMgr.fileExists(atPath: fileURL.path) {
do {
try fileMgr.removeItem(at: fileURL)
} catch {
print("Error removing existing file: (error)")
return nil
}
}
do {
try data.write(to: fileURL, options: .atomic)
print("Saved file: (fileURL.path)") // prints file URL as expected
return fileURL
} catch {
print("Error writing file: (error)")
return nil
}
}
}
struct SharingPresenterView_Previews: PreviewProvider {
static var previews: some View {
SharingPresenterView()
}
}
The problem I'm experiencing is evident here:
struct ImageView: View {
var imageURL: URL
// this init method was added explicitly to allow printing out the incoming imageURL property's value
init(imageURL: URL) {
print("Initializing ImageView imageURL: (imageURL.path)") // prints "/" for URL path
self.imageURL = imageURL
}
var body: some View {
VStack {
imageToShow(imageURL: imageURL)
}
}
private func imageToShow(imageURL: URL) -> Image {
if let uiImage = UIImage(contentsOfFile: imageURL.path) {
return Image(uiImage: uiImage)
} else {
return Image(systemName: "xmark.octagon")
}
}
}
struct ImageView_Previews: PreviewProvider {
static var previews: some View {
// added as a sanity check after getting file path from Console
// preview displays image as expected
let urlString = "/Users/cosmo/Library/Developer/CoreSimulator/Devices/3476F355-5345-4F11-9436-267B001C3663/data/Containers/Data/Application/A519ECB0-BB83-490D-8993-7E67D0FEB5A2/tmp/Map.png"
let url = URL(fileURLWithPath: urlString)
ImageView(imageURL: url)
}
}
Am I doing something wrong? Is my expectation wrong? (I have reviewed some tutorial code that uses this approach, so I'm expecting that's not the case.) How do I accomplish my goal of sending a URL that's created at runtime to another view?
question from:
https://stackoverflow.com/questions/65839213/swiftui-state-is-set-but-is-not-passed-to-subsequent-view 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…