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!")
}
}
}
я использую
init()
вместо. я думаю
onApear()
не альтернатива
viewDidLoad()
. Потому что onApear вызывается, когда появляется ваше представление. Поскольку ваше представление может появляться несколько раз, оно конфликтует с
viewDidLoad
который называется один раз.
Представьте себе, что у вас есть
TabView
. При пролистывании страниц onApear () вызывается несколько раз. Однако viewDidLoad() вызывается только один раз.