如何在SwiftUI选项卡视图中保持滚动位置

如何在SwiftUI选项卡视图中保持滚动位置,swiftui,tabview,Swiftui,Tabview,在选项卡视图中使用滚动视图,我注意到当我在选项卡之间来回移动时,滚动视图不会保持其滚动位置。如何更改下面的示例以使ScrollView保持其滚动位置 import SwiftUI struct HomeView: View { var body: some View { ScrollView { VStack { Text("Line 1") Text("Line 2")

选项卡视图中使用
滚动视图
,我注意到当我在选项卡之间来回移动时,滚动视图不会保持其滚动位置。如何更改下面的示例以使ScrollView保持其滚动位置

import SwiftUI

struct HomeView: View {
    var body: some View {
        ScrollView {
            VStack {
                Text("Line 1")
                Text("Line 2")
                Text("Line 3")
                Text("Line 4")
                Text("Line 5")
                Text("Line 6")
                Text("Line 7")
                Text("Line 8")
                Text("Line 9")
                Text("Line 10")
            }
        }.font(.system(size: 80, weight: .bold))
    }
}

struct ContentView: View {
    @State private var selection = 0

    var body: some View {
        TabView(selection: $selection) {
            HomeView()
                .tabItem {
                    Image(systemName: "house")
                }.tag(0)

            Text("Out")
                .tabItem {
                    Image(systemName: "cloud")
                }.tag(1)
        }
    }
}

不幸的是,鉴于SwiftUI(iOS 13.x/Xcode 11.x)的当前限制,内置组件无法实现这一点

原因有二:

  • 当您从选项卡切换时,SwiftUI将完全处理您的视图。这就是您的滚动位置丢失的原因。它不像UIKit那样有很多屏幕外的UIViewController
  • 没有与
    UIScrollView.contentOffset
    等效的
    ScrollView。这意味着您无法将滚动状态保存到某个位置,并在用户返回选项卡时将其还原
  • 最简单的方法可能是使用
    uitabarcontroller
    填充
    UIHostingController
    s。这样,当用户在选项卡之间移动时,不会丢失每个选项卡的状态

    否则,您可以创建一个自定义选项卡容器,并自己修改每个选项卡的不透明度(而不是有条件地将它们包括在层次结构中,这是导致当前问题的原因)

    var主体:一些视图{
    ZStack{
    self.tab1内容不透明度(self.currentTab==.tab1?1.0:0.0)
    self.tab2Content.opacity(self.currentTab==.tab2?1.0:0.0)
    self.tab3内容不透明度(self.currentTab==.tab3?1.0:0.0)
    }
    }
    
    我曾经使用过这种技术,当我想让
    WKWebView
    在每次用户短暂切换时都不会完全重新加载


    我推荐第一种技术。特别是如果这是你的应用程序的主导航。

    此答案作为@arsenius答案中链接的解决方案@oivvio的补充发布,该解决方案介绍如何使用
    UIABController
    填充
    UIHostingController
    s

    链接中的答案有一个问题:如果子视图具有外部SwiftUI依赖项,则不会更新这些子视图。对于子视图只有内部状态的大多数情况,这是可以接受的。然而,如果你像我一样,是一个喜欢全局Redux系统的React开发人员,你会遇到麻烦

    为了解决这个问题,关键是每次调用
    updateuivewcontroller
    时,都要为每个
    UIHostingController
    更新
    rootView
    。我的代码还避免了创建不必要的
    UIView
    UIViewControllers
    :如果不将它们添加到视图层次结构中,它们的创建成本不会太高,但浪费越少越好

    警告:代码不支持动态选项卡视图列表。为了正确地支持这一点,我们希望识别每个子选项卡视图,并执行数组差异以正确地添加、排序或删除它们。这在原则上是可以做到的,但超出了我的需要

    我们首先需要一个
    选项卡item
    。通过这种方式,控制器可以获取所有信息,而无需创建任何
    uitabaritem

    struct XNTabItem:视图{
    标题:字符串
    让图像:UIImage?
    让身体:AnyView
    公共初始化(标题:字符串,图像:UIImage?,@ViewBuilder内容:()->内容){
    self.title=标题
    self.image=image
    self.body=AnyView(content())
    }
    }
    
    然后我们有控制器:

    struct XNTabView:UIViewControllerRepresentable{
    让tabItems:[XNTabItem]
    func makeUIViewController(上下文:UIViewControllerRepresentableContext)->UIAbbarController{
    让rootController=UITabBarController()
    rootController.viewControllers=tabItems.map{
    让host=UIHostingController(rootView:$0.body)
    host.tabBarItem=UITabBarItem(标题:$0.title,图像:$0.image,选择图像:$0.image)
    返回主机
    }
    返回根控制器
    }
    func updateUIViewController(uuController:UIAbbarController,上下文:UIViewControllerRepresentableContext){
    让子项=rootController.viewControllers为![UIHostingController]
    用于zip中的(newTab,主机)(self.tabItems,子项){
    host.rootView=newTab.body
    如果host.tabBarItem.title!=host.tabBarItem.title{
    host.tabBarItem.title=host.tabBarItem.title
    }
    如果host.tabBarItem.image!=host.tabBarItem.image{
    host.tabBarItem.image=host.tabBarItem.image
    }
    }
    }
    }
    
    子控制器在
    makeUIViewController
    中初始化。无论何时调用
    updateUIViewController
    ,我们都会更新每个子控制器的根视图。我没有对
    rootView
    进行比较,因为根据苹果关于视图更新方式的描述,我觉得在框架级别也会进行同样的检查。我可能错了

    使用它真的很简单。下面是我从当前正在进行的模拟项目中获取的部分代码:

    
    类模型:ObservieObject{
    @已发布变量allHouseInfo=HouseInfo.samples
    公共函数flipFavorite(用于id:Int){
    如果let index=(allHouseInfo.firstIndex{$0.id==id}){
    allHouseInfo[index].isFavorite.toggle()
    }
    }
    }
    结构收藏夹视图:视图{
    let favorites:[HouseInfo]
    var body:一些观点{
    如果favorites.count>0{
    返回任意视图(滚动视图{
    ForEach(收藏夹){
    卡片内容(信息:$0)
    }
    })
    }否则{
    返回任意视图(文本(“无收藏夹”))
    }
    }
    }
    结构ContentView:View{
    静态let-housingTabImage=UIImage(系统名称:“house.fill”)
    静态let-favoritesTabImage=UIImage(系统名称:“heart.fill”)
    @ObservedObject var模型=模型()
    我的最爱:[何