SwiftUI NavigationStack重复打开同一视图问题求助
我是iOS开发新手,在用SwiftUI开发时遇到了列表导航的问题,一直没能解决。
核心问题
- 首次点击PostListView中的列表项时,
PostDetailsView会针对同一帖子被实例化两次,破坏了业务逻辑;再次点击列表项时,PostDetailsView仅被实例化一次,功能正常。 - 如果使用带
destination和label的NavigationLink,功能可以正常运行,但我需要从PostDetailsView导航到嵌套的PostDetailsView(当博客中存在其他帖子链接时),这必须借助path和NavigationStack实现编程式导航才能完成。
原始实现代码
struct BlogNavigationGraph: View { @State private var path = [PostUiModel]() var body: some View { NavigationStack(path: $path) { PostListView(viewModel: /* pass view model here */) .navigationDestination(for: PostUiModel.self) { post in PostDetailsView(post: post, viewModel: /* pass view model here */) } } } } struct PostListView: View { @ObservedObject var viewModel: PostListViewModel var body: some View { Group { switch viewModel.state { case .loading: LoadingView() case .error: ErrorView() case .hasPosts(let data): PostList(data) } }.onAppear { viewModel.onViewDidAppear() } } } struct PostList: View { let data: [PostUiModel] var body: some View { List(data, id: \.id) { item in PostItem(post: item) } } } struct PostItem: View { let post: PostUiModel var body: some View { NavigationLink(value: post) { PostItemContent(post: post) } } } struct PostItemContent: View { var body: some View { // complex content here } }
简化复现代码
为了复现问题,我写了一个带硬编码数据的简化示例:
import SwiftUI struct UiModel: Identifiable, Hashable { let id: Int let title: String } struct TestView: View { @State private var path = [UiModel]() var body: some View { NavigationStack(path: $path) { TestList() .navigationDestination(for: UiModel.self) { item in TestItemDetails(viewModel: TestItemDetailsViewModel(), item: item) { target in path.append(target) } } } } } struct TestList: View { let data = [ UiModel(id: 1, title: "Title 1"), UiModel(id: 2, title: "Title 2"), UiModel(id: 3, title: "Title 3") ] var body: some View { List(data) { item in TestItem(item: item) } } } struct TestItem: View { let item: UiModel init(item: UiModel) { self.item = item } var body: some View { NavigationLink(value: item) { TestItemContent(item: item) } } } struct TestItemContent: View { let item: UiModel init(item: UiModel) { self.item = item } var body: some View { Text(item.title) } } struct TestItemDetails: View { @ObservedObject var viewModel: TestItemDetailsViewModel let item: UiModel let onRedirect: (UiModel) -> Void var body: some View { VStack { Text("Details for: {\(item.id), \(item.title)}") switch viewModel.state { case .normal: Text("Normal") case .error: Text("Error") } Button("Go to next") { onRedirect( UiModel( id: 5, title: "Redirected item title" ) ) } }.onAppear { viewModel.onViewDidAppear() } } } enum TestItemDetailsState { case normal case error } class TestItemDetailsViewModel: ObservableObject { @Published var state: TestItemDetailsState = .normal func onViewDidAppear() { state = .error } } struct TestView_Previews: PreviewProvider { static var previews: some View { TestView() } }
复现的现象
在这个示例中:
TestItem.init会针对每个项被调用两次,TestItemContent.init针对每个项被调用一次- 首次点击列表项时,
TestItemDetails.init会被调用两次;再次点击则仅调用一次,功能正常 - 当
TestItemDetails.onAppear触发时,TestItemDetailsViewModel.onViewDidAppear会被调用,但状态变更无法传递到TestItemDetails视图 - 若不使用
.navigationDestination修饰符,功能可正常运行,但无法实现从当前详情视图推入另一个TestItemDetails视图的需求
有没有人能帮我分析下哪里出错了?
内容的提问来源于stack exchange,提问作者Danail Alexiev
相关产品推荐
相关产品推荐

