如何创建适配全屏及旋转的UIPageViewController的SwiftUI包装器?
兼容iOS 15+的SwiftUI ScrollView分页方案
如果没法用iOS 17新增的分页修饰器,下面两种方案可以完美适配iOS 15及更高版本,覆盖不同场景需求:
方法一:纯SwiftUI手势驱动分页
不用依赖UIKit,全用SwiftAPI实现,适合轻量分页场景。核心是通过拖拽手势计算偏移,结合滚动偏移量控制页面切换。
import SwiftUI struct PagingScrollView<Content: View>: View { let pageCount: Int let content: (Int) -> Content @State private var currentPage = 0 @State private var offsetX: CGFloat = 0 var body: some View { ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 0) { ForEach(0..<pageCount, id: \.self) { index in content(index) .frame(width: UIScreen.main.bounds.width) } } } .content.offset(x: offsetX) .gesture( DragGesture() .onChanged { value in // 拖拽时实时跟随手指移动 offsetX = value.translation.width - CGFloat(currentPage) * UIScreen.main.bounds.width } .onEnded { value in // 结束拖拽时判断切换方向 let pageOffset = value.translation.width / UIScreen.main.bounds.width let newPage = currentPage - Int(pageOffset.rounded()) currentPage = max(0, min(newPage, pageCount - 1)) // 平滑滚动到目标页 withAnimation { offsetX = -CGFloat(currentPage) * UIScreen.main.bounds.width } } ) .onAppear { // 初始定位到第一页 offsetX = -CGFloat(currentPage) * UIScreen.main.bounds.width } } } // 使用示例 struct ContentView: View { var body: some View { PagingScrollView(pageCount: 3) { index in Color( red: Double.random(in: 0...1), green: Double.random(in: 0...1), blue: Double.random(in: 0...1) ) .overlay( Text("Page \(index + 1)") .font(.largeTitle) .foregroundColor(.white) ) } } }
方法二:包装UIPageViewController(更稳定)
如果需要原生级的分页体验(比如自动吸附、边缘弹跳控制),直接把UIKit的UIPageViewController桥接到SwiftUI,兼容性和稳定性拉满。
import SwiftUI import UIKit struct PageViewController<Page: View>: UIViewControllerRepresentable { var pages: [Page] @Binding var currentPage: Int func makeUIViewController(context: Context) -> UIPageViewController { let pageVC = UIPageViewController( transitionStyle: .scroll, navigationOrientation: .horizontal ) pageVC.dataSource = context.coordinator pageVC.delegate = context.coordinator return pageVC } func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) { let currentVC = UIHostingController(rootView: pages[currentPage]) pageViewController.setViewControllers([currentVC], direction: .forward, animated: true) } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate { var parent: PageViewController init(_ parent: PageViewController) { self.parent = parent } // 上一页逻辑 func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let index = parent.pages.firstIndex(where: { (viewController as? UIHostingController<Page>)?.rootView === $0 }) else { return nil } return index > 0 ? UIHostingController(rootView: parent.pages[index - 1]) : nil } // 下一页逻辑 func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let index = parent.pages.firstIndex(where: { (viewController as? UIHostingController<Page>)?.rootView === $0 }) else { return nil } return index < parent.pages.count - 1 ? UIHostingController(rootView: parent.pages[index + 1]) : nil } // 更新当前页码 func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { guard completed, let currentVC = pageViewController.viewControllers?.first, let index = parent.pages.firstIndex(where: { (currentVC as? UIHostingController<Page>)?.rootView === $0 }) else { return } parent.currentPage = index } } } // 使用示例 struct ContentView: View { @State private var currentPage = 0 var body: some View { PageViewController( pages: [ Color.red.overlay(Text("Page 1").font(.largeTitle).foregroundColor(.white)), Color.blue.overlay(Text("Page 2").font(.largeTitle).foregroundColor(.white)), Color.green.overlay(Text("Page 3").font(.largeTitle).foregroundColor(.white)) ], currentPage: $currentPage ) } }
内容的提问来源于stack exchange,提问作者wristbands
相关产品推荐
相关产品推荐

