Native Pager + Native SegmentedControl in SwiftUI
关键点
为了保证MySegmentControl和MyPageControlView能实现联动效果,需要用同一个index
MySegmentControl(index: self.$index)
MyPageControlView(index: self.$index)
关键点
利用background来计算每个item的宽度和位置,将用于指示器
.background (
GeometryReader { geo in
Color.clear.onAppear {
let index = self.model.sections.firstIndex(of: section) ?? 0
self.frames[index] = geo.frame(in: .global).midX - 11.5
}
}
)
显示指示器,指示器可自定义
.overlay(self.underline, alignment: .bottomLeading)
var underline: some View {
Color.red
.frame(width: 23, height: 5)
.padding(.leading, self.frames[self.index] ?? 0)
}
item点击切换,同样可自定义为Button
.overlay(MoveUnderlineButton(){
withAnimation {
self.index = self.model.sections.firstIndex(of: section) ?? 0
}
})
struct MoveUnderlineButton: View {
let action: () -> Void
var body: some View {
GeometryReader { geometry in
Button(action: {
self.action()
}) {
Rectangle().foregroundColor(.clear)
}
}
}
}
关键点
利用content来设定偏移量
.content
.offset(x: self.getOffset())
private func getOffset() -> CGFloat {
if self.oldIndex != self.index {
DispatchQueue.main.async {
self.oldIndex = self.index
self.isUserSwiping = false
}
}
let offset = self.isUserSwiping ? self.offset : CGFloat(self.index) * -APPWidth
return offset
}
使用DragGesture来,计算用户滑动的位置
.gesture(
DragGesture()
.onChanged({ value in
self.isUserSwiping = true
self.offset = value.translation.width + -APPWidth * CGFloat(self.index)
})
.onEnded({ value in
if (value.translation.width < 0){
if value.predictedEndTranslation.width < APPWidth / 2, self.index < self.model.sections.count - 1 {
self.index += 1
}
}
else if (value.translation.width > 0){
if value.predictedEndTranslation.width > APPWidth / 2, self.index > 0 {
self.index -= 1
}
}
withAnimation {
self.isUserSwiping = false
}
})
)
SwiftUISegmentedControlPage is released under the MIT license. See LICENSE for details.