UIPageViewController中的断言失败

20

我在UIPageViewController中遇到了断言失败。

Assertion failure in -[UIPageViewController _flushViewController:animated:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.60.12/UIPageViewController.m
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:
'Don't know about flushed view <UIView: 0x15a5bff30; frame = (0 0; 768 903); autoresize = W+H; layer = <CALayer: 0x15a5bfc30>>'
*** First throw call stack:
(0x181ebedb0 0x181523f80 0x181ebec80 0x182844154 0x1877a1c40 0x1877a1da8 0x18784e9c4 0x18784ebfc 0x187852318 0x18784dd98 0x1870101e4 0x1849a2994 0x18499d5d0 0x1870270a4 0x10028b620 0x100348b78 0x100379f54 0x100168878 0x18733d568 0x1870330b4 0x1870f1a00 0x18733e71c 0x1870f832c 0x18703536c 0x18700f7ac 0x18700ed40 0x18700eba8 0x1873283b4 0x18700d5e8 0x18784ebd4 0x187852318 0x18784df3c 0x1871db550 0x1871daf6c 0x101c9b768 0x1849f0234 0x1849f00e8 0x182135e54 0x181e5d030 0x181e757d4 0x181e74f0c 0x181e72c64 0x181d9cc50 0x183684088 0x18707e088 0x10033b200 0x18193a8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException

我不知道为什么会出现这个错误。你有任何线索可以指导我如何找到问题或者进行调试吗?


1
什么是 Flush View Controller? - Saurabh Jain
我不了解 flushViewController... 我没有使用过 flushViewController.. 我认为 UIPageViewController 在内部使用 flushViewController 方法。 - appchemist
你的 <UIView: 0x15a5bff30; frame = (0 0; 768 903); ...> 是什么类型的视图,你在做什么操作? - Lepidopteron
我遇到了类似的问题,这个问题解决了吗? - kformeck
@kformeck 似乎不是。已创建赏金。 - Chris
你的问题中缺乏足够的上下文信息,无法提供详细准确的答案。我们需要了解你如何实现分页控制器以及你有哪些子视图。 - Scriptable
6个回答

14

遇到此断言的直接方法是使用循环源为使用滚动转换样式定义的UIPageController

当源包含两个页面时,每个页面都是另一个页面的前一个和下一个。如果您滑动包含两个页面的UIPageController,然后尝试设置包含3个页面的源,则会得到上面提到的断言,并假定UIPageControllerDataSource在2页情况下允许循环过渡之前/之后方法。

使用滚动转换的无崩溃规则:

1)在调用setViewControllers方法之前设置dataSource

2)使用没有动画的setViewControllers方法(animated:false

3)对于单页模式,将dataSource设置为nil

4)不要允许2页模式的循环

所有这些建议结合起来,使UIPageController绝对稳定。

import UIKit

/// Convenient subclass of UIPageViewController
@objc class AMPageViewController: UIPageViewController {

    /// Turn on/off PageControl at the bottom
    @objc var showPageControl: Bool = true

    /// Array of all viewControllers
    @objc var source: [UIViewController]? {

        didSet {
            let count = source?.count ?? 0
            if count > 0 {
                dataSource = count > 1 ? self : nil
            }
            else {
                dataSource = nil
                delegate = nil
            }
        }
    }

    /// Index of the current viewController from source
    @objc var pageIndex: Int {

        get {
            var currentPageIndex: Int = 0
            if let vc = viewControllers?.first, let source = source, let pageIndex = source.index(of: vc) {
                currentPageIndex = pageIndex
            }

            return currentPageIndex
        }

        set {
            guard newValue >= 0, let source = source, newValue < source.count else { return }

            let vc = source[newValue]
            let direction: UIPageViewControllerNavigationDirection = newValue < pageIndex ? .reverse : .forward

            setViewController(vc, direction: direction)
        }
    }

    override weak var delegate: UIPageViewControllerDelegate? {

        get { return super.delegate }

        set {
            if source?.count ?? 0 > 0 {
                super.delegate = newValue
            }
            else {
                super.delegate = nil
            }
        }
    }

    /// Initializer in scroll-mode with interPageSpacing
    @objc init(navigationOrientation: UIPageViewControllerNavigationOrientation = .horizontal, interPageSpacing: Int = 0) {

        let options = (interPageSpacing > 0) ? [UIPageViewControllerOptionInterPageSpacingKey : 5] : nil

         super.init(transitionStyle: .scroll, navigationOrientation: navigationOrientation, options: options)
     }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    /// Set viewcontroller by index from source
    @objc func setPageIndex(_ index: Int, completion: ((Bool) -> Void)? = nil) {

        guard index > 0, let source = source, index < source.count else { return }

        let vc = source[index]
        let direction: UIPageViewControllerNavigationDirection = index < pageIndex ? .reverse : .forward

        setViewController(vc, direction: direction, completion: completion)
    }


    private func setViewController(_ viewController: UIViewController, direction: UIPageViewControllerNavigationDirection = .forward, completion: ((Bool) -> Void)? = nil) {

        super.setViewControllers([viewController], direction: direction, animated: false, completion: completion)
    }
}

extension FFPageViewController: UIPageViewControllerDataSource {

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

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

        let count = source.count

        if count == 2, index == 0 {
            return nil
        }

        let prevIndex = (index - 1) < 0 ? count - 1 : index - 1

        let pageContentViewController: UIViewController = source[prevIndex]

        return pageContentViewController
    }


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

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

        let count = source.count

        if count == 2, index == 1 {
            return nil
        }

        let nextIndex = (index + 1) >= count ? 0 : index + 1

        let pageContentViewController = source[nextIndex]

        return pageContentViewController
    }


    func presentationCount(for pageViewController: UIPageViewController) -> Int {

        return showPageControl ? (source?.count ?? 0) : 0
    }


    func presentationIndex(for pageViewController: UIPageViewController) -> Int {

        guard showPageControl else { return 0 }

        return pageIndex
    }
}

您可以在GitHub项目中找到完整的实现和使用示例。


5
如果你想要动画,该怎么办? - Alexandre G

3

UIPageViewController进行转场时,其中的ViewController(例如:UITableViewController)转场会导致崩溃。

在我的情况下(崩溃):

步骤1

self.pageViewController.setViewControllers([self.tableViewController2], direction: .forward, animated: true, completion: nil)

步骤2

UIPageViewController过渡时滚动tableView。

我的解决方案

(禁用目标视图控制器和当前视图控制器的滚动)

self.tableViewController1.tableView.isScrollEnabled = false
self.tableViewController2.tableView.isScrollEnabled = false
self.pageViewController.setViewControllers([self.tableViewController2], direction: .forward, animated: true, completion: { _ in
    self.tableViewController1.tableView.isScrollEnabled = true
    self.tableViewController2.tableView.isScrollEnabled = true
})

2

如果您正在编写代码,请将pageViewController.setViewControllers函数调用移至DispatchQueue.main.async块中。

我不知道为什么它有效,但对我有效。 供参考。


1
当我在子控制器中有文本字段并且在滚动到下一个控制器时没有关闭键盘,这也发生在我身上。如果是这种情况,请在您以编程方式更改控制器的操作中添加endEditing,或者如果您正在scrollViewDidScroll页面视图控制器的委托方法中滚动,则添加endEditing。

1
当您将UIPageViewControllerTransitionStyle设置为scroll而不是pageCurl时,会发生这种情况。
如果您动态创建视图控制器并在UIPageViewController上设置它们,则必须确保第二次调用setViewControllers是在第一次完成动画后调用的,因为UIKit存在一个错误。尽管这不是一种好的做法,但延迟分派是一种快速而脏的修复方法。
更多细节请参见此处。

https://forums.developer.apple.com/thread/6554


1
对我来说,问题是将self.pageViewController作为当前视图控制器的成员使用,而不是在didFinishAnimating委托方法中获取的参数pageViewController

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接