每次我从 SwiftUI 的详细视图 return 时,如何阻止视图刷新?
How do I stop a view from refreshing each time I return from a detail view in SwiftUI?
我有一个显示 post 列表的视图。我已经实现了 infinite scrolling,它运行正常。然而,有一个小问题让我 运行 陷入困境,试图解决这个问题让我原地踏步。
主视图
struct PostsHomeView: View {
@EnvironmentObject var viewModel: ViewModel
@State var dataInitiallyFetched = false
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 0) {
if self.viewModel.posts.count > 0 {
PostsListView(posts: self.viewModel.posts,
isLoading: self.viewModel.canFetchMorePosts,
onScrolledAtBottom: self.viewModel.fetchMorePosts
)
} else {
VStack {
Text("You have no posts!")
}
}
}
.onAppear() {
if !self.dataInitiallyFetched {
self.viewModel.fetchMostRecentPosts()
self.dataInitiallyFetched = true
}
}
.navigationBarTitle("Posts", displayMode: .inline)
}
}
}
列表视图
struct PostsListView: View {
@EnvironmentObject var viewModel: ViewModel
let posts: [Post]
let isLoading: Bool
let onScrolledAtBottom: () -> Void
var body: some View {
List {
postsList
if isLoading {
loadingIndicator
}
}
}
private var postsList: some View {
ForEach(posts, id: \.self) { post in
PostsCellView(post: post)
.onAppear {
if self.posts.last == post {
self.onScrolledAtBottom()
}
}
}
.id(UUID())
}
}
问题
点击列表中的 post 之一后,我将进入详细视图。当我点击导航栏的后退按钮返回 posts 列表时,整个视图会重新加载,我的 post 获取方法会再次触发。
为了停止触发获取最近 posts 的获取方法,我添加了一个标志,我在初始加载后将其设置为 true。当我在详细信息视图和 post 的主屏幕之间来回切换时,这会阻止获取初始集 post 的获取方法。
我尝试了各种方法来阻止 fetchMorePosts
函数启动,但我一直在兜圈子。我在我的视图模型中的 fetchMorePosts
函数的顶部添加了一个 guard 语句。它检查字符串是否等于“homeview”,如果不等于,则未完成获取。每当访问详细视图时,我都会将此字符串设置为“detailview”,然后在 guard 语句中将其重置回“homeview”。
guard self.lastView == "homeview" else {
self.lastView = "homeview"
return
}
这在一定程度上有效,但我不断发现它无法按预期工作的情况。必须有一种直接的方式告诉 SwiftUI 不要重新加载视图。问题是该方法位于 onAppear
闭包中,这对于无限滚动的工作至关重要。我还没有使用 iOS 14,所以我不能使用 @StateObject。
有没有办法告诉 SwiftUI 每次我从细节视图 return 时不要触发 onAppear
?
提前致谢
罪魁祸首是 .id(UUID())
。我将它从我的列表中删除,然后一切又恢复正常了。
谢谢阿斯佩里。非常感谢您的帮助。
我有一个显示 post 列表的视图。我已经实现了 infinite scrolling,它运行正常。然而,有一个小问题让我 运行 陷入困境,试图解决这个问题让我原地踏步。
主视图
struct PostsHomeView: View {
@EnvironmentObject var viewModel: ViewModel
@State var dataInitiallyFetched = false
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 0) {
if self.viewModel.posts.count > 0 {
PostsListView(posts: self.viewModel.posts,
isLoading: self.viewModel.canFetchMorePosts,
onScrolledAtBottom: self.viewModel.fetchMorePosts
)
} else {
VStack {
Text("You have no posts!")
}
}
}
.onAppear() {
if !self.dataInitiallyFetched {
self.viewModel.fetchMostRecentPosts()
self.dataInitiallyFetched = true
}
}
.navigationBarTitle("Posts", displayMode: .inline)
}
}
}
列表视图
struct PostsListView: View {
@EnvironmentObject var viewModel: ViewModel
let posts: [Post]
let isLoading: Bool
let onScrolledAtBottom: () -> Void
var body: some View {
List {
postsList
if isLoading {
loadingIndicator
}
}
}
private var postsList: some View {
ForEach(posts, id: \.self) { post in
PostsCellView(post: post)
.onAppear {
if self.posts.last == post {
self.onScrolledAtBottom()
}
}
}
.id(UUID())
}
}
问题
点击列表中的 post 之一后,我将进入详细视图。当我点击导航栏的后退按钮返回 posts 列表时,整个视图会重新加载,我的 post 获取方法会再次触发。
为了停止触发获取最近 posts 的获取方法,我添加了一个标志,我在初始加载后将其设置为 true。当我在详细信息视图和 post 的主屏幕之间来回切换时,这会阻止获取初始集 post 的获取方法。
我尝试了各种方法来阻止 fetchMorePosts
函数启动,但我一直在兜圈子。我在我的视图模型中的 fetchMorePosts
函数的顶部添加了一个 guard 语句。它检查字符串是否等于“homeview”,如果不等于,则未完成获取。每当访问详细视图时,我都会将此字符串设置为“detailview”,然后在 guard 语句中将其重置回“homeview”。
guard self.lastView == "homeview" else {
self.lastView = "homeview"
return
}
这在一定程度上有效,但我不断发现它无法按预期工作的情况。必须有一种直接的方式告诉 SwiftUI 不要重新加载视图。问题是该方法位于 onAppear
闭包中,这对于无限滚动的工作至关重要。我还没有使用 iOS 14,所以我不能使用 @StateObject。
有没有办法告诉 SwiftUI 每次我从细节视图 return 时不要触发 onAppear
?
提前致谢
罪魁祸首是 .id(UUID())
。我将它从我的列表中删除,然后一切又恢复正常了。
谢谢阿斯佩里。非常感谢您的帮助。