在执行Segue时,iOS13出现一个奇怪的错误,我无法弄清这是什么意思,也找不到针对此错误的任何文档。问题在于,这似乎会导致很多延迟(几秒钟),直到执行segue。
2019-09-11 22:45:38.861982 + 0100 Thrive [2324:414597] [TableView]仅警告一次:通知UITableView布局其可见单元格 和其他内容,而不必属于视图层次结构(表视图 或其父视图之一尚未添加到窗口中)。这可能 通过强制表视图内的视图加载和执行而导致错误 没有准确信息的布局(例如表格视图范围,特征 收集,布局边距,安全区域插图等),并且还将 由于额外的布局传递而导致不必要的性能开销。 在以下位置建立符号断点 UITableViewAlertForLayoutOutsideViewHierarchy可以在 调试器,看看是什么原因导致这种情况的发生,因此可以避免这种情况 如果可能,请完全采取行动,或将其推迟到表格视图中 已添加到窗口。表格视图:层=; contentOffset:{0,0}; contentSize:{315,118}; AdjustedContentInset:{0,0,0,0}; dataSource:>
我正在使用Hero,但是我尝试禁用它并使用常规的Segue,但这并没有阻止这种滞后。
启动segue的代码是didSelectRowAt
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
selectedCell = realIndexFor(activeGoalAt: indexPath)
performSegue(withIdentifier: "toGoalDetails", sender: nil)
} else if indexPath.section == 1 {
selectedCell = indexPath.row
performSegue(withIdentifier: "toIdeaDetails", sender: nil)
} else {
selectedDecision = indexPath.row
hero(destination: "DecisionDetails", type: .zoom)
}
}
然后来自目标VC的viewDidLoad或viewWillAppear中的任何代码都不会以任何方式影响这一点(我尝试将它们全部注释掉,没有任何区别。
知道这是什么原因吗?我可以分享其他任何需要的细节。
谢谢。
答案 0 :(得分:8)
这件事发生在我身上,因为我在viewWillAppear( :)方法中注册了用于更改方向通知的设备。 我将注册移到了viewDidAppear( :)中,并且Xcode不再在断点处停止。
我可以说的是,当视图已经可见时,可以运行布局更改...
答案 1 :(得分:7)
就像@ joe-h一样,我遇到了这个错误,也感到惊讶,因为他上面展示的放松方法是许多开发人员使用的一种方法,并且在某些重要的Apple iOS示例代码中使用。
我的代码中的触发行(@ joe-h,我想也可能在您的代码中)是selectedIndexPath处的tableView.reloadRows(这是未包装的tableView.indexPathForSelectedRow):
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
不幸的是,如果在更新现有tableView行中的值后要平仓,则无法注释掉该行(这是上述Apple FoodTracker教程以及Apple的Everyone Can Code系列中使用的一种方法) )。如果您不重新加载行,则您的更改将不会显示在tableView中。在取消对重新加载的注释之后,我添加了一个带有以下代码的viewDidAppear,这似乎可以解决问题:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let selectedIndexPath = tableView.indexPathForSelectedRow {
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
}
}
我欢迎评论这是否是一种合理的方法,但是目前看来,这是可行的。
答案 2 :(得分:4)
此警告可能发生在不可见的更新表视图或集合视图时,例如在父视图控制器上时。为了解决这个问题,首先,我在视图控制器中创建了一个属性,其中包含表视图,以检查视图控制器是否可见,如下所示:
var isVisible: Bool = false
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.isVisible = true
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
self.isVisible = false
}
然后在对更改做出反应之前,首先在数据源委托中检查视图控制器是否可见。如果不是,请不要进行任何更新。例如
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard isVisible else { return }
tableView.beginUpdates()
}
在tableView中进行任何更改之前,应检查可见性。例如,对于NSFetchedResultsController,必须在我们实现的所有委托回调中完成。
更新
我最近发现,如果使用动画false更新表视图,即使它不可见,也不会发出任何警告。
答案 3 :(得分:3)
我的专案有相同的错误;一个具有可扩散数据源的tableView。一直困扰着它几个小时。问题在于更新快照,更具体地说是在后台线程上更新(默认)。强制在主线程上更新数据源摆脱了这个问题!希望这对外面的人有帮助!
func updateData(on annotations: [Annotation]) {
var snapshot = NSDiffableDataSourceSnapshot<AnnotationType, Annotation>()
//Append available sections
AnnotationType.allCases.forEach { snapshot.appendSections([$0]) }
//Append annotations to their corresponding sections
annotations.forEach { (annotation) in
snapshot.appendItems([annotation], toSection: annotation.type as AnnotationType)
}
//Force the update on the main thread to silence a warning about tableview not being in the hierarchy!
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
答案 4 :(得分:3)
我发现最健壮和安全的方法是等待表视图/集合视图的didMoveToWindow
因为即使在 viewWillAppear
中,视图也可能不附加到窗口并且将您的代码放在 viewDidAppear
中可能会导致不必要的图形故障
class MyTableViewOrCollectionView: UITableView {
var didMoveToWindowCallback: (()->())? = nil
override func didMoveToWindow() {
super.didMoveToWindow()
didMoveToWindowCallback?()
didMoveToWindowCallback = nil
}
}
你可以
override func viewDidLoad() {
super.viewDidLoad()
tableView.didMoveToWindowCallback = { [weak self] in
self?.setupInitialContent()
}
}
答案 5 :(得分:2)
我是Xcode / Swift的新手,所以这可能会或可能不会帮助任何人。从详细信息视图返回列表时,我在更新至应用程序中的iOS 13和Xcode 11后开始出现此错误。
我发现自己正在放松tableView.reloadRows
和tableView.insertRows
(如Apple在one of their tutorials中所建议的那样)
@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing meal.
meals[selectedIndexPath.row] = meal
tableView.reloadRows(at: [selectedIndexPath], with: .none)
}
else {
// Add a new meal.
let newIndexPath = IndexPath(row: meals.count, section: 0)
meals.append(meal)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
}
}
)
我注释掉了这段代码,然后它消失了。
奇怪的是,离开排序和self.tableView.reloadData()
并没有给我错误。
答案 6 :(得分:2)
我在SwiftUI上遇到了类似的断点,甚至没有处理viewDidLoad或viewDidappear
//
// ContentView.swift
// DD
//
// Created by Roman Emperor on 3/29/20.
// Copyright © 2020 Emperors. All rights reserved.
//
import Combine
import SwiftUI
// Defining a class Booking of type Bindable Object [changed to ObservableObject]
class Booking: ObservableObject {
var didChange = PassthroughSubject<Void, Never>()
// Array of types to work with
static let types = ["Consultation", "Tooth Pain", "Cleaning", "Brases", "Dental Implant" ]
// Setting instance varibale type
var type = 0 { didSet { update() } }
func update () {
didChange.send(())
}
}
struct ContentView: View {
@ObservedObject var booking = Booking() //bindableObject in old swift version
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $booking.type, label: Text("Select a Booking Type")) {
ForEach(0 ..< Booking.types.count){
Text(Booking.types[$0]).tag($0)
}
}
}
}
.navigationBarTitle(Text("Darpan Dental Home"))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
完整的输出日志在这里:
*> 2020-03-29 09:22:09.626082 + 0545 DD [1840:76404] [TableView]警告
仅一次:通知UITableView布置其可见单元格和其他 内容,而不会出现在视图层次结构中(表视图或以下之一) 其超级视图尚未添加到窗口)。这可能会导致错误 强制表格视图内的视图加载和执行布局而无需 准确的信息(例如表格视图范围,特征收集,布局 边距,安全区域插图等),并且也将导致不必要的 由于额外的布局次数而导致的性能开销。 成为象征 UITableViewAlertForLayoutOutsideViewHierarchy的断点以捕获 并在调试器中查看此情况, 如果可能的话,请完全避免执行此操作,或者将其推迟到表格中 视图已添加到窗口。*
**此UITableViewAlertForLayoutOutsideViewHierarchy在SwiftUI中在哪里? **
答案 7 :(得分:2)
在viewDidDisappear方法中,我声明了tableView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
函数。有人说这并不重要,但它影响了tableView委托方法。例如,当我收到此警告时,不会调用viewForHeader函数。
答案 8 :(得分:1)
extension UIView {
func rootView() -> UIView {
var view = self
while view.superview.isNotNil {
view = view.superview!
}
return view
}
var isOnWindow: Bool {
return self.rootView() is UIWindow
}
}
然后,您只需要检查tableView isOnWindow
是否像...
if self.tableView.isOnWindow {
/// do stuff
}
免责声明:如文档所述,您可能需要推迟调用,这意味着没有保证将再次调用您的方法,因此当isOnWindow
为真时,您有责任执行更新。
答案 9 :(得分:1)
iPad OS 13.2.3 swift 5.2 Xcode 11.2.1
仅在设备为横向时启动应用程序时遇到此问题。
我在主控制器的viewDidLoad
函数中调用了详细信息序列,以确保正确设置了详细信息视图。
override func viewDidLoad() {
super.viewDidLoad()
...
self.performSegue(withIdentifier: "showDetail", sender: self)
}
当我移除performSeque
时,警告不再出现,但是,
细节控制器上的左栏按钮不再正常工作,仅在设备处于横向状态时启动应用程序时才再次正常工作。最左侧的按钮将激活右侧的下一个按钮,而不是第一个按钮应该执行的操作。
修复条形按钮的方法是将其添加到viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
...
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.allVisible
}
然后执行
override func viewWillAppear(_ animated: Bool) {
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.automatic
super.viewWillAppear(animated)
}
我无法解释为什么这样做!
在加载iPados 13之前,此应用程序可以正常工作。
答案 10 :(得分:1)
发生同样的问题,删除了tableView.reloadSections
,已修复。这是引起警告的代码行:
iOS 13:
tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
在iOS 14中,删除tableView.reloadSections
不能解决该警告。
答案 11 :(得分:1)
对于使用 DiffableDataSource
的人,将 animatingDifferences
设置为 false
并且警告将消失。
dataSource.apply(snapshot, animatingDifferences: false)
答案 12 :(得分:1)
或者也许您的代码(如我的)没有任何问题,并且此消息只是随机开始弹出。在这种情况下,请清理您的项目,重新启动 Xcode 并观察消息神奇地消失!
答案 13 :(得分:0)
请检查以下功能
override func viewWillLayoutSubviews()
答案 14 :(得分:0)
对于在详细视图控制器中使用 UISplitViewController 和 UITableView 遇到此问题的任何人,您可以尝试子类化并覆盖 layoutSubviews,如下所示 (From this thread):
class CustomTableView: UITableView {
override func layoutSubviews() {
if (self.window == nil) {
return
}
super.layoutSubviews()
}
}