QSD的Swift妙妙屋1:TabView

TabView


开发者手册

说明

TabView is a view that switches between multiple child views using interactive user interface elements.
TabView是一个使用交互式用户界面元素在多个子视图之间切换的视图。

申明

struct TabView<SelectionValue, Content> where SelectionValue : Hashable, Content : View

概述

To create a user interface with tabs, place views in a TabView and apply the [tabItem(_:)] modifier to the contents of each tab. The following creates a tab view with three tabs:

例如以下的代码

TabView {
    Text("The First Tab")
        .tabItem {
            Image(systemName: "1.square.fill")
            Text("First")
        }
    Text("Another Tab")
        .tabItem {
            Image(systemName: "2.square.fill")
            Text("Second")
        }
    Text("The Last Tab")
        .tabItem {
            Image(systemName: "3.square.fill")
            Text("Third")
        }
}
.font(.headline)

可以创造一个最基本的TabView

!!!NOTICE

Tab里的内容只能包含文字,或图片,或文字和图片的组合

Tab一般来说最多只能显示5个,多余的Tab(包括第5个)会被省略到一个...

5个Tab
6个Tab

今日目标

1. 以Logo+文字的形式创建5个TabView

2. 封装TabView到一个TabContent的func中,并添加选中Tab能改变Logo样式的功能

1. 创建5个TabView

外观

上面是一个图标,下面是文字,所以才用VStack

以第一个lightbulb💡+'饭乎'为例

VStack{
    Image(systemName: "lightbulb")
    Text("饭乎")
  }

于是得到了一个:

构造TabView核心代码

TabView(selection: $selection){
            VStack{
                Text("现在选中的Tab:\(selection)")
                    .font(.title)
                Button(action: {
                    withAnimation{
                        self.selection = 1
                    }
                })
                {
                    Text("Change Tab to 1")
                        .font(.largeTitle)
                } //用按钮手动切换到第二个Tab
            }
                .tabItem {
                    VStack{
                        Image(systemName: "lightbulb")
                        Text("饭乎")
                    }
                }
                .tag(0)
// 其他几个tab以此类推

解释

selection: $selction用于获取当前选中的Tab编号,也就是最后tag里的值
tag用于给每一个Tab标记一个独特的编号
systemName是个好东西,能调用Apple全套的图标。可以下载SF Symbols查阅图标名称

2. 创造一个TabContent的func,输入Tab的编号、是否选中,并返回some View

先把每个Tab的名称/图标名称加到Array里

let TabName: Array<String> = ["饭乎","饭厅","饭迹","饭桶","饭具","垃圾桶"]
let TabLogoName: Array<String> = ["lightbulb","tray.full","clock","person.circle","hammer","trash"]

TabContent返回一个VStack,包含Logo和文字

选中时更改图标:如果选中,就图标名称末尾加上.fill

func TabContent (TabIndex: Int, Selected: Bool) -> some View {
    return
        VStack {
            Image(systemName: Selected ? (TabLogoName[TabIndex] + ".fill"): TabLogoName[TabIndex])
            Text(TabName[TabIndex])
        }
}

解释

Selected ? (TabLogoName[TabIndex]+".fill"):TabLogoName[TabIndex]
形如XXX ? A : B,如果XXX是真的就返回A,否则返回B

some View

some View的这个奇怪用法,俺也不是完全懂。
这里有一篇文章仿佛可以参考https://zhuanlan.zhihu.com/p/105213050
some View的用法使得SwiftUI支持接受一些opaque return types——即我们并不需要非常明确的告诉SwiftUI这个东西是啥,他只要遵循conform View就可以了。
简而言之,some View是指,这个东西长得像一个View,遵循View的法则。

因此在SwiftUI的语法下,以下代码是会报错的:

    func WrongExample() -> View {
        return Text("This code is wrong!")
    }

错误信息:Protocol 'View' can only be used as a generic constraint because it has Self or associated type requirements,这是因为明明我们想要它return一个View,但是程序里return了一个Text!

这样是正确的代码;

    func RightExample() -> Text {
        return Text("Ohhhhhhh!")
    }

但是这样并不是很方便——这个东西只能return一个Text,应用不是非常广泛;
因此我们选择使用some View

    func RightExample() -> some View {
        return Text("Ohhhhhhhh!")
    }
引用斯坦福那位教授的奇妙比喻:
  1. View就像乐高。
  2. Text就像一块乐高——他是乐高的基本元件之一,他是乐高。
  3. 把很多乐高元件组合起来,形成一个大东西,他还是乐高。
  4. Lego Bricks -> Lego Furniture -> Lego House -> Lego Neighbourhood -> Lego World!他们都是乐高。
  5. 和乐高不同的是,有些特殊的View用来组合像Text一样的基本元件,他们叫View Combiner
  6. 因此,some View也可以返回一些View Combiner,他们之中有很多的View

然后主程序中调用下就可以了

最终代码&运行效果


//
//  ContentView.swift
//  WhereToEat?
//
//  Created by QSD on 2020/7/2.
//  Copyright © 2020 QSDQSB. All rights reserved.
//

import SwiftUI

let TabName: Array<String> = ["饭乎","饭厅","饭迹","饭桶","饭具","垃圾桶"]
let TabLogoName: Array<String> = ["lightbulb","tray.full","clock","person.circle","hammer","trash"]

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

    var focused = 0
    var body: some View {
        
        TabView(selection: $selection){
            VStack{
                Text("现在选中的Tab:\(selection)")
                    .font(.title)
                Button(action: {
                    withAnimation{
                        self.selection = 1
                    }
                })
                {
                    Text("Change Tab to 1")
                        .font(.largeTitle)

                }
            }
                .tabItem {
                TabContent(TabIndex: 0, Selected: selection == 0)

            }
                .tag(0)
                    
            
            // MARK: - 饭厅
            ZStack{
                Text("现在选中的Tab:\(selection)")
                .font(.title)
            }
                .tabItem {
                    TabContent(TabIndex: 1, Selected: selection == 1)

                }
                .tag(1)
            
            // MARK: - 饭迹
            ZStack{
                Text("现在选中的Tab:\(selection)")
                    .font(.title)
            }
                .tabItem{
                    ZStack{
                    TabContent(TabIndex: 2, Selected: selection == 2)
                    }
                }
                .tag(2)
            
            // MARK: - 饭桶
            ZStack{
                Text("现在选中的Tab:\(selection)")
                    .font(.title)
            }
                .tabItem{
                    TabContent(TabIndex: 3, Selected: selection == 3)
                }
                .tag(3)
            
            Text("饭具")
                .tabItem{
                    TabContent(TabIndex: 4, Selected: selection == 4)
                }
                .tag(4)
        }.accentColor(.pink)
    }
    
    
    
}

func TabContent (TabIndex: Int, Selected: Bool) -> some View {
    return
        VStack {
            Image(systemName: Selected ? (TabLogoName[TabIndex] + ".fill"): TabLogoName[TabIndex])
            Text(TabName[TabIndex])
        }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

当然,如果觉得一遍一遍输tag很烦的话,用个ForEach就完事儿了:

                ForEach (1..<3) {Index in
                    ZStack{
                        Text("现在选中的Tab:\(self.selection)")
                            .font(.title)
                    }
                        .tabItem {
                            TabContent(TabIndex: Index, Selected: self.selection == Index)
                            }
                        .tag(Index)
                }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容