Fast scroll UIPageViewController prevents viewcontroller from updating

好久不见. 提交于 2019-12-04 14:07:24

I've encountered exactly the same problem. It essentially boils down to UIPageViewController (_UIQueuingScrollView to be exact) not updating its view hierarchy correctly.

I noticed UIPageViewController is clever enough not to add/remove the content view if the page remains the same (even if it's in wrong place in the view hierarchy it somehow manages to cope with it so it looks good to the user). That's why I added a PageTrackingView to observe the content view being added (or not) to UIPageViewController view hierarchy. By tracking if the change happens I can calculate the current index by flipping it. So currently the workaround is good enough for 2 pages only.

class PageTrackingView: UIView {

    var pageIndexable: PageIndexable?

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if var pageIndexable = pageIndexable {
            let newIndex = (pageIndexable.internalIndex == 0) ? 1 : 0
            pageIndexable.internalIndex = newIndex
        }
        //Reset pageIndexable so that the index flip is fired only once
        pageIndexable = nil
    }
}

protocol PageIndexable {
    var internalIndex: Int { get set }
}

class PageViewController: UIPageViewController,
    UIPageViewControllerDataSource,
    UIPageViewControllerDelegate,
    PageIndexable {

    private var supportedViewControllers = [UIViewController]()

    internal var internalIndex: Int = 0 {
    didSet {
        if supportedViewControllers.indices.contains(internalIndex) {
            //Do something with the actual internalIndex value
        } else {
            assertionFailure()
        }
    }

    init(transitionStyle style: UIPageViewControllerTransitionStyle = .scroll,
        navigationOrientation: UIPageViewControllerNavigationOrientation = .horizontal,
        options: [String: Any]? = nil,
        viewControllers: [T]) {
        supportedViewControllers = viewControllers

        if !supportedViewControllers.isEmpty {
            let viewController = supportedViewControllers[0]
            let trackingView = PageTrackingView(frame: viewController.view.frame)
            viewController.view.frame = viewController.view.bounds
            trackingView.addSubview(viewController.view)
            viewController.view = trackingView
        }
        super.init(transitionStyle: style,
            navigationOrientation: navigationOrientation,
            options: options)
    }

    func pageViewController(_ pageViewController: UIPageViewController,
        didFinishAnimating finished: Bool,
        previousViewControllers: [UIViewController],
        transitionCompleted completed: Bool) {

        if !supportedViewControllers.isEmpty, let trackingView = supportedViewControllers[0].view as? PageTrackingView {
            trackingView.pageIndexable = self
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
        datasource = self
    }
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {

    guard let index = arrayVCs.index(of: viewController) else {
        return nil
    }

    if index == 0 {
        return nil
    }

    let prevIndex = abs((index - 1) % arrayVCs.count)
    return arrayVCs[prevIndex]
}

func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {

    guard let index = arrayVCs.index(of: viewController) else {
        return nil
    }

    if index == arrayVCs.count - 1 {
        return nil
    }

    let nextIndex = abs((index + 1) % arrayVCs.count)
    return arrayVCs[nextIndex]

}

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {

        if let viewController = pageViewController.viewControllers?[0] {
            guard let index = arrayVCs.index(of: viewController) else {
                return
            }
            self.segment.selectedSegmentIndex = index
        }
    }

Try this. It's work for me.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!