Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
130 views
in Technique[技术] by (71.8m points)

SwiftUI: State is set but is not passed to subsequent view

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...