Search code examples
swiftswiftui

How to limit the dot indicator tabview


i have 8 pages or data want show, and i want just 3 indiator for that.

i have 8 pages or data want show, and i want just 3 indiator for that.

import SwiftUI

struct OnBoardingModel {
    let image: String
    let title: String
    let subTitle: String
}


struct OnBoardingView: View {
    @State private var currentStep = 0
    
    let datas = [
        OnBoardingModel(image: "onboard1", title: "OnBoard1", subTitle: "OnBoard1"),
        OnBoardingModel(image: "onboard1", title: "OnBoard2", subTitle: "OnBoard2"),
        OnBoardingModel(image: "onboard1", title: "OnBoard3", subTitle: "OnBoard3"),
        OnBoardingModel(image: "onboard1", title: "OnBoard4", subTitle: "OnBoard4"),
        OnBoardingModel(image: "onboard1", title: "OnBoard5", subTitle: "OnBoard5"),
        OnBoardingModel(image: "onboard1", title: "OnBoard6", subTitle: "OnBoard6"),
        OnBoardingModel(image: "onboard1", title: "OnBoard7", subTitle: "OnBoard7"),
        OnBoardingModel(image: "onboard1", title: "OnBoard8", subTitle: "OnBoard8")
    ]
    
    var body: some View {
        VStack {
            TabView(selection: $currentStep) {
                ForEach(0..<datas.count, id: \.self) { index in
                    VStack {
                        Image(datas[index].image)
                            .resizable()
                            .frame(width: 250, height: 250)
                        
                        Text(datas[index].title)
                            .font(.title)
                            .fontWeight(.bold)
                        
                        Text(datas[index].subTitle)
                            .font(.subheadline)
                    }
                    .tag(index)
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            
            HStack {
                ForEach(0..<datas.count, id: \.self) { index in
                    if index == currentStep {
                        Rectangle()
                            .frame(width: 20, height: 10)
                            .clipShape(RoundedRectangle(cornerRadius: 10))
                            .foregroundStyle(Color("hanBlue"))
                    } else {
                        Circle()
                            .frame(width: 10, height: 10)
                            .foregroundStyle(Color.gray)
                    }
                }
            }
            
            Button(action: {
                if currentStep < datas.count - 1 {
                    currentStep += 1
                } else {
                    //Logic
                }
            }) {
                Text("Continue")
            }
        }
    }
}

#Preview {
    OnBoardingView()
}

i want make the dot indicator show 3 dot. like this so user can swipe swipe until the end of data. but its only show 3 indicator.

(https://i.sstatic.net/2PfHm.png)


Solution

  • I would put the dots in a scroll view so the animation looks smooth when going through each tab.

    ScrollView(.horizontal) {
        HStack {
            ForEach(0..<datas.count, id: \.self) { index in
                
                Circle()
                    .frame(width: index == currentStep ? 16 : 10, height: index == currentStep ? 16 : 10)
                    .foregroundStyle(.white.opacity(index == currentStep ? 1 : 0.5))
            }
        }
        .padding()
        .scrollTargetLayout()
    }
    .background(.blue, in: RoundedRectangle(cornerRadius: 30))
    .frame(width: 70) // found empirically to fit 3 dots
    .scrollTargetBehavior(.viewAligned)
    .scrollPosition(id: Binding($currentStep), anchor: .center)
    .allowsHitTesting(false) // disallow manual scrolling
    

    Remember to also do .animation() in the tab view to make this animated:

    TabView(selection: $currentStep.animation())
    

    Feel free to customise the dots' appearance. I tried to make it similar to the screenshot in the question.