Created
February 16, 2025 07:24
-
-
Save yosshi4486/cad596cb23f201ee5084d416156b0284 to your computer and use it in GitHub Desktop.
Simple UIPageViewController Bridge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import UIKit | |
// Add refinements to this official practice: | |
// https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit | |
struct PageView<Page : View> : View { | |
var pages: () -> [Page] | |
@State private var currentIndex: Int = 0 | |
init(@PagesBuilder<Page> pages: @escaping () -> [Page]) { | |
self.pages = pages | |
} | |
var body: some View { | |
ZStack { | |
PageViewController(currentIndex: $currentIndex, pages: pages).ignoresSafeArea(edges: .bottom) | |
VStack { | |
Spacer() | |
PageControl(numberOfPages: pages().count, currentPage: $currentIndex) | |
} | |
} | |
} | |
} | |
@resultBuilder | |
struct PagesBuilder<Page : View> { | |
public static func buildBlock(_ components: Page...) -> [Page] { | |
components | |
} | |
} | |
struct PageViewController<Page : View> : UIViewControllerRepresentable { | |
var pages: () -> [Page] | |
@Binding var currentIndex: Int | |
init(currentIndex: Binding<Int>, @PagesBuilder<Page> pages: @escaping () -> [Page]) { | |
self._currentIndex = currentIndex | |
self.pages = pages | |
} | |
func makeUIViewController(context: Context) -> UIPageViewController { | |
let viewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) | |
viewController.dataSource = context.coordinator | |
viewController.delegate = context.coordinator | |
return viewController | |
} | |
func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) { | |
uiViewController.setViewControllers([context.coordinator.controllers[currentIndex]], direction: .forward, animated: false) | |
} | |
func makeCoordinator() -> Coordinator { | |
return Coordinator(parent: self) | |
} | |
class Coordinator: NSObject, UIPageViewControllerDelegate, UIPageViewControllerDataSource { | |
var parent: PageViewController | |
var controllers: [UIViewController] = [] | |
init(parent: PageViewController) { | |
self.parent = parent | |
self.controllers = parent.pages().map({ UIHostingController(rootView: $0) }) | |
} | |
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { | |
guard let index = controllers.firstIndex(of: viewController), index != 0 else { | |
return nil | |
} | |
return controllers[index - 1] | |
} | |
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { | |
guard let index = controllers.firstIndex(of: viewController), index != (controllers.endIndex - 1) else { | |
return nil | |
} | |
return controllers[index + 1] | |
} | |
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { | |
if completed, | |
let visibleViewController = pageViewController.viewControllers?.first, | |
let index = controllers.firstIndex(of: visibleViewController) { | |
parent.currentIndex = index | |
} | |
} | |
} | |
} | |
struct PageControl: UIViewRepresentable { | |
var numberOfPages: Int | |
@Binding var currentPage: Int | |
func makeUIView(context: Context) -> UIPageControl { | |
let control = UIPageControl() | |
control.numberOfPages = numberOfPages | |
control.backgroundStyle = .prominent | |
control.addTarget(context.coordinator, action: #selector(Coordinator.updateCurrentPage(_:)), for: .valueChanged) | |
return control | |
} | |
func updateUIView(_ uiView: UIPageControl, context: Context) { | |
uiView.currentPage = currentPage | |
} | |
func makeCoordinator() -> Coordinator { | |
return Coordinator(parent: self) | |
} | |
class Coordinator: NSObject { | |
var parent: PageControl | |
init(parent: PageControl) { | |
self.parent = parent | |
} | |
@MainActor @objc func updateCurrentPage(_ sender: UIPageControl) { | |
self.parent.currentPage = sender.currentPage | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment