SwiftUI 视图 - viewDidLoad()?
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)
}
}
}
}
提前致谢。
希望对您有所帮助。我发现 a blogpost 讨论了如何为导航视图执行 onAppear。
想法是将您的服务放入 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 = []
}
}
}
}
}
致谢:Majid Jabrayilov
完全更新 Xcode 11.2,Swift 5.0
我认为 viewDidLoad()
正好等于在 body 闭包中实现。
SwiftUI 以 onAppear()
和 onDisappear()
的形式为我们提供了 UIKit 的 viewDidAppear()
和 viewDidDisappear()
的等价物。您可以将任何代码附加到您想要的这两个事件,SwiftUI 将在它们发生时执行它们。
例如,这将创建两个使用 onAppear()
和 onDisappear()
打印消息的视图,并使用导航 link 在两者之间移动:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Hello World")
}
}
}.onAppear {
print("ContentView appeared!")
}.onDisappear {
print("ContentView disappeared!")
}
}
}
我们可以使用视图修饰符来实现。
- 创建
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?()
}
}
}
}
- 创建
View
扩展:
extension View {
func onLoad(perform action: (() -> Void)? = nil) -> some View {
modifier(ViewDidLoadModifier(perform: action))
}
}
- 这样使用:
struct SomeView: View {
var body: some View {
VStack {
Text("HELLO!")
}.onLoad {
print("onLoad")
}
}
}
我正在使用 init()
。我认为 onApear()
不能替代 viewDidLoad()
。因为当您的视图出现时会调用 onApear。由于您的视图可以出现多次,因此它与调用一次的 viewDidLoad
冲突。
想象一下 TabView
。通过轻扫页面 onApear() 被多次调用。然而 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)
}
}
}
}
提前致谢。
希望对您有所帮助。我发现 a blogpost 讨论了如何为导航视图执行 onAppear。
想法是将您的服务放入 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 = []
}
}
}
}
}
致谢:Majid Jabrayilov
完全更新 Xcode 11.2,Swift 5.0
我认为 viewDidLoad()
正好等于在 body 闭包中实现。
SwiftUI 以 onAppear()
和 onDisappear()
的形式为我们提供了 UIKit 的 viewDidAppear()
和 viewDidDisappear()
的等价物。您可以将任何代码附加到您想要的这两个事件,SwiftUI 将在它们发生时执行它们。
例如,这将创建两个使用 onAppear()
和 onDisappear()
打印消息的视图,并使用导航 link 在两者之间移动:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Hello World")
}
}
}.onAppear {
print("ContentView appeared!")
}.onDisappear {
print("ContentView disappeared!")
}
}
}
我们可以使用视图修饰符来实现。
- 创建
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?()
}
}
}
}
- 创建
View
扩展:
extension View {
func onLoad(perform action: (() -> Void)? = nil) -> some View {
modifier(ViewDidLoadModifier(perform: action))
}
}
- 这样使用:
struct SomeView: View {
var body: some View {
VStack {
Text("HELLO!")
}.onLoad {
print("onLoad")
}
}
}
我正在使用 init()
。我认为 onApear()
不能替代 viewDidLoad()
。因为当您的视图出现时会调用 onApear。由于您的视图可以出现多次,因此它与调用一次的 viewDidLoad
冲突。
想象一下 TabView
。通过轻扫页面 onApear() 被多次调用。然而 viewDidLoad() 只被调用一次。