美文网首页Swift UI前端开发那些事儿
SwiftUI 实现静态&动态TabView

SwiftUI 实现静态&动态TabView

作者: Bepawbikvy | 来源:发表于2021-06-20 20:00 被阅读0次

    SwiftUI 官方文档
    SwiftUI 源码教程
    GitHub Package 论坛


    前言:最近跟着SwiftUI 源码教程基本跑了一遍。自己并尝试写一些基本的语法或小功能,在写SwiftUI时,要尝试抛开Objective-C的思想,两者完全不同的概念,SwiftUI多协议,此View非彼View,只有码的过程中才会深有感触...

    静态TabView

    导航视图便于我们创建分层的视图堆栈,方便用户能够向下获取数据,但对于显示不相关的数据而言效果不佳。为此,我们需要使用SwiftUITabView,它会在屏幕底部创建一个按钮条,点击每个按钮会显示一个不同的视图。

    1、将选项卡放置在TabView中,通过tabItem()修饰符, 可以自定义视图在选项卡栏中的显示方式,从而在其旁边提供图像和一些文本:

    TabView {
        Text("Tab 1")
            .tabItem {
                Image(systemName: "star")
    // Image("homepageSelect" )
                Text("Homepages")
            }
    
        Text("Tab 2")
            .tabItem {
                Image(systemName: "star.fill")
                Text("My Parcels")
            }
    }
    

    除了让用户通过点击其标签项来切换视图之外,SwiftUI还允许我们使用状态以编程方式控制当前视图。操作步骤如下:

    • 创建一个@State属性以跟踪当前显示的选项卡;
    • 每当我们想跳到另一个选项卡时,将该属性修改为一个新值;
    • 将其作为绑定传递到TabView中,因此将自动对其进行跟踪;
    • 告诉SwiftUI应该为该属性的每个值显示哪个选项卡。

    第一、我们需要某种状态来跟踪当前选项卡,因此将其作为属性添加到ContentView

    @State private var selectedTab = 0
    

    第二、我们需在某处进行修改时,这将要求SwiftUI切换选项卡。我们可以将onTapGesture()修饰符附加到第一个选项卡,如下所示:

    TabView {
       .onTapGesture {
            self.selectedTab = 1
        }
        Text("Homepages ")
            .tabItem {
                Image(systemName: "star")
    // Image("homepageSelect" )
                Text("Homepages")
            }
    

    第三、我们需要将TabView的选择绑定到$selectedTab。当我们创建TabView时,它将作为参数传递,因此将你的代码更新为:

    TabView (selection: $selectedTab)  {...}
    

    当我们说self.selectedTab = 1时,SwiftUI如何知道Tab1是哪个Tab?你可能会认为这些Tab可以被视为一个数组,在这种情况下,第二个Tab将位于索引1处,但这会引起各种问题:如果我们将该 Tab 移至Tab视图中的其他位置,该怎么办?

    在更深层次上,它还分解了SwiftUI的核心概念之一:我们应该能够自由地组合视图。如果Tab 1是数组中的第二项,则:

    • Tab 0是第一个tab。
    • Tab 1是第二个tab。
    • Tab 0具有显示Tab 1onTapGesture()

    对于Tab 0对如何配置其父项TabView具有深入的了解。SwiftUI提供了更好的解决方案:我们可以为每个视图附加一个唯一的标识符,并将其用于选定的标签。这些标识符称为标签,并使用tag()修饰符附加,如下所示:

    TabView (selection: $selectedTab) {
                    HomePageView()
                        .font(.title/)
                        .onTapGesture {
                            self.selectedTab = 1
                        }
                        .tabItem {
                            Image(self.selectedTab == 0 ? "homepageSelected" : "homepage")
    //                        .frame(width: 50, height: 50, alignment: .center)
                            Text("Homepages")
                        }
                        .tag(0)
    
    //                MyPacelsView()
                        .tabItem {
                            Image(self.selectedTab == 1 ? "parclesSelect" : "parcles")
                            Text("My Parcels")
                        }
                        .tag(1)
    }
    
    

    现在我们可以在选项卡之间来回切换。

    当然,仅使用01是不理想的——这些值是固定的,因此可以解决视图四处移动的问题,但它们不容易记住。所幸的是我们可以改用字符串:给每个视图有且只有唯一且反映其目的的字符串标记,然后将其用于@State属性。从长远来看,这更容易使用,对比整数更建议使用。

    提示:通常要同时使用NavigationViewTabView,但要注意:TabViewTex parse error!NavigationView, 而不是相反。

    动态TabView

    逻辑与静态TabView 一样 ,这里我就直接上代码。如下:

    import SwiftUI
    
    struct HomePageView: View {
        var body: some View {
            VStack {
                Image("homepage")
                Text("wkk ")
                    .font(.system(size: 50))
            }
        }
    }
    
    struct MyParclesView: View {
        var body: some View {
            VStack {
                Image("parcles")
                Text("My Parcels")
                    .font(.system(size: 50))
            }
        }
    }
    
    struct TabElement {
        var id: Int
        var title: String
        var image: String
        var imageSelect: String
    }
    let itemList: [TabElement] = [
        TabElement(id: 0, title: "Homepages", image: "homepage", imageSelect: "homepageSelected"),
        TabElement(id: 1, title: "My Parcels", image: "parcles", imageSelect: "parclesSelect"),
        TabElement(id: 2, title: "Booking", image: "booking", imageSelect: "bookingSelect"),
        TabElement(id: 3, title: "Me", image: "me", imageSelect: "meSelect"),
    ]
    struct ContentView: View {
        @State private var selectedTab = 0
    
        var body: some View {
            NavigationView {
                VStack {
                    TabView(selection: $selectedTab) {
                        ForEach(itemList, id: \.id) { i in
                            self.viewForTab(i)
                        }
                    }
                }
            }
        }
    
        private func viewForTab(_ item: TabElement) -> some View {
    //        print(item.title)
            return contentForTab(item)
                .tabItem {
                    Image(self.selectedTab == item.id ? item.imageSelect: item.image)
                    Text((self.selectedTab == 0 && item.id == 0) ? "" : item.title)
                }.tag(item.id)
        }
        
        private func contentForTab(_ item: TabElement) -> some View {
            switch item.id {
            case 0:
                return AnyView(HomePageView())
            case 1:
                return AnyView(MyParclesView())
            default:
                return AnyView(HomePageView())
            }
        }
        
        //  用于打印log
        func log(_ log: String) -> EmptyView {
            print("** \(log)")
            return EmptyView()
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    注意: 每个TabView遍历对应的content,需要AnyView()达到类型擦除视图。但是AnyView可能会存在性能问题。他先销毁就hierarchy,然后创建新的。这个开销可能会造成性能的损失。应该有相关优化的方案~~~

    总结

    考虑到TabView的内容常规是4个,所以用SwiftUI写没必要设置成动态的TabView,静态TabView就满足大部分APP了,并且TabView对应几个content就定义几个func,条理也清晰。

    相关文章

      网友评论

        本文标题:SwiftUI 实现静态&动态TabView

        本文链接:https://www.haomeiwen.com/subject/tidaxltx.html