为什么更改@State变量时,此SwiftUI View为什么不更新?

时间:2020-04-15 01:59:27

标签: swiftui

我有一个SwiftUI视图,该视图带有一个Hacker News API提交ID,然后在fetchStory()中获取该项目的详细信息。

fetchStory()完成其HTTP调用后,将更新视图上的@State private var url,但是视图从不重新渲染以显示新值-始终显示初始空值。

为什么?

import Foundation
import SwiftUI

struct StoryItem: Decodable {
    let title: String
    let url: String?
}

struct StoryView: View {
    public var _storyId: Int

    @State private var url: String = "";

    init(storyId: Int) {
        self._storyId = storyId
        self.fetchStory()
    }

    var body: some View {
        VStack {
            Text("(Load a webview here for the URL of Story #\(self._storyId))")
            Text("URL is: \(self.url)") // this never changes!
        }
    }

    private func fetchStory() {
        let url = URL(string: "https://hacker-news.firebaseio.com/v0/item/\(self._storyId).json")!

        let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
            guard let data = data else {
                print(String(error.debugDescription))
                return
            }

            do {
                let item: StoryItem = try JSONDecoder().decode(StoryItem.self, from: data)

                if let storyUrl = item.url {
                    self.url = storyUrl
                } else {
                    print("No url")
                }
            } catch {
                print(error)
            }
        }

        task.resume()
    }
}

struct StoryView_Previews: PreviewProvider {
    static var previews: some View {
        StoryView(storyId: 22862053)
    }
}

2 个答案:

答案 0 :(得分:1)

类似这样的东西:

import SwiftUI

struct StoryItem: Decodable {
let title: String
let url: String?
}

class ObservedStoryId: ObservableObject {
@Published var storyId: String = ""
init(storyId: String) {
    self.storyId = storyId
}
}

struct StoryView: View {

@ObservedObject var storyId: ObservedStoryId
@State var url: String = ""

var body: some View {
    VStack {
        Text("(Load a webview here for the URL of Story #\(self.storyId.storyId))")
        Text("URL is: \(self.url)")
        Text(self.storyId.storyId)
    }.onReceive(storyId.$storyId) { _ in self.fetchStory() }
}

private func fetchStory() {
    let url = URL(string: "https://hacker-news.firebaseio.com/v0/item/\(self.storyId.storyId).json")!
    let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
        guard let data = data else {
            print(String(error.debugDescription))
            return
        }
        do {
            let item: StoryItem = try JSONDecoder().decode(StoryItem.self, from: data)
            if let storyUrl = item.url {
                self.url = storyUrl
            } else {
                print("No url")
            }
        } catch {
            print(error)
        }
    }
    task.resume()
}
}

并这样称呼它:

struct ContentView: View {

    @ObservedObject var storyId = ObservedStoryId(storyId: "22862053")

    var body: some View {
     StoryView(storyId: storyId)
    }
}

答案 1 :(得分:0)

解决您的问题:

init(storyId: Int) {
    self._storyId = storyId
}

var body: some View {
    VStack {
        Text("(Load a webview here for the URL of Story #\(self._storyId))")
        Text("URL is: \(self.url)") // this now works
    }.onAppear(perform: fetchStory)
}

这为什么起作用,而不是您的代码, 我的猜测是:“ self.url”只能在特殊的SwiftUI View函数(例如onAppear())内进行更新/更改,而在其他地方则不会改变。