如果在此期间动画开始,则会发生UIPageViewController转换错误

问题描述:

我在使用UIPageViewController的应用程序中出现奇怪的行为。 我的应用程序的布局是一个PageViewController(相机胶卷),底部有一个广告横幅。如果在此期间动画开始,则会发生UIPageViewController转换错误

横幅的容器启动时隐藏起来,当广告加载时,我用动画设置isHidden=false

我的问题是,当旗帜进入它打破了UIPageViewController过渡画面,如果在进步在这个视频中显示:

enter image description here

我做了再现错误很容易与新项目几行,你可以在GITHUB结账:你只需要发送垃圾邮件的“下一步”按钮,直到横幅加载。它也可以通过翻转PageViewController来重现,但很难重现。

完整的示例代码:

class TestViewController: UIViewController,UIPageViewControllerDelegate,UIPageViewControllerDataSource { 
    @IBOutlet weak var constraintAdviewHeight: NSLayoutConstraint! 
    weak var pageViewController : UIPageViewController? 

    @IBOutlet weak var containerAdView: UIView! 
    @IBOutlet weak var adView: UIView! 
    @IBOutlet weak var containerPager: UIView! 

    var currentIndex = 0; 
    var clickEnabled = true 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) 
     pageViewController = pageVC 
     pageVC.delegate = self 
     pageVC.dataSource = self 
     addChildViewController(pageVC) 
     pageVC.didMove(toParentViewController: self) 
     containerPager.addSubview(pageVC.view) 
     pageVC.view.translatesAutoresizingMaskIntoConstraints = true 
     pageVC.view.frame = containerPager.bounds 
     pushViewControllerForCurrentIndex() 
    } 
    override func viewDidAppear(_ animated: Bool) { 
     super.viewDidAppear(animated) 
     self.containerAdView.isHidden = true 

     DispatchQueue.main.asyncAfter(deadline: .now()+4) { 
      self.simulateBannerLoad() 
     } 

    } 

    @IBAction func buttonClicked(_ sender: Any) { 
     guard clickEnabled else {return} 
     currentIndex -= 1; 
     pushViewControllerForCurrentIndex() 

    } 

    @IBAction func button2Clicked(_ sender: Any) { 
     guard clickEnabled else {return} 
     currentIndex += 1; 
     pushViewControllerForCurrentIndex() 
    } 


    private func simulateBannerLoad(){ 
     constraintAdviewHeight.constant = 50 
     pageViewController?.view.setNeedsLayout() 
     UIView.animate(withDuration: 0.3, 
         delay: 0, options: .allowUserInteraction, animations: { 
         self.containerAdView.isHidden = false 
         self.view.layoutIfNeeded() 
         self.pageViewController?.view.layoutIfNeeded() 
     }) 

    } 

    //MARK: data source 

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { 
     return getViewControllerForIndex(currentIndex+1) 
    } 

    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { 
     return getViewControllerForIndex(currentIndex-1) 
    } 

    func getViewControllerForIndex(_ index:Int) -> UIViewController? { 
     guard (index>=0) else {return nil} 
     let vc :UIViewController = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: "pageTest") 
     vc.view.backgroundColor = (index % 2 == 0) ? .red : .green 
     return vc 
    } 

    func pushViewControllerForCurrentIndex() { 
     guard let vc = getViewControllerForIndex(currentIndex) else {return} 
     print("settingViewControllers start") 
     clickEnabled = false 
     pageViewController?.setViewControllers([vc], direction: .forward, animated: true, completion: { finished in 
      print("setViewControllers finished") 
      self.clickEnabled = true 
     }) 
    } 
} 

注:另一个不必要的影响是最后完成的块当错误发生时不会被调用,所以给人们留下的按钮禁用:

func pushViewControllerForCurrentIndex() { 
     guard let vc = getViewControllerForIndex(currentIndex) else {return} 
     print("settingViewControllers start") 
     clickEnabled = false 
     pageViewController?.setViewControllers([vc], direction: .forward, animated: true, completion: { finished in 
      print("setViewControllers finished") 
      self.clickEnabled = true 
     }) 
    } 

注2:横幅加载事件是我无法手动控制的。由于用于展示广告的库在主线程中可以随时进行回调。在示例项目中,这是模拟DispatchQueue.main.asyncAfter:请致电

我该如何解决这个问题?谢谢

+0

我认为它可能是一个线程问题。尝试主线程上的主线程动画 – maku

+0

我在主线程中做动画(Thread.isMainThread == true) – Addev

什么问题?

布局会中断动画。

如何预防?

pageViewController动画时不改变布局。

当加载广告: 确认pageViewController是否动画,如果是的话,等到动画完成,然后更新,或更新

样品:

private func simulateBannerLoad(){ 
     if clickEnabled { 
      self.constraintAdviewHeight.constant = 50 
     } else { 
      needUpdateConstraint = true 
     } 
    } 
    var needUpdateConstraint = false 
    var clickEnabled = true { 
     didSet { 
      if clickEnabled && needUpdateConstraint { 
       self.constraintAdviewHeight.constant = 50 
       needUpdateConstraint = false 
      } 
     } 
    }