SwiftUI实战之Page左右分页滑动

先看下要实现的效果


​​​​​​​

  • Scrollview高级用法,实现分页滚动

实现方案:

一、定义页面struct

struct PageContent : Identifiable,Hashable,Equatable{
    static func == (lhs: PageContent, rhs: PageContent) -> Bool {
        return lhs.id == rhs.id
    }
    
    var hashValue: Int { return id.hashValue }
    
    func hash(into hasher: inout Hasher){ }
    
    var id : UUID = UUID()
    var title : String
    var isSelect : Bool = false
    var view : AnyView
    

    mutating func updateSelect(_ select : Bool) {
        self.isSelect = select
    }
}

二、定义ViewModel的Class

class HDHomeBottomViewModel : ObservableObject {
    @Published var pages = [PageContent]()
    @Published var currentIndex : Int = 0
}

三、制作分布社图Struct

struct PagerView : View {
    private var currentIndex: Int = 0
    @State private var offset: CGFloat = 0
    @StateObject var vm = HDHomeBottomViewModel()
    private var pages : [PageContent]
    
    init(pages : [PageContent],currentIndex : Int) {
        self.pages = pages
        self.currentIndex = currentIndex
    }
    
        var body: some View {
            GeometryReader { geometry in
                ScrollViewReader { proxy in
                    VStack(spacing: 0, content: {
                        ScrollView(.horizontal, showsIndicators: false,content: {
                            VStack(spacing: 0) {
                                HStack(spacing: 0, content: {
                                    ForEach(vm.pages.indices,id: \.self){ index in
                                        Text(vm.pages[index].title).frame(width: 80,height: 40).font(Font.system(size: 14)).foregroundColor(
                                            index == vm.currentIndex ? .blue : .black
                                        )
                                            .id(index)
                                            .onTapGesture {
                                                withAnimation {
                                                    updateIndex(index)
                                                    proxy.scrollTo(vm.currentIndex, anchor: .center)
                                                    self.offset = -geometry.size.width * CGFloat(vm.currentIndex)
                                                }
                                            }
                                    }
                                    
                                })
                                .frame(minWidth:geometry.size.width)
                                HStack(spacing: 0, content: {
                                    Color.blue.frame(width: 80,height: 1.5).offset(x:CGFloat(80 * (vm.currentIndex)),y:0).animation(.easeOut(duration: 0.24))
                                    Spacer()
                                })
                            }
                        })
                        ScrollView(.horizontal, showsIndicators: false) {
                            HStack(alignment: .center, spacing: 0) {
                                ForEach(vm.pages.indices,id:\.self) { index in
                                    vm.pages[index].view.frame(width: geometry.size.width, height: nil).id(index)
                                }
                            }
                        }
                        .content.offset(x: self.offset)
                        .frame(width: geometry.size.width, height: nil, alignment: .leading)
                        .gesture(DragGesture()
                            .onChanged({ value in
                                self.offset = value.translation.width - geometry.size.width * CGFloat(vm.currentIndex)
                            })
                                .onEnded({ value in
                                    if abs(value.predictedEndTranslation.width) >= geometry.size.width / 2 {
                                        var nextIndex: Int = (value.predictedEndTranslation.width < 0) ? 1 : -1
                                        nextIndex += vm.currentIndex
                                        vm.currentIndex = nextIndex.keepIndexInRange(min: 0, max: vm.pages.endIndex - 1)
                                    }
                                    withAnimation {
                                        self.offset = -geometry.size.width * CGFloat(vm.currentIndex)
                                        updateIndex(vm.currentIndex)
                                        proxy.scrollTo(vm.currentIndex, anchor: .center)
                                    }
                                })
                        )
                    })
                }
            }.onAppear {
                vm.pages.append(contentsOf: self.pages)
                if self.currentIndex < self.pages.count {
                    vm.currentIndex = self.currentIndex
                }
            }
        }
    
    func updateIndex(_ newIndex : Int) {
        print(newIndex)
        vm.currentIndex = newIndex
        for (index,_) in vm.pages.enumerated() {
            vm.pages[index].updateSelect(index == newIndex ? true : false)
        }
    }
}

extension Int {
    func keepIndexInRange(min: Int, max: Int) -> Int {
        switch self {
        case ..<min: return min
        case max...: return max
        default: return self
        }
    }
}

四、使用分页视图

import SwiftUI

@main
struct SwiftUITestApp: App {
    var body: some Scene {
        WindowGroup {
            PagerView(pages: [PageContent(title: "第一页",isSelect: true, view: AnyView(Color.orange)),
                              PageContent(title: "第二页",isSelect: false, view: AnyView(Color.green)),
                              PageContent(title: "第三页",isSelect: false, view: AnyView(Color.yellow)),
                              PageContent(title: "第四页",isSelect: false, view: AnyView(Color.blue)),
                              PageContent(title: "第五页",isSelect: false, view: AnyView(Color.black)),
                              PageContent(title: "第六页",isSelect: false, view: AnyView(Color.red)),
                              PageContent(title: "第七页",isSelect: false, view: AnyView(Color.accentColor)),
                              PageContent(title: "第八页",isSelect: false, view: AnyView(Color.blue)),
                              PageContent(title: "第九页",isSelect: false, view: AnyView(Color.black)),
                              PageContent(title: "第十页",isSelect: false, view: AnyView(Color.red)),
                              PageContent(title: "第十一页",isSelect: false, view: AnyView(Color.accentColor))],currentIndex: 14)
        }
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

humiaor

你的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值