iOS UIPageViewController失败并且不显示ViewControllers

时间:2018-02-19 03:22:28

标签: ios swift uipageviewcontroller

我们设置了左右按钮,方便用户快速浏览不同的汽车。如果用户快速点击下一页10次或更多次,我们的页面视图控制器将丢失视图控制器。

这是车辆正确显示的车辆页面(模糊以隐藏不相关的信息)。见图:

Shows Vehicle Correctly

如果滚动动画打开(true),则在快速点击右箭头6次或更多次后会丢失车辆页面。见图:

enter image description here

代码:

private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
    let viewControllers = [viewController]
    let isAnimated = true // false always works. However, animation is required.
    setViewControllers(viewControllers, direction: direction, animated: isAnimated, completion: nil)
}

在调试时以及当页面视图控制器停止显示汽车时,我确保正在设置的视图控制器不是零并且列表(汽车)也是非零。

我尝试了UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source?的解决方案的变体,其中使用了完成块。但是,它没有用。

weak var pvcw: UIPageViewController? = self
setViewControllers(viewControllers, direction: direction, animated: true, completion: {(_ finished: Bool) -> Void in
    let pvcs: UIPageViewController? = pvcw
    if pvcs == nil {
        return
    }
    DispatchQueue.main.async(execute: {() -> Void in
        pvcs?.setViewControllers(viewControllers, direction: direction, animated: false) {(_ finished: Bool) -> Void in }
    })
})

有什么想法吗?谢谢。

更新

我注意到有时候所包含的View Controller可以偏离中心而不是完全缺失。

View Controller Off Centered

我更深入地了解了视图控制器完全丢失的情况。单击“调试视图层次结构”并打开“Show Clipped Content”时,视图控制器完全丢失时显示以下内容:

Turning on Show Clipped Content

Clipped Content

因此,似乎缺少的内容被剪裁/超出范围。

仅显示线框显示以下内容:

wire frames only

页面视图控制器有一个

  • _UIPageViewControllerContentView包含
  • 包含
  • 的_UIQueuingScrollView
  • 包含
  • 的UIView
  • VehicleDetailTableViewController(带有汽车图像和细节的UITableViewController)。

我还看到,当事情很奇怪时,_UIQueuingScrollView的界限是完全不同的。当一切正常时,边界的x为1125,而X为375。

仅当使用滚动的过渡样式而不是页面卷曲时才会发生这种情况。使用Page Curl时,一切正常。

我们如何预防/解决此问题?

第二次更新

此代码使问题消失。然而,它留下了更刺耳的经历。也许由于延迟0.4秒,蓝色背景有时会显示正常使用。

private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {

    let viewControllers = [viewController]
    setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
            self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
        })
    })
}

这不是一个好的用户体验。 有更好的方法吗?

我希望滚动过渡平滑,不要简单地显示蓝色背景,也不要丢失其内容,即View Controller内容。

3 个答案:

答案 0 :(得分:2)

虽然真正的答案是让视觉控制器尽可能简单(但不是更简单),但这里是修复问题的代码,当用户导航到下一个视图时,有时会显示背景的副作用控制器。

private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {

    let viewControllers = [viewController]
    setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
            self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
        })
    })
}

答案 1 :(得分:1)

一个简单的解决方案是通过添加一个小的"提前点击来解除按钮水龙头与视图控制器的变化。缓冲。创建一个按钮队列(使用一个简单的NSMutableArray充当FIFO队列),在其中添加每个导航按钮,然后在添加之前调用队列为空的出队函数

在dequeue函数中,删除第一个条目并相应地更改视图,然后在setViewControllers完成处理程序中再次调用自身,如果队列不为空。

确保仅在主线程上进行处理以避免线程问题。如果您愿意,您还可以添加限制数量"提前点击"你允许,也许可以在方向性变化上刷新队列。

答案 2 :(得分:1)

您好我已经创建了Sample Project,这可以解决您的问题。我添加了100个ViewControllers(通过循环),它可以正常使用滚动动画。事情就在他们的位置。

我在这个项目中所做的是:

  1. 为具有两个属性的页面创建了一个BaseClass

    a)Int

    类型的pageIndex

    b)委托回调协议

  2. 通过ContainerView

  3. 将UIPageViewController添加到ViewController
  4. 创建了一个名为Page的ViewController,它扩展了PageViewBase

  5. 运行循环计数100并将数据添加到数组并设置数据源并委托给self(PageControlelr)并根据pageIndex属性进行管理

  6. PageViewController

    class PageViewController: UIPageViewController {
        var list = [Page]()
        var sb: UIStoryboard?
        var viewController: ViewController! // settting from ViewController
        override func viewDidLoad() {
            super.viewDidLoad()
            sb = UIStoryboard(name: "Main", bundle: nil)
            DispatchQueue.main.asyncAfter(deadline: .now()+0.4, execute: {
                self.setupList()
            })
        }
        func setupList(){
            for i in 0..<100{
                let model = PageModel(title: "Title \(i + 1)", subTitle: "SubTitle \(i + 1)")
                let page = sb?.instantiateViewController(withIdentifier: "PageID") as! Page
                page.data = model
                page.pageIndex = i
                page.delegate = viewController
                list.append(page)
            }
            self.delegate = self
            self.dataSource = self
            setViewControllers([list[0]], direction: .forward, animated: true, completion: nil)
            self.updateCurrentPageLabel(index: 0)
        }
        func movePage(index: Int){
            let currentIndex = self.viewControllers![0] as! Page
            self.updateCurrentPageLabel(index: index)
            setViewControllers([list[index]], direction: index > currentIndex.pageIndex ? .forward : .reverse, animated: true)
        }
        func getCurrentPageIndex() -> Int{
            return (self.viewControllers![0] as! Page).pageIndex
        }
        func updateCurrentPageLabel(index: Int){
            (self.parent as? ViewController)?.currentListingLabel.text = "\(index + 1) of \(list.count)"
        }
    }
    extension PageViewController: UIPageViewControllerDelegate{
        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            let currentIndex = (self.viewControllers![0] as! Page).pageIndex
            self.updateCurrentPageLabel(index: currentIndex)
    
        }
    }
    extension PageViewController: UIPageViewControllerDataSource{
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            let index = (viewController as! Page).pageIndex
            if index > 0 {
                return list[index-1]
            }
            return nil
        }
    
        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
            let index = (viewController as! Page).pageIndex
            if index < list.count-1 {
                return list[index+1]
            }
            return nil
        }
    
    }
    

    import UIKit
    
    struct PageModel {
        var title: String
        var subTitle: String
    }
    
    class Page: PageViewBase {
        @IBOutlet weak var titleLabel: UILabel!
        @IBOutlet weak var subTitleLabel: UILabel!
        @IBOutlet weak var imageView: UIImageView!
        @IBOutlet weak var btnWorking: UIButton!
        var data: PageModel?
        override func viewDidLoad() {
            super.viewDidLoad()
            setupTags()
            setupActions()
            setupData()
        }
        func setupData(){
            if let data = data{
                self.titleLabel.text = data.title
                self.subTitleLabel.text = data.subTitle
                imageView.image = #imageLiteral(resourceName: "car")
            }
    
        }
    
        enum buttonTags: Int{
            case working = 1
        }
        func setupTags(){
            btnWorking.tag = buttonTags.working.rawValue
        }
        func setupActions(){
            btnWorking.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
        }
        @objc func didSelect(_ sender: UIView){
            if let tag = buttonTags.init(rawValue: sender.tag){
                switch tag{
                case .working:
                    delegate?.didReceive(withMessage: "wokring button clicked of index \(pageIndex)")
                }
            }
        }
    }
    

    ViewController // MainController

    import UIKit
    protocol CallBack {
        func didReceive(withMessage message: String)
    }
    class ViewController: UIViewController {
        @IBOutlet weak var containerView: UIView!
        @IBOutlet weak var btnCall: UIButton!
        @IBOutlet weak var btnMessage: UIButton!
        @IBOutlet weak var btnNext: UIButton!
        @IBOutlet weak var btnBack: UIButton!
        @IBOutlet weak var currentListingLabel: UILabel!
        var pageController: PageViewController?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            setupTags()
            setupActions()
            setupContainerView()
        }
    
        enum buttonTags: Int{
            case call = 1
            case message
            case next
            case back
        }
        func setupTags(){
            btnCall.tag = buttonTags.call.rawValue
            btnMessage.tag = buttonTags.message.rawValue
            btnNext.tag = buttonTags.next.rawValue
            btnBack.tag = buttonTags.back.rawValue
        }
        func setupActions(){
            btnCall.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
            btnMessage.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
            btnNext.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
            btnBack.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
        }
        @objc func didSelect(_ sender: UIView){
            if let tag = buttonTags.init(rawValue: sender.tag){
                switch tag{
                case .call:
                    print("Call button called for index \(pageController?.getCurrentPageIndex() ?? 0)")
                case .message:
                    print("message button called for index  \(pageController?.getCurrentPageIndex() ?? 0)")
                case .next:
                    if let p = pageController{
                        let currentIndex = p.getCurrentPageIndex()
                        if currentIndex < p.list.count - 1{
                            p.movePage(index: currentIndex + 1)
                        }
                    }
                case .back:
                    if let p = pageController{
                        let currentIndex = p.getCurrentPageIndex()
                        if currentIndex > 0{
                            p.movePage(index: currentIndex - 1)
                        }
                    }
                }
            }
        }
        func setupContainerView(){
            let sb = UIStoryboard(name: "Main", bundle: nil)
            pageController = sb.instantiateViewController(withIdentifier: "PageViewControllerID") as? PageViewController
            pageController?.viewController = self
            addViewIntoParentViewController(vc: pageController)
        }
        func addViewIntoParentViewController(vc: UIViewController?){
            if let vc = vc{
                for v in self.containerView.subviews{
                    v.removeFromSuperview()
                }
                self.containerView.addSubview(vc.view)
                self.containerView.translatesAutoresizingMaskIntoConstraints = false
                vc.view.translatesAutoresizingMaskIntoConstraints = false
                addChildViewController(vc)
                NSLayoutConstraint.activate([
                    vc.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
                    vc.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
                    vc.view.topAnchor.constraint(equalTo: containerView.topAnchor),
                    vc.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
                    ])
                vc.didMove(toParentViewController: self)
            }
        }
    }
    extension ViewController: CallBack{
        func didReceive(withMessage message: String) {
            print("message: \(message)")
        }
    }
    

    PageViewBase

    import UIKit
    
    class PageViewBase: UIViewController {
        var pageIndex = -1
        var delegate: CallBack?
    }