快速滚动 UIPageViewController 阻止视图控制器更新 [英] Fast scroll UIPageViewController prevents viewcontroller from updating

查看:29
本文介绍了快速滚动 UIPageViewController 阻止视图控制器更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 UIPageviewcontroller,里面有两个控制器.当您滑动到下一个时,我使用 viewController 参数来设置适当的委托.但我的经验是,如果您滑动得太快,函数 viewControllerAfter 将不会正确更新 viewController.最初的滑动应该将视图控制器的索引从 0 更新为 1,但如果滑动太快则不会这样做.

I have a UIPageviewcontroller which got two controllers inside. As you swipe to the next, I use the viewController argument to set the appropriate delegate. But I experience that if you swipe too fast, the function viewControllerAfter isn't updating the viewController correctly. The initially swipe should update the index of the viewcontroller from 0 to 1, but doesn't do so if you swipe too fast.

import UIKit

class WizardPageViewController: UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    lazy var orderedViewControllers: [UIViewController] = {
        return [self.newVc(viewController: "intro"),
                self.newVc(viewController: "welcome")]
    }()

    var pageControl = UIPageControl()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.dataSource = self
        self.delegate = self
        configurePageControl()

        // This sets up the first view that will show up on our page control
        if let firstViewController = orderedViewControllers.first {
            setViewControllers([firstViewController],
                               direction: .forward,
                               animated: true,
                               completion: nil)
        }
    }

    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        let pageContentViewController = pageViewController.viewControllers![0]
        self.pageControl.currentPage = orderedViewControllers.index(of: pageContentViewController)!
    }

    func newVc(viewController: String) -> UIViewController {
        return UIStoryboard(name: "Wizard", bundle: nil).instantiateViewController(withIdentifier: viewController)
    }

    func configurePageControl() {
        // The total number of pages that are available is based on how many available colors we have.
        pageControl = UIPageControl(frame: CGRect(x: 0,y: UIScreen.main.bounds.maxY - 225,width: UIScreen.main.bounds.width,height: 50))
        self.pageControl.numberOfPages = orderedViewControllers.count
        self.pageControl.currentPage = 0
        pageControl.isEnabled = false
        //self.pageControl.tintColor = UIColor.black
        self.pageControl.pageIndicatorTintColor = UIColor.gray
        self.pageControl.currentPageIndicatorTintColor = UIColor(red:0.647, green:0.192, blue:0.216, alpha:1.00)
        self.view.addSubview(pageControl)
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }

        print(orderedViewControllers.index(of: viewController))
        let previousIndex = viewControllerIndex - 1

        // User is on the first view controller and swiped left to loop to
        // the last view controller.
        guard previousIndex >= 0 else {
            return nil
            // Uncommment the line below, remove the line above if you don't want the page control to loop.
            // return nil
        }

        guard orderedViewControllers.count > previousIndex else {
            return nil
        }

        return orderedViewControllers[previousIndex]
    }

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let viewControllerIndex = orderedViewControllers.index(of: viewController) else {
            return nil
        }

        print(orderedViewControllers.index(of: viewController)) // Returns 0 is I swipe too fast, otherwise 1
        if let vc = orderedViewControllers[viewControllerIndex] as? WelcomeViewController {
            vc.delegate = self
        }

        let nextIndex = viewControllerIndex + 1
        let orderedViewControllersCount = orderedViewControllers.count

        // User is on the last view controller and swiped right to loop to
        // the first view controller.
        guard orderedViewControllersCount != nextIndex else {
            return nil
            // Uncommment the line below, remove the line above if you don't want the page control to loop.
            // return nil
        }

        guard orderedViewControllersCount > nextIndex else {
            return nil
        }

        return orderedViewControllers[nextIndex]
    }

}

推荐答案

我遇到了完全相同的问题.它本质上归结为 UIPageViewController(准确地说是 _UIQueuingScrollView)没有正确更新其视图层次结构.

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

我注意到 UIPageViewController 足够聪明,不会在页面保持不变的情况下添加/删除内容视图(即使它在视图层次结构中处于错误的位置,它以某种方式设法应对它,因此它对用户来说看起来不错).这就是为什么我添加了一个 PageTrackingView 来观察添加(或不添加)到 UIPageViewController 视图层次结构的内容视图.通过跟踪是否发生更改,我可以通过翻转它来计算当前索引.因此,目前该解决方法仅适用于 2 个页面.

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
    }
}

这篇关于快速滚动 UIPageViewController 阻止视图控制器更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆