SwiftUI 实现 Segment Tab

最近忙得前言都不想喵两句了...希望此demo对你有帮助~


效果图:


Segment Tab 效果图.gif

核心实现原理:
AppBar(index: $index, offset: $offset)控制按钮逻辑;GeometryReader包裹你按钮对应的所有视图,将灵活的首选大小返回到其父布局,offset控制偏移量。

实现代码如下:


import SwiftUI

struct ContentView: View {
    var body: some View {
        Home()
    }
}

struct Home: View {
    @State var index = 0
    @State var offset: CGFloat = 0
    var width = UIScreen.main.bounds.width
    @State var offsetMoved: CGFloat = 0
    
    var body: some View {
        VStack(spacing: 0) {
            
            AppBar(index: $index, offset: $offset)
            
            GeometryReader{g in
                HStack(spacing: 0) {
                    Scape()
                        .frame(width: g.frame(in : .global).width)
                    Code()
                        .frame(width: g.frame(in : .global).width)
                    Cat()
                        .frame(width: g.frame(in : .global).width)
                }
                .offset(x: self.offset + self.offsetMoved)

                .highPriorityGesture(DragGesture()
                .onEnded({ value in
                        if value.translation.width > 150 { // minimum drag...
                            print("right")
                            self.changeView(left: false)
                        } else if value.translation.width < -150 {
                            print("left")
                            self.changeView(left: true)
                        }
                        self.offsetMoved = 0 // 重置微调
                    })
                    .onChanged({ value in
                        self.offsetMoved = value.translation.width // 微调
                    }))
            }
        }
        .animation(.default)
        .edgesIgnoringSafeArea(.all)
    }
    
    func changeView(left: Bool) {
        // tab
        if left {
            
            if self.index != 2 {
                self.index += 1
            }
        } else {
            if self.index != 0 {
                self.index -= 1
            }
        }
        
        // view
        if self.index == 0 {
            self.offset = 0
        }
        else if self.index == 1 {
            self.offset = -self.width
        }
        else {
            self.offset = -self.width*2
        }
        // change the width based on the size of the tabs...
    }
}

struct AppBar: View {
    @Binding var index: Int
    @Binding var offset: CGFloat
    
    let imageWH: CGFloat = 25
    
    var width = UIScreen.main.bounds.width
    
    var body: some View {
        
        VStack(alignment: .leading, content: {
            
            Text("Segment Tab")
                .font(.title)
                .foregroundColor(.white)
                .padding(.leading)
                .padding(.bottom)
            
            HStack {
                
                Button(action: {
                    self.index = 0
                    self.offset = 0
                }) {
                    VStack(spacing: 8){
                        HStack(spacing: 12) {
                            Image(systemName: "leaf")
                                .resizable()
                                .foregroundColor(self.index == 0 ? .white : Color.white.opacity(0.7))
                                .frame(width: imageWH, height: imageWH)
                            Text("Scape")
                                .foregroundColor(self.index == 0 ? .white : Color.white.opacity(0.7))
                        }
                        
                        Capsule()
                            .foregroundColor(self.index == 0 ? Color.white : Color.clear)
                            .frame(width: 32, height: 4)
                        Spacer().frame(maxWidth: .infinity, maxHeight: 0)
                    }
                }
                
                Button(action: {
                    self.index = 1
                    self.offset = -self.width
                }) {
                    VStack(spacing: 8){
                        HStack(spacing: 12) {
                            Image(systemName: "laptopcomputer")
                                .resizable()
                                .foregroundColor(self.index == 1 ? .white : Color.white.opacity(0.7))
                                .frame(width: imageWH+8, height: imageWH)
                            Text("Code")
                                .foregroundColor(self.index == 1 ? .white : Color.white.opacity(0.7))
                        }
                        
                        Capsule()
                            .foregroundColor(self.index == 1 ? Color.white : Color.clear)
                            .frame(width: 32, height: 4)
                        Spacer().frame(maxWidth: .infinity, maxHeight: 0)
                    }
                }
                
                Button(action: {
                    self.index = 2
                    self.offset = -self.width*2
                }) {
                    VStack(spacing: 8){
                        HStack(spacing: 12) {
                            Image("Cat_Footprint")
//                                .resizable()
                                .foregroundColor(self.index == 2 ? Color.white : Color.white.opacity(0.7))
                                .frame(width: imageWH, height: imageWH)
                                
                            Text("Cat")
                                .foregroundColor(self.index == 2 ? .white : Color.white.opacity(0.7))
                                
                        }
                        
                        Capsule()
                            .foregroundColor(self.index == 2 ? Color.white : Color.clear)
                            .frame(width: 32, height: 4)
                        Spacer().frame(maxWidth: .infinity, maxHeight: 0)
                    }
                }
            }
        })
        .padding(.top, (UIApplication.shared.windows.first?.safeAreaInsets.top)! + 15)
        .padding(.horizontal)
        .padding(.bottom, 10)
        .background(Color(.systemBlue))
    }
}

struct Cat: View {
    var body: some View {
        
        ScrollView(.vertical, showsIndicators: false) {
            VStack(spacing: 0) {
                ForEach(1...8, id: \.self) {i in
                    
                    HStack(spacing: 0) {
                        Spacer().frame(width: 16)
                        Image("cat_\(i)")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(height: 200)
                            .cornerRadius(15)
                            .padding(.top)
                            .padding(.horizontal)
                        Spacer().frame(width: 16)
                    }
                }
            }
        }.padding(.bottom, 18)
    }
}

struct Code: View {
    var body: some View {
        GeometryReader{_ in
            ScrollView(.vertical, showsIndicators: false) {
                VStack(spacing: 0) {
                    
                    ForEach(1...4, id: \.self) {i in
                        HStack(spacing: 0) {
                            Spacer().frame(width: 16)
                            Image("code_\(i)")
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(height: 200)
                                .cornerRadius(15)
                                .padding(.top)
                                .padding(.horizontal)
                            Spacer().frame(width: 16)
                        }
                    }
                    
                }
            }.padding(.bottom, 18)
        }
        .background(Color.white)
    }
}

struct Scape: View {
    var body: some View {
        
        GeometryReader{_ in
            
            ScrollView(.vertical, showsIndicators: false) {
                VStack(spacing: 0) {
                    
                    ForEach(1...5, id: \.self) {i in
                        HStack(spacing: 0) {
                            Spacer().frame(width: 16)
                            Image("scape_\(i)")
                                .resizable()
                                .aspectRatio(contentMode: .fill)
                                .frame(height: 200)
                                .cornerRadius(15)
                                .padding(.top)
                                .padding(.horizontal)
                            Spacer().frame(width: 16)
                        }
                    }
                    
                }
            }.padding(.bottom, 18)
        }
        .background(Color.white)
    }
}

你可能感兴趣的:(SwiftUI 实现 Segment Tab)