最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - The value got from onChange is always the old one - Stack Overflow

programmeradmin6浏览0评论

I created an image view like the ChildView below.

But when I update the image, it has some strange behavior. The value is still the old one after the closure of the old onChange closure being called in this case. The url always has an one round delay. But when I use the onChange in the content view, the url is up to date again in the block.

How does this onChange work?

import SwiftUI

struct ContentView: View {
    @State var url:URL? = Bundle.main.url(forResource: "image1", withExtension: "jpeg")
    let alt = Bundle.main.url(forResource: "image1", withExtension: "jpeg")
    let alt1 = Bundle.main.url(forResource: "image2", withExtension: "PNG")
    var body: some View {
        VStack {
            Button("Change Image") {
                url == alt1 ? (url = alt) : (url = alt1)
            }
            Text("url : \(url?.lastPathComponent ?? "nil")")
            ChildView(url: url)
        }
        .padding()
    }
}
struct ChildView: View {
    var url:URL?
    @State var image: UIImage? = nil
    var body: some View {
        VStack{
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
            }
        }
/*
        .task(id:url) {
            loadImage() //works fine.
        }
*/
        .onAppear{
            loadImage()
        }
        .onChange(of: url) { value in
            print("old OnChange \(url?.lastPathComponent) \(value?.lastPathComponent)")
            // "url: old, value: new" <--------- very strange here.
            loadImage()
        }
        .onChange(of:url) { old, new in //<--------- works fine, but only available in iOS 17.
        }
    }
    func loadImage() {
        if let url = url {
            image = UIImage(contentsOfFile: url.path)
        }
    }
}

I created an image view like the ChildView below.

But when I update the image, it has some strange behavior. The value is still the old one after the closure of the old onChange closure being called in this case. The url always has an one round delay. But when I use the onChange in the content view, the url is up to date again in the block.

How does this onChange work?

import SwiftUI

struct ContentView: View {
    @State var url:URL? = Bundle.main.url(forResource: "image1", withExtension: "jpeg")
    let alt = Bundle.main.url(forResource: "image1", withExtension: "jpeg")
    let alt1 = Bundle.main.url(forResource: "image2", withExtension: "PNG")
    var body: some View {
        VStack {
            Button("Change Image") {
                url == alt1 ? (url = alt) : (url = alt1)
            }
            Text("url : \(url?.lastPathComponent ?? "nil")")
            ChildView(url: url)
        }
        .padding()
    }
}
struct ChildView: View {
    var url:URL?
    @State var image: UIImage? = nil
    var body: some View {
        VStack{
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
            }
        }
/*
        .task(id:url) {
            loadImage() //works fine.
        }
*/
        .onAppear{
            loadImage()
        }
        .onChange(of: url) { value in
            print("old OnChange \(url?.lastPathComponent) \(value?.lastPathComponent)")
            // "url: old, value: new" <--------- very strange here.
            loadImage()
        }
        .onChange(of:url) { old, new in //<--------- works fine, but only available in iOS 17.
        }
    }
    func loadImage() {
        if let url = url {
            image = UIImage(contentsOfFile: url.path)
        }
    }
}
Share edited Nov 28, 2024 at 11:05 Perry Wang asked Nov 28, 2024 at 10:48 Perry WangPerry Wang 1992 silver badges10 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 1

You are using the old (now deprecated) version of .onChange, because you need to support iOS versions before iOS 17.

The documentation to the old version includes a note:

This modifier’s closure captures values that represent the state before the change. The new modifiers capture values that correspond to the new state. The new behavior makes it easier to perform updates that rely on values other than the one that caused the modifier’s closure to run.

So when your closure is run, the value of url is still the old one. You are calling loadImage from inside the closure, which therefore loads the old image.

To fix, try adding a parameter to loadImage:

func loadImage(url: URL?) {
    // ... implementation as before
}

Now supply the URL to load when calling the function. In the case of the call in the .onChange closure, this means passing value

.onAppear{
    loadImage(url: url) // 
发布评论

评论列表(0)

  1. 暂无评论