import UIKit
class InfinitePageView: UIPageViewController {
/// PageView의 currentIndex가 변경될때 마다 호출된다.
var pageIndexChanged: ((Int) -> ())?
/// PageView를 tap했을때 currentPage의 index를 반환한다.
var didSelectPageAt: ((Int) -> ())?
/// 보여주고자 하는 images.
var imageData = [UIImage]() {
didSet {setPageData()}
}
override init(transitionStyle style: UIPageViewControllerTransitionStyle, navigationOrientation: UIPageViewControllerNavigationOrientation, options: [String : Any]? = nil) {
super.init(transitionStyle: style, navigationOrientation: navigationOrientation, options: options)
delegate = self
dataSource = self
}
required init?(coder: NSCoder) {
super.init(coder: coder)
delegate = self
dataSource = self
}
private var pageData = [UIViewController]()
private func setPageData() {
pageData = imageData.enumerated().map { (index, image) -> UIViewController in
let viewController = UIViewController()
viewController.view.backgroundColor = .clear
viewController.view.tag = index
let imageView = UIImageView(frame: viewController.view.bounds)
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.backgroundColor = .clear
imageView.image = image
viewController.view.addSubview(imageView)
return viewController
}
setViewControllers([pageData[0]], direction: .forward, animated: false)
}
private var candidateIndex = 0
private var currentIndex = 0 {
didSet {pageIndexChanged?(self.currentIndex)}
}
/// didSelectPageAt의 사용여부. (기본값: true)
func isSelectable(_ value: Bool = true) {
if value {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didSelected))
view.addGestureRecognizer(tapGesture)
} else {
view.gestureRecognizers?.removeAll()
}
}
@objc private func didSelected() {
didSelectPageAt?(currentIndex)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in view.subviews {
if view is UIScrollView { // PageView 크기를 하단 Indicator에 겹치도록
view.frame = self.view.bounds
} else if view is UIPageControl { // Indicator 색상변경
(view as! UIPageControl).pageIndicatorTintColor = .clear
(view as! UIPageControl).currentPageIndicatorTintColor = .clear
self.view.bringSubview(toFront: view)
}
}
}
}
extension InfinitePageView: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed {currentIndex = candidateIndex}
}
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
candidateIndex = pendingViewControllers[0].view.tag
}
}
extension InfinitePageView: UIPageViewControllerDataSource {
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
return 0
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return pageData.count
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard pageData.count > 1 else {return nil}
if viewController.view.tag > 0 {
return pageData[viewController.view.tag - 1]
}
return pageData.last
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard pageData.count > 1 else {return nil}
if viewController.view.tag < pageData.count - 1 {
return pageData[viewController.view.tag + 1]
}
return pageData.first
}
}