SwiftUI分页滑动PageView(转接UIScrollVi
2021-08-26 本文已影响0人
zjam9333
在SwiftUI的ScrollView中似乎找不到isPagingEnabled
类似的属性,用于开启分页滑动。
iOS14中用TabView{...}.tabViewStyle(PageTabViewStyle())
也可以达到分页滑动的效果,但不知如何获得currentPage
之类的属性,无法使用自定义的PageControl
。
等官方的ScrollView
或TabView
更新page
相关的功能,便无需考虑此转接方案
那么考虑使用UIViewRepresentable
、UIHostingController
等转接UIkit的方案。
大概思路是定义一个PageView
,其content
传入UIScrollViewRep
,UIScrollViewRep
内部的UIScrollView
布局content
,且有其Coordinator
实例作为UIScrollViewDelegate
代理监听滑动contentOffset
,并且更改绑定的currentPage
struct PageView<Content: View> : View {
let pageCount: Int
@Binding var currentPage: Int
@ViewBuilder let content: () -> Content
var body: some View {
GeometryReader { geo in
UIScrollViewRep(pageCount: pageCount, currentPage: $currentPage, size: geo.size) {
HStack(alignment: .center, spacing: 0) {
content()
}
}
.frame(width: geo.size.width, height: geo.size.height)
}
}
}
struct UIScrollViewRep<ScrollViewContent: View>: UIViewRepresentable {
let pageCount: Int
@Binding var currentPage: Int
let size: CGSize
var content: (() -> ScrollViewContent)?
func makeUIView(context: Context) -> UIScrollView {
let scrollView = UIScrollView()
scrollView.contentInsetAdjustmentBehavior = .never
scrollView.isPagingEnabled = true
scrollView.delegate = context.coordinator
scrollView.showsHorizontalScrollIndicator = false
if let con = content?() {
let contro = UIHostingController(rootView: con)
if let view = contro.view {
view.backgroundColor = .clear
scrollView.addSubview(view)
}
}
return scrollView
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
var frame = CGRect(origin: .zero, size: size)
frame.size.width *= CGFloat(pageCount)
if let view = uiView.subviews.first {
view.frame = frame
}
uiView.contentSize = frame.size
}
typealias UIViewType = UIScrollView
func makeCoordinator() -> Coor<ScrollViewContent> {
let coo = Coor<ScrollViewContent>(self)
return coo
}
class Coor<ScrollViewContent: View>: NSObject, UIScrollViewDelegate {
var parent: UIScrollViewRep<ScrollViewContent>
init(_ parent: UIScrollViewRep<ScrollViewContent>) {
self.parent = parent
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let width = scrollView.frame.size.width
let offsetx = scrollView.contentOffset.x
let page = offsetx / width
self.parent.currentPage = Int(page)
}
}
最终在页面声明使用
PageView(pageCount: 4, currentPage: $currentPage) {
ForEach(0 ..< 4) { index in
Color(.red).cornerRadius(10).padding(.horizontal, 10)
}
}
.frame(height: 360)
自定义PageControl
HStack(alignment: .center, spacing: 6) {
ForEach(0 ..< 4) { index in
let isCurr = index == currentPage
Color(isCurr ? .systemBlue : .gray).cornerRadius(2).frame(width: isCurr ? 10 : 4, height: 4)
}
}.frame(height: 20).offset(x: 0, y: -10)
截屏2021-08-26 下午3.15.49.png
缺点是需要传入pageCount
,content
内只能传一个pages
的组合。要做到TabView
那样自动算出pageCount
等,还需研究