I am getting a strange error with iOS13 when performing a Segue and I can\'t figure out what it means, nor can I find any documentation for this error. The problem is that t
I am getting similar breakpoint with SwiftUI, without even dealing with viewDidLoad or 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()
}
}
The Complete output Log is here:
*> 2020-03-29 09:22:09.626082+0545 DD[1840:76404] [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.*
**where is this UITableViewAlertForLayoutOutsideViewHierarchy in SwiftUI ? **
This warning can happen du to updating table view or collection view while it is not visible, for example when it is on the parent view controller. To solve that, first, I created a property in the view controller, containing the table view to check if the view controller is visible or not, as bellow:
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
}
Then in the data source delegate, before reacting to changes, first check if the view controller is visible. If it was not, do not do any updates. For example
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard isVisible else { return }
tableView.beginUpdates()
}
You should check that visibility before doing any changes in the tableView. For example, in case of NSFetchedResultsController, it must be done in all delegate callbacks which we have implemented.
UPDATE
I recently found that if you update the table view with animation false, even when it is not visible, there won't be any warnings.
I had the same error on my Project; A tableView with a diffable datasource. Been bugging on it for hours. Problem lies in updating the snapshot, more specifically on a background thread (default). Forcing the update of the datasource on the main thread got rid of the problem! Hope this helps someone out there!
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)
}
}
Like @joe-h, I was getting this error and was also surprised as the unwind approach he shows above is one used by lots of developers + is in some significant Apple iOS sample code.
The triggering line in my code (@joe-h, I'm guessing likely in yours, too) is a tableView.reloadRows at the selectedIndexPath (which is an unwrapped tableView.indexPathForSelectedRow):
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
Unfortunately commenting out the row isn't an option if you are unwinding after updating the value in an existing tableView row (which is an approach in the Apple FoodTracker tutorial mentioned above, as well as one used in Apple's Everyone Can Code series). If you don't reload the row(s) then your change won't show in the tableView. After commenting out the reload in the unwind, I added a viewDidAppear with the following code and this seems to fix things:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let selectedIndexPath = tableView.indexPathForSelectedRow {
tableView.reloadRows(at: [selectedIndexPath], with: .automatic)
}
}
I'd welcome comments on whether this is a sound approach, but for now, this seems to be working.
iPadOS 13.2.3 swift 5.2 Xcode 11.2.1
Just ran into this issue only when starting the app while the device was landscape.
I was calling the detail seque in the viewDidLoad
func of the master controller to make sure the detail view was setup correctly.
override func viewDidLoad() {
super.viewDidLoad()
...
self.performSegue(withIdentifier: "showDetail", sender: self)
}
When I removed the performSeque
the warning not longer appeared, however,
the left bar buttons on the detail controller no longer worked properly, again only when starting the app while the device was landscape. The left most button would activate the next button to the right instead of what the first button was suppose to do.
The fix for the bar buttons was to add to the viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
...
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.allVisible
}
Then execute
override func viewWillAppear(_ animated: Bool) {
self.splitViewController?.preferredDisplayMode = UISplitViewController.DisplayMode.automatic
super.viewWillAppear(animated)
}
I have no explanation why this worked!
This app had worked flawlessly until iPados 13 was loaded.
Had the same issue, removing tableView.reloadSections
fixed it. This was the line of code causing the warning:
iOS 13:
tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
in iOS 14, removing tableView.reloadSections
did not fix the warning.