스와이프 좌<->우 이동 및 버튼 좌<->우 이동 PageViewController 

import UIKit

protocol IPageViewControllerDelegate: class {
    ///스크롤 시작 포인트
    func pageViewWillBeginScrollDragging(offset: CGPoint)
    ///스크롤 진행 포인트 / 진행율 (0.0 ~ 1.0)
    func pageViewDidScroll(scrollView: UIScrollView, percent: CGFloat, direction: Int)
    ///페이지  이동 완료 인덱스
    func pageMoveDidFinishAnimating(currentPage index: Int)
    
}

class IPageViewController : UIPageViewController {

    weak var pageDelegate: IPageViewControllerDelegate?
    private var pageViews: Array<UIViewController>! ///페이지 뷰 컨트롤 배열
    private var startOffset = CGFloat(0)            ///스크롤뷰 사용
    private var chooseIdx = 0                       /// 선택한 페이지 Index
    private var currentIdx = 0                      /// 현재 페이지 Index

    override init(transitionStyle style: UIPageViewController.TransitionStyle, navigationOrientation: UIPageViewController.NavigationOrientation, options: [UIPageViewController.OptionsKey : Any]? = nil) {
        super.init(transitionStyle: style, navigationOrientation: navigationOrientation, options: options)
    }
    
    deinit {
        log(direction: .ETC, ofType: self, datas: "IPageViewController deinit")
        pageViews.removeAll()
        pageDelegate = nil
        
        removeFromParent()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    
    override func viewDidLoad() {
         super.viewDidLoad()
        
        self.delegate = self
        self.dataSource = self
                  
         for v in view.subviews{
            if v is UIScrollView {
                (v as! UIScrollView).delegate = self
                (v as! UIScrollView).bouncesZoom = false
                (v as! UIScrollView).isPagingEnabled = true
            }
        }
     }
    
    override func setViewControllers(_ viewControllers: [UIViewController]?, direction: UIPageViewController.NavigationDirection, animated: Bool, completion: ((Bool) -> Void)? = nil) {
        
        //초기화 이후 호출되는 경우에는 페이지 강제이동 처리
        if pageViews != nil {
            super.setViewControllers([viewControllers![0]], direction: direction, animated: animated, completion: completion)
            return
        }
        
        if viewControllers == nil { return }
        guard let views = viewControllers else { return }
        
        pageViews = views
        
        super.setViewControllers([pageViews[0]], direction: direction, animated: animated, completion: completion)
    }
    
    override func willMove(toParent parent: UIViewController?){
        super.willMove(toParent: parent)
    }
    
    override func didMove(toParent parent: UIViewController?){
        super.didMove(toParent: parent)
    }
    
    /// IPageViewController Child View Controller 클래스 호출
    func getViewControllerAtIndex(index: Int) -> UIViewController? {
        if index >= pageViews.count { return nil }
        
        let vc = pageViews![index]
        return vc
    }
    
    /// IPageViewController Parent 클래스 호출
    func getParentViewController() -> UIViewController? {
        guard let parentVC = self.parent else {
            log(direction: .ERROR, ofType: self, datas: "parent VC Load Error")
            return nil
        }
        return parentVC
    }
    
    /// 현재 페이지 인덱스
    func getCurrentPage() -> Int {
    
        return pageViews[self.currentIdx].view.tag
    }
    
    func getTotalPages() -> Int {
        return pageViews.count
    }
}

extension IPageViewController : UIScrollViewDelegate{
    /*
    - 제스쳐 페이지 이동시 처음 시작된 페이지 값 (제스쳐 시작시 한번 호출)
    - 강제로 페이지 이동시에는 호출안됨 (nextPageWithIndex 사용)
     */
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        startOffset = scrollView.contentOffset.x
        
        pageDelegate?.pageViewWillBeginScrollDragging(offset: scrollView.contentOffset)
    }
    
    
    ///제스쳐 이동시 터치 종료 후 호출
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

        if(currentIdx == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width){
            scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
        }else if(currentIdx == (pageViews.count - 1) && scrollView.contentOffset.x > scrollView.bounds.size.width) {
            scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
        }
    }

    /// 페이지 이동되는 경우
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
       
        
        // 버튼으로 강제 페이지뷰 이동시 startOffset = 0
        // 제스쳐 스크롤 이동중 이중터치시 startOffset 값 변경으로 UI 조작시 문제 발생됨
        if startOffset == 0 || startOffset != scrollView.frame.width { return }
        
        var direction = 0 //스크롤 멈춤

        if startOffset < scrollView.contentOffset.x {
            direction = 1 //오른쪽 페이지로 이동
        }else if startOffset > scrollView.contentOffset.x {
            direction = -1 //왼쪽 페이지로 이동
        }

        let positionFromStartOfCurrentPage = abs(startOffset - scrollView.contentOffset.x)
        // percent 범위 (0.0 ~ 1)
        let percent = positionFromStartOfCurrentPage /  self.view.frame.width

        pageDelegate?.pageViewDidScroll(scrollView: scrollView, percent: percent, direction: direction)
        
        // 끝 페이지 바운스 안되게 처리
        if (currentIdx == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width){
            scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
        }else if (currentIdx == (pageViews.count - 1) && scrollView.contentOffset.x > scrollView.bounds.size.width) {
            scrollView.contentOffset = CGPoint(x:scrollView.bounds.size.width, y:0.0)
        }
    }
}

extension IPageViewController: UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    /// 강제 페이지 변경
    func nextPageWithIndex(index: Int, animated: Bool = true) {
        
        print("nextPageWithIndex - index:\(index) chooseIdx:\(chooseIdx) currentIdx:\(currentIdx)")
        /* 초기화 - startOffset = 0
         - 강제로 페이지뷰 이동시(버튼사용 등..) 스크롤뷰 이벤트 발생되지만
           pageViewController: didFinishAnimating 이벤트 호출이 안되서 페이지 변경 완료를 알수 없음
         - 스크롤 이벤트 연동된 UI 있을경우 필히 사용
         */
        
        var direction: UIPageViewController.NavigationDirection = .forward
        if currentIdx > index {
            direction = .reverse
        } else {
            direction = .forward
        }
        
        startOffset = 0
        chooseIdx = index
        currentIdx = index
        
        setViewControllers([pageViews[index]], direction: direction, animated: animated, completion: nil)
        //self.viewControllerAtIndex(index: index)
    }
    
    // 현재 페이지 로드가 끝났을 때
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        if completed {

            if let currentViewController = pageViewController.viewControllers?.first {
                currentIdx = currentViewController.view.tag - 1001
                pageDelegate?.pageMoveDidFinishAnimating(currentPage: currentIdx)
            }
        }
    }
        
    /// 이전 페이지 뷰 리턴
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        
        if var index = pageViews.firstIndex(of: viewController) {
            if( index == 0 || index == NSNotFound) {
                return nil
            }
            
            index -= 1
            return self.getViewControllerAtIndex(index: index)
        }
        return nil
    }
    
    /// 다음 페이지 뷰 리턴
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

        if var index = pageViews.firstIndex(of: viewController) {

            if( index == NSNotFound) { return nil }
            index += 1
            
            if(index == self.pageViews.count){ return nil }
            return self.getViewControllerAtIndex(index: index)
        }
        
        return nil
    }
    
    // 인디케이터 개수
    /*
    func presentationCount(for pageViewController: UIPageViewController) -> Int {
        print("presentationCount - \(textArray.count)")
        
        return self.textArray.count
    }
    */
    
    // 인디케이터 초기 선택 값
    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
        print("chooseIdx - \(chooseIdx)")
        return chooseIdx
    }
}

 

[사용방법]

import UIKit

// MARK: IPageViewController Delegate
extension BasicPageViewController: IPageViewControllerDelegate {
    func pageViewWillBeginScrollDragging(offset: CGPoint) {
        print("pageViewWillBeginScrollDragging \(offset)")
    }
    
    func pageViewDidScroll(scrollView: UIScrollView, percent: CGFloat, direction: Int) {
        let page = scrollView.contentOffset.x / scrollView.bounds.width
        let progressInPage = scrollView.contentOffset.x - (page * scrollView.bounds.width)
        let progress = CGFloat(page) + progressInPage
        
        print("pageViewDidScroll \(page) / \(progressInPage) / \(progress)")
        
    }
    
    func pageMoveDidFinishAnimating(currentPage index: Int) {
        print("pageMoveDidFinishAnimating \(index)")
        pageControl.currentPage = index
        currentPageIndex = index
    }
}

extension BasicPageViewController: UIPageViewControllerDelegate {
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        let nextPage = Int(targetContentOffset.pointee.x / self.view.frame.width)
        
        self.pageControl.currentPage = nextPage
        
    }
}

class BasicPageViewController: UIViewController {
    
    var currentPageIndex = 0
    
    @IBOutlet weak var backButton: UIButton!
    @IBOutlet weak var nextButton: UIButton!
    @IBOutlet weak var pageControl: UIPageControl! {
        didSet {
            
        }
    }
    
    // Custom PageViewController 생성
    lazy var pageVC: IPageViewController! = { [weak self] in
        /* Storyboard
        let storyboard = UIStoryboard(storyboard: .Main)
        let pageVC: IPageViewController = storyboard.instantiateViewController()
        */
        // Code
        let pageVC = IPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
        
        return pageVC
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // PageView 설정
        setupPageView()
        
        // PageController 페이지 설정
        pageControl.numberOfPages = pageVC.getTotalPages()
        
        // 이전/다음 페이지 버튼 및 PageController UI 위에 표시
        self.view.bringSubviewToFront(backButton)
        self.view.bringSubviewToFront(nextButton)
        self.view.bringSubviewToFront(pageControl)
    }
    
    // PageViewController 설정
    func setupPageView() {
        pageVC.pageDelegate = self
        
        var pageViews: Array<UIViewController> = []
        
        let pageVC1 = UIViewController()
        let pageVC2 = UIViewController()
        let pageVC3 = UIViewController()
        let pageVC4 = UIViewController()
        let pageVC5 = UIViewController()
        
        pageVC1.view.tag = 1001
        pageVC2.view.tag = 1002
        pageVC3.view.tag = 1003
        pageVC4.view.tag = 1004
        pageVC5.view.tag = 1005
        
        pageVC1.view.backgroundColor = .bubblegum
        pageVC2.view.backgroundColor = .rosePink
        pageVC3.view.backgroundColor = .orange
        pageVC4.view.backgroundColor = .bubblegum
        pageVC5.view.backgroundColor = .greyish
        
        pageViews.append(pageVC1)
        pageViews.append(pageVC2)
        pageViews.append(pageVC3)
        pageViews.append(pageVC4)
        pageViews.append(pageVC5)
        
        self.pageVC.setViewControllers(pageViews, direction: .forward, animated: true, completion: nil)
        
        self.addChild(self.pageVC)
        self.view.addSubview(self.pageVC.view)
        
        // AutoLayout
        self.pageVC.view.translatesAutoresizingMaskIntoConstraints = false
        self.view.addConstraint(NSLayoutConstraint(item: self.pageVC.view as Any, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0))
        self.view.addConstraint(NSLayoutConstraint(item: self.pageVC.view as Any, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0))
        self.view.addConstraint(NSLayoutConstraint(item: self.pageVC.view as Any, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 0))
        self.view.addConstraint(NSLayoutConstraint(item: self.pageVC.view as Any, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0))
        
        self.pageVC.didMove(toParent: self)
        
    }
    
    // 이전 페이지 이동
    @IBAction func backButton(_ sender: UIButton) {
        let backPage = currentPageIndex - 1
        
        if backPage < 0  {
            return
        }
        
        pageVC.nextPageWithIndex(index: backPage, animated: true)
        pageControl.currentPage = backPage
        currentPageIndex = backPage
    }
    
    // 다음 페이지 이동
    @IBAction func nextButton(_ sender: UIButton) {
        let nextPage = currentPageIndex + 1
        
        if nextPage >= pageVC.getTotalPages()  {
            return
        }
        
        pageVC.nextPageWithIndex(index: nextPage, animated: true)
        pageControl.currentPage = nextPage
        currentPageIndex = nextPage
    }
}

+ Recent posts