【问题标题】:Fast scroll UIPageViewController prevents viewcontroller from updating快速滚动 UIPageViewController 阻止 viewcontroller 更新
【发布时间】:2018-08-23 17:15:49
【问题描述】:

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

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

}

【问题讨论】:

    标签: ios swift uipageviewcontroller


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:
      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
              }
          }
      

      试试这个。这对我有用。

      【讨论】:

      • 这并不能真正解决我的问题。如果滑动太快,仍然没有设置正确的视图控制器的索引。
      • @Recusiwe 回答已编辑。请试试这个。这是我的工作。
      • 如果你有更多的控制器作为页面,它是否也可以工作?
      • 我应该在哪里设置委托,就像我的帖子中一样?
      • @Recuwise 只需将您的三个方法替换为方法,并且仅更正数据源其余部分都是正确的
      猜你喜欢
      • 1970-01-01
      • 2019-11-21
      • 2019-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多