先看下要实现的效果
- 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)
}
}
}