Swiftui implements static & dynamic tabview

Time:2022-1-30

Swiftui official document
Swiftui source code tutorial
GitHub package Forum


Recently followedSwiftui source code tutorialBasically ran once. And try to write some basic grammar or small functions in writingSwiftUITry to get rid of it whenObjective-CTheir ideas, completely different concepts,SwiftUIMulti protocol, thisViewNot thatView, only in the process of coding can I feel deeply

static stateTabView

Navigation view is convenient for us to create a hierarchical view stack, which is convenient for users to get data down, but it is not effective for displaying irrelevant data. To do this, we need to useSwiftUIofTabView, it will create a button bar at the bottom of the screen, and clicking each button will display a different view.

1. Place the tab in tabview throughtabItem()Modifier to customize how the view is displayed in the tab bar, providing an image and some text next to it:

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

    Text("Tab 2")
        .tabItem {
            Image(systemName: "star.fill")
            Text("My Parcels")
        }
}

In addition to allowing users to switch views by clicking on their tab items,SwiftUIIt also allows us to use state to programmatically control the current view. The operation steps are as follows:

  • Create a@StateProperty to track the currently displayed tabs;
  • Whenever we want to jump to another tab, change the property to a new value;
  • Pass it as a binding toTabViewTherefore, it will be tracked automatically;
  • tellSwiftUIWhich tab should be displayed for each value of this attribute.

First, we need some state to track the current tab, so we add it as an attribute toContentView

@State private var selectedTab = 0

Second, when we need to make changes somewhere, this will requireSwiftUISwitch tabs. We canonTapGesture()The modifier is attached to the first tab as follows:

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

Third, we need toTabViewYour selection is bound to$selectedTab。 When we createTabViewIt will be passed as a parameter, so update your code to:

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

When we sayself.selectedTab = 1When,SwiftUIHow to knowTab1Which is itTab? You might think theseTabCan be treated as an array, in this case, the second oneTabWill be at index 1, but this will cause various problems: if weTabmove toTabWhat about other positions in the view?

At a deeper level, it also decomposesSwiftUIOne of the core concepts of: we should be able to freely combine views. IfTab 1Is the second item in the array, then:

  • Tab 0This is the first tab.
  • Tab 1This is the second tab.
  • Tab 0With displayTab 1ofonTapGesture()

aboutTab 0How to configure its parentTabViewHave in-depth understanding.SwiftUIProvides a better solution: we can attach a unique identifier to each view and use it for the selected label. These identifiers are called tags and usetag()The modifier is appended as follows:

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)
}

Now we can switch back and forth between tabs.

Of course, only use0and1Is not ideal – these values are fixed, so you can solve the problem of moving the view around, but they are not easy to remember. Fortunately, we can use strings instead: give each view a string tag that is unique and reflects its purpose, and then use it for@StateProperties. In the long run, this is easier to use and is more recommended than integers.

Tips:Usually used at the same timeNavigationViewandTabView, but note:TabViewTex parse error!NavigationView, not vice versa.

dynamicTabView

Logic and staticTabViewSimilarly, here I’ll go directly to the code. As follows:

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())
        }
    }
    
    //Used to print log
    func log(_ log: String) -> EmptyView {
        print("** \(log)")
        return EmptyView()
    }
}

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

be careful:eachTabViewTraversing the corresponding content requiresAnyView()Type erase view reached. howeverAnyViewThere may be performance problems. He destroyed it firsthierarchy, and then create a new one. This overhead may result in performance loss. There should be relevant optimization schemes~~~

summary

Considering that the content of tabview is usually 4, so useSwiftUIWriting doesn’t have to be dynamicTabView, staticTabViewIt meets most apps, andTabViewDefine several corresponding contentsfunc, organized and clear.