Disable UIPageViewController bounce

前端 未结 13 2385
离开以前
离开以前 2020-11-27 04:08

Searched a lot for this one, but couldn\'t find a proper solution yet.

Is it possible to disable the bounce effect of a UIPageViewController and still

13条回答
  •  渐次进展
    2020-11-27 04:43

    Edited answer of Dong Ma, where:

    • added - respects layout direction (Hebrew for example)
    • fixed - wrong counting currentIndex when swipes very quick

    Info:

    • Written in Swift 5.0
    • Builded and tested in Xcode 10.2.1
    • iOS 12.0

    How to:

    1. Let's assume we have a UIViewController where UIPageViewController is added as child VC.
    class ViewController: UIViewController {
        var pageNavigationController: UIPageViewController! 
    
        private var lastPosition: CGFloat
        private var nextIndex: Int
        var currentIndex: Int     
    
        // rest of UI's setups  
    }
    
    1. Set ViewController as delegate of UIPageViewController:
    extension ViewController: UIPageViewControllerDataSource {
    
        func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
            guard
                let currentVisibleViewController = pageViewController.viewControllers?.first,
                let nextIndex = pageViewControllers.firstIndex(of: currentVisibleViewController)
            else {
                return
            }
    
            self.nextIndex = nextIndex
        }
    
        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if completed, let currentVisibleViewController = pageViewController.viewControllers?.first, let newIndex = pageViewControllers.firstIndex(of: currentVisibleViewController) {
                self.currentIndex = newIndex
            }
    
            self.nextIndex = self.currentIndex
        }
    }
    
    1. Set ViewController as datasource of UIPageController:
    extension ViewController: UIPageViewControllerDataSource {
    
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            // provide next VC
        }
    
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
            // provide prev VC
        }
    
        // IMPORTANT: that's the key why it works, don't forget to add it
        func presentationIndex(for pageViewController: UIPageViewController) -> Int {
            return currentIndex
        }
    }
    
    1. "Disable" bouncing by setting ViewController as delegate of UIPageViewController's UIScrollView:
    // MARK: - UIScrollViewDelegate (disable bouncing for UIPageViewController)
    extension BasePaginationVC: UIScrollViewDelegate {
    
        func attachScrollViewDelegate() {
            for subview in pageNavigationController.view.subviews {
                if let scrollView = subview as? UIScrollView {
                    scrollView.delegate = self
                    lastPosition = scrollView.contentOffset.x
                    break
                }
            }
        }
    
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            switch UIView.userInterfaceLayoutDirection(for: view.semanticContentAttribute) {
            case .leftToRight:
                if nextIndex > currentIndex {
                    if scrollView.contentOffset.x < (lastPosition - (0.9 * scrollView.bounds.size.width)) {
                        currentIndex = nextIndex
                    }
                } else {
                    if scrollView.contentOffset.x > (lastPosition + (0.9 * scrollView.bounds.size.width)) {
                        currentIndex = nextIndex
                    }
                }
    
                if currentIndex == 0 && scrollView.contentOffset.x < scrollView.bounds.size.width {
                    scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
                } else if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x > scrollView.bounds.size.width {
                    scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
                }
            case .rightToLeft:
                if nextIndex > currentIndex {
                    if scrollView.contentOffset.x > (lastPosition + (0.9 * scrollView.bounds.size.width)) {
                        currentIndex = nextIndex
                    }
                } else {
                    if scrollView.contentOffset.x < (lastPosition - (0.9 * scrollView.bounds.size.width)) {
                        currentIndex = nextIndex
                    }
                }
    
                if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x < scrollView.bounds.size.width {
                    scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
                } else if currentIndex == 0 && scrollView.contentOffset.x > scrollView.bounds.size.width {
                    scrollView.contentOffset = CGPoint(x: scrollView.bounds.size.width, y: 0)
                }
            @unknown default:
                fatalError("unknown default")
            }
    
            lastPosition = scrollView.contentOffset.x
        }
    
        func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
            switch UIView.userInterfaceLayoutDirection(for: view.semanticContentAttribute) {
            case .leftToRight:
                if currentIndex == 0 && scrollView.contentOffset.x <= scrollView.bounds.size.width {
                    targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
                } else if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x >= scrollView.bounds.size.width {
                    targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
                }
            case .rightToLeft:
                if currentIndex == pageViewControllers.count - 1 && scrollView.contentOffset.x <= scrollView.bounds.size.width {
                    targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
                } else if currentIndex == 0 && scrollView.contentOffset.x >= scrollView.bounds.size.width {
                    targetContentOffset.pointee = CGPoint(x: scrollView.bounds.size.width, y: 0)
                }
            @unknown default:
                fatalError("unknown default")
            }
        }
    }
    

提交回复
热议问题