追踪一个控制台警告 -- UITableViewAlertForLayoutOutsideViewHierarchy

5,654 阅读1分钟

[TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window.


UITableView没有被添加到 UIWindow的Hierarchy时调用了layoutIfNeeded, 会触发这个警告.

我的这次个例中的原因是在viewDidLoad时对tableView做了RxSwift相关的数据绑定, 而RxSwift的相关Api会在绑定时调用tableView的layoutIfNeeded, 所以触发了这个警告.

解决方法有两种:

1. 在tableView在UIWindow的管理下的时才做数据绑定, view会在viewController的以下几个生命周期被添加到UIWindow的管理:

extension UIViewController {
    func printIfViewIsOnWindow(when: String) {
        print("\(when) ==> self.view isOnWindow: \(self.view.isOnWindow)")
    }
}

extension UIView {
    func rootView() -> UIView {
        var view = self
        while view.superview != nil {
            view = view.superview!
        }
        return view
    }
    
    var isOnWindow: Bool {
        return self.rootView() is UIWindow
    }
}


view did load ==> self.view isOnWindow: false

view will appear ==> self.view isOnWindow: false

view did appear ==> self.view isOnWindow: true

view will disappear ==> self.view isOnWindow: true

view did disappear ==> self.view isOnWindow: false

view will appear ==> self.view isOnWindow: false

可以看到在viewDidAppear时view是受到window管理的, 所以在这个时机做数据绑定是OK的.


2. 修改/RxCocoa/Common/DelegateProxyType.swift中的

object.layoutIfNeeded()

if let tv = object as? UITableView {
    if tv.window != nil {
        object.layoutIfNeeded()
    }
} else {
    object.layoutIfNeeded()
}

这样tableView就不会在没有window的时候调用layoutIfNeeded了.