SwiftUI View - viewDidLoad()?

При попытке загрузить изображение после загрузки вида объект модели, управляющий видом (см. Ниже MovieDetail), имеет строку 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)
            }
        }
    }
}

Заранее спасибо.

4 ответа

Решение

Я надеюсь, что это полезно. Я нашел пост в блоге, в котором рассказывается о том, как делать что-то в Appar для представления навигации.

Идея состоит в том, что вы запекаете свой сервис в BindableObject и подписываетесь на эти обновления в вашем представлении.

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 = []
                }
            }
        }
    }
}

Кредит: Маджид Джабраилов

Это решение работает ТОЛЬКО, если структура представления не изменена (вы не добавляете / не удаляете подпредставления). Однако вы все равно можете изменить состояние представлений (например, положение, анимировать их).

Создайте View расширение:

extension View {
    func onLoad(perform action: (() -> Void)? = nil) -> some View {
        var actionPerformed = false
        return self.onAppear {
            guard !actionPerformed else {
                return
            }
            actionPerformed = true
            action?()
        }
    }
}

Тогда используйте это так:

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

Я не слишком много тестировал свое решение, но, похоже, оно работает нормально. "onLoad" будет напечатан только один раз, даже если вы вернетесь из другого представления в стеке навигации.

Полностью обновлен для Xcode 11.2, Xcode 5.0

Я думаю, чтоviewDidLoad()ровно равно реализовать в закрытии корпуса.
SwiftUI дает нам эквиваленты UIKit'sviewDidAppear() а также viewDidDisappear() в виде onAppear() а также onDisappear(). Вы можете прикрепить к этим двум событиям любой код, который хотите, и SwiftUI выполнит их, когда они произойдут.

Например, это создает два представления, которые используют onAppear() а также onDisappear() для печати сообщений с навигационной ссылкой для перемещения между ними:

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

ссылка:https://www.hackingwithswift.com/quick-start/swiftui/how-to-respond-to-view-lifecycle-events-onappear-and-ondisappear

я использую init()вместо. я думаю onApear() не альтернатива viewDidLoad(). Потому что onApear вызывается, когда появляется ваше представление. Поскольку ваше представление может появляться несколько раз, оно конфликтует с viewDidLoad который называется один раз.

Представьте себе, что у вас есть TabView. При пролистывании страниц onApear () вызывается несколько раз. Однако viewDidLoad() вызывается только один раз.

Другие вопросы по тегам