SwiftUI View-viewDidLoad() ? ?

试图在视图加载后加载图像,驱动视图的模型对象(请参阅下面的 MovieDetails)具有 urlString。因为 SwiftUI View元素没有生命周期方法(也没有视图控制器来驱动) ,那么处理这个问题的最佳方法是什么呢?

我的主要问题是,无论我尝试用哪种方式解决问题(绑定一个对象或使用状态变量) ,我的视图没有 urlString,直到它加载..。

// movie object
struct Movie: Decodable, Identifiable {


let id: String
let title: String
let year: String
let type: String
var posterUrl: String


private enum CodingKeys: String, CodingKey {
case id = "imdbID"
case title = "Title"
case year = "Year"
case type = "Type"
case posterUrl = "Poster"
}
}
// root content list view that navigates to the detail view
struct ContentView : View {


var movies: [Movie]


var body: some View {
NavigationView {
List(movies) { movie in
NavigationButton(destination: MovieDetail(movie: movie)) {
MovieRow(movie: movie)
}
}
.navigationBarTitle(Text("Star Wars Movies"))
}
}
}
// detail view that needs to make the asynchronous call
struct MovieDetail : View {


let movie: Movie
@State var imageObject = BoundImageObject()


var body: some View {
HStack(alignment: .top) {
VStack {
Image(uiImage: imageObject.image)
.scaledToFit()


Text(movie.title)
.font(.subheadline)
}
}
}
}

先谢谢你。

65244 次浏览

I hope this is helpful. I found a blogpost that talks about doing stuff onAppear for a navigation view.

Idea would be that you bake your service into a BindableObject and subscribe to those updates in your view.

struct SearchView : View {
@State private var query: String = "Swift"
@EnvironmentObject var repoStore: ReposStore


var body: some View {
NavigationView {
List {
TextField($query, placeholder: Text("type something..."), onCommit: fetch)
ForEach(repoStore.repos) { repo in
RepoRow(repo: repo)
}
}.navigationBarTitle(Text("Search"))
}.onAppear(perform: fetch)
}


private func fetch() {
repoStore.fetch(matching: query)
}
}
import SwiftUI
import Combine


class ReposStore: BindableObject {
var repos: [Repo] = [] {
didSet {
didChange.send(self)
}
}


var didChange = PassthroughSubject<ReposStore, Never>()


let service: GithubService
init(service: GithubService) {
self.service = service
}


func fetch(matching query: String) {
service.search(matching: query) { [weak self] result in
DispatchQueue.main.async {
switch result {
case .success(let repos): self?.repos = repos
case .failure: self?.repos = []
}
}
}
}
}

Credit to: Majid Jabrayilov

Fully updated for Xcode 11.2, Swift 5.0

I think the viewDidLoad() just equal to implement in the body closure.
SwiftUI gives us equivalents to UIKit’s viewDidAppear() and viewDidDisappear() in the form of onAppear() and onDisappear(). You can attach any code to these two events that you want, and SwiftUI will execute them when they occur.

As an example, this creates two views that use onAppear() and onDisappear() to print messages, with a navigation link to move between the two:

struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Hello World")
}
}
}.onAppear {
print("ContentView appeared!")
}.onDisappear {
print("ContentView disappeared!")
}
}
}

ref: https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear

We can achieve this using view modifier.

  1. Create ViewModifier:
struct ViewDidLoadModifier: ViewModifier {


@State private var didLoad = false
private let action: (() -> Void)?


init(perform action: (() -> Void)? = nil) {
self.action = action
}


func body(content: Content) -> some View {
content.onAppear {
if didLoad == false {
didLoad = true
action?()
}
}
}


}
  1. Create View extension:
extension View {


func onLoad(perform action: (() -> Void)? = nil) -> some View {
modifier(ViewDidLoadModifier(perform: action))
}


}
  1. Use like this:
struct SomeView: View {
var body: some View {
VStack {
Text("HELLO!")
}.onLoad {
print("onLoad")
}
}
}

I'm using init() instead. I think onApear() is not an alternative to viewDidLoad(). Because onApear is called when your view is being appeared. Since your view can be appear multiple times it conflicts with viewDidLoad which is called once.

Imagine having a TabView. By swiping through pages onApear() is being called multiple times. However viewDidLoad() is called just once.