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
我不知道为什么会出现这个错误。有关原因或如何调试的任何线索吗?
UIPageController
的循环源。当源包含两页时,每一页是上一页,另一页是下一页。如果您滑动包含两页的
UIPageController
,然后尝试将源设置为 3 页,您将得到上面提到的断言,并保证假设
UIPageControllerDataSource
before/after 方法允许在 2 页的情况下循环转换。使用
UIPageController
和滚动过渡实现无崩溃的主要规则:1) 在调用
dataSource
方法之前设置
setViewControllers
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项目找到。
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
})
self.pageViewController
作为当前视图控制器的成员,而不是
pageViewController
作为在
didFinishAnimating
委托方法中获取的参数。
layoutIfNeeded() 进行滚动视图(uitableview、uicollectionview 等)。
for view in view.subviews {
if let subView = view as? UIScrollView {
subView.layoutIfNeeded()
}
}
setViewControllers([viewController],
direction: direction,
animated: false,
completion: { (finished) -> Void in })