【问题标题】:WebImage not updating the View - SwiftUI (Xcode 12)WebImage 未更新视图 - SwiftUI (Xcode 12)
【发布时间】:2021-07-11 21:04:56
【问题描述】:

我的问题是,当我将观察到的对象字符串属性更改为另一个值时,它会更新 JSON Image 值,打印出更新后的值(使用 Unsplash API),但 WebImage(来自 SDWebImageSwiftUI)不会改变。

Result 适用的结构体:

struct Results : Codable {
    var total : Int
    var results : [Result]
}

struct Result : Codable {
    var id : String
    var description : String?
    var urls : URLs
}

struct URLs : Codable {
    var small : String
}

这是包含应该更新的 webImage 的视图:

struct MoodboardView: View {
    @ObservedObject var searchObjectController = SearchObjectController.shared
    
    var body: some View {
        
        List {
            VStack {
                Text("Mood Board: \(searchObjectController.searchText)")
                    .fontWeight(.bold)
                    .padding(6)
                ForEach(searchObjectController.results, id: \.id, content: { result in
                    Text(result.description ?? "Empty")
                    WebImage(url: URL(string: result.urls.small) )
                        .resizable()
                        .frame(height:300)
                    })
                }.onAppear() {
                searchObjectController.search()
            }
            
        }
    }
}   

这是执行 API 请求的类:

class SearchObjectController : ObservableObject {
    static let shared = SearchObjectController()
    private init() {}
    
    var token = "gQR-YsX0OpwkYpbjhPVi3b4kSR-DtWrR5phwDm2kPMM"
    @Published var results = [Result]()
    @Published var searchText : String = "forest"
    
    func search () {
        let url = URL(string: "https://api.unsplash.com/search/photos?query=\(searchText)")
        var request = URLRequest(url: url!)
        request.httpMethod = "GET"
        request.setValue("Client-ID \(token)", forHTTPHeaderField: "Authorization")
        print("request: \(request)")
        
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard let data = data else {return}
            print(String(data: data, encoding: .utf8)!)

            do {
                let res = try JSONDecoder().decode(Results.self, from: data)
                DispatchQueue.main.async {
                    self.results.append(contentsOf: res.results)
                }
                //print(self.results)
            } catch {
                print("catch: \(error)")
            }
        }
        task.resume()
    }
}

如果您想查看,我如何更改按钮中 searchText 的值:

struct GenerateView: View {
    
    @ObservedObject var searchObjectController = SearchObjectController.shared
    
    @State private var celsius: Double = 0
    
    var body: some View {
        ZStack {
            Color.purple
                .ignoresSafeArea()
            VStack{
                
                Text("Generate a Random Idea")
                    .padding()
                    .foregroundColor(.white)
                    .font(.largeTitle)
                    .frame(maxWidth: .infinity, alignment: .center)
                    
                Image("placeholder")
                Slider(value: $celsius, in: -100...100)
                    .padding()
                Button("Generate") {
                    print("topic changed to\(searchObjectController.searchText)")
                    
                    searchObjectController.searchText.self = "tables"
                }
                Spacer()
            }
        }
    }
}

【问题讨论】:

    标签: ios json xcode swiftui sdwebimage


    【解决方案1】:

    事实证明您更新了搜索文本但不再搜索。看看这个就行了:

    Button("Generate") {
        print("topic changed to\(searchObjectController.searchText)")
    
        searchObjectController.searchText.self = "tables" // you changed the search text but didnt search again
        self.searchObjectController.search() // adding this does the trick
    }
    

    还有。我更新了您的代码以使用EnvironmentObject。如果您在整个应用程序中使用一个对象的一个​​实例。考虑将其设为EnvironmentObject,这样就不必一直传递它。

    添加它很容易。将其添加到您的 @main 场景中

    导入 SwiftUI

    @main
    struct StackoverflowApp: App {
        
        @ObservedObject var searchObjectController = SearchObjectController()
        
        var body: some Scene {
            WindowGroup {
                ContentView().environmentObject(self.searchObjectController)
            }
        }
    }
    

    而且使用起来更简单:

    struct MoodboardView: View {
        
        // Env Obj. so we reference only one object
        @EnvironmentObject var searchObjectController: SearchObjectController
    
        var body: some View {
            
            Text("")
        }
    }
    

    这是您的代码,其中包含更改并按预期工作: 我在所做的更改中添加了 cmets

    
    struct ContentView: View {
        
        var body: some View {
            MoodboardView()
        }
    }
    
    struct GenerateView: View {
    
        @EnvironmentObject var searchObjectController: SearchObjectController
    
        @State private var celsius: Double = 0
    
        var body: some View {
            ZStack {
                Color.purple
                    .ignoresSafeArea()
                VStack{
                    
                    Text("Generate a Random Idea")
                        .padding()
                        .foregroundColor(.white)
                        .font(.largeTitle)
                        .frame(maxWidth: .infinity, alignment: .center)
                        
                    Image("placeholder")
                    Slider(value: $celsius, in: -100...100)
                        .padding()
                    Button("Generate") {
                        print("topic changed to\(searchObjectController.searchText)")
                        
                        searchObjectController.searchText.self = "tables" // you changed the search text but didnt search again
                        self.searchObjectController.search() // adding this does the trick
                        
                    }
                    Spacer()
                }
            }
        }
    
    }
    
    class SearchObjectController : ObservableObject {
        
        //static let shared = SearchObjectController() // Delete this. We want one Object of this class in the entire app.
         
        //private init() {} // Delete this. Empty Init is not needed
    
        var token = "gQR-YsX0OpwkYpbjhPVi3b4kSR-DtWrR5phwDm2kPMM"
        @Published var results = [Result]()
        @Published var searchText : String = "forest"
    
        func search () {
            let url = URL(string: "https://api.unsplash.com/search/photos?query=\(searchText)")
            var request = URLRequest(url: url!)
            request.httpMethod = "GET"
            request.setValue("Client-ID \(token)", forHTTPHeaderField: "Authorization")
            print("request: \(request)")
            
            let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
                guard let data = data else {return}
                print(String(data: data, encoding: .utf8)!)
    
                do {
                    let res = try JSONDecoder().decode(Results.self, from: data)
                    DispatchQueue.main.async {
                        self.results.append(contentsOf: res.results)
                    }
                    //print(self.results)
                } catch {
                    print("catch: \(error)")
                }
            }
            task.resume()
        }
    }
    
    struct MoodboardView: View {
        
        // Env Obj. so we reference only one object
        @EnvironmentObject var searchObjectController: SearchObjectController
    
        var body: some View {
            
            List {
                VStack {
                    Text("Mood Board: \(searchObjectController.searchText)")
                        .fontWeight(.bold)
                        .padding(6)
                    ForEach(searchObjectController.results, id: \.id, content: { result in
                        Text(result.description ?? "Empty")
                        WebImage(url: URL(string: result.urls.small) )
                            .resizable()
                            .frame(height:300)
                        })
                    }.onAppear() {
                    searchObjectController.search()
                }
                
                // I added your update button here so I can use it.
                GenerateView()
            }
        }
    }
    
    struct Results : Codable {
        var total : Int
        var results : [Result]
    }
    
    struct Result : Codable {
        var id : String
        var description : String?
        var urls : URLs
    }
    
    struct URLs : Codable {
        var small : String
    }
    
    

    附言。请不要在搜索时出现它,您只需将结果附加到数组中,不要删除旧的。这就是为什么您在更新后仍然看到第一张图像的原因。新的只是附加在底部。向下滚动以查看它们。如果您不想这样做,只需在search 上清空带有结果的数组@

    【讨论】:

    • 感谢您回答西蒙!我进行了更改,但出现了一些类型错误“实例方法'onReceive(_:perform:)' 要求'Result' 符合'Publisher'”和“无法将'Result' 类型的值转换为预期的参数类型'Int '”,所以我用 JSON 结果更新了我的问题。希望这会有所帮助。
    猜你喜欢
    • 2020-12-21
    • 2021-10-14
    • 2020-03-06
    • 2020-05-02
    • 1970-01-01
    • 2020-07-12
    • 2021-02-17
    • 1970-01-01
    • 2020-10-22
    相关资源
    最近更新 更多