UITableViewAlertForLayoutOutsideViewHierarchy

Hi i get an error


[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. Table view: <UITableView: 0x102094800; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x282dac3f0>; layer = <CALayer: 0x2823fb9c0>; contentOffset: {0, -64}; contentSize: {320, 248}; adjustedContentInset: {64, 0, 49, 0}; dataSource: <IOUUOMe.UOMeViewController: 0x10140a340>>


And really the question is what is this error stating and how do i fix it. This happens when i tap an UIBarButton on an Navigation Bar. I have put an symbolic breakpoint which is how i found the button that was causing the error but how do i get rid of this error. and fix it properly

Replies

Still an issue in newly released 13.4.


My main problem is when a table is in split overlay mode (e.g. by default on iPhone X/XR/11) but hidden the rotation causes UITableView layoutSubviews which throws UITableViewAlertForLayoutOutsideViewHierarchy despite the table not being in the window.


Apple please see FB7306484


My current workaround involves subclassing UITableView with:


#import "TableView.h"

@implementation TableView

- (void)layoutSubviews{
    if(!self.window){
        return;
    }
    [super layoutSubviews];
}

@end


But there are still cases where the issue occurs.


I'm thinking about just ignoring this and going back to letting the table view be updated when not in a window too.

In my case, the error occurs only if the table view is scrolled before a row is selected. It's a hack, but your solution worked for me. Thanks!

Inaddition to this still happening in 13.4, it is also happening in SwiftUI. I have a view with this code, and the error happens when the user swpies to delete a row...so far I haven't found any workaround.... 😐


import SwiftUI

struct ContentView: View {
    @State var showNewJournal = false
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(entity: Journal.entity(), sortDescriptors: []) var journals: FetchedResults<Journal>
    @State var image: Data = .init(count: 0)
    @State var journal: Journal = Journal()
    let dl = DataLoader()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(journals, id: \.journalId){ journal in
                    HStack{
                        NavigationLink(destination: MonthSelector(journalId: journal.journalId)) {
                            VStack(alignment: .leading){
                                Image(uiImage: UIImage(data: (journal.coverImage ?? UIImage(named: "defaultPhoto.jpg")?.pngData())!)!
                                    .withHorizontallyFlippedOrientation())
                                    .resizable()
                                    .frame(width: 100, height: 100)
                                    .aspectRatio(contentMode: .fill)
                                    .clipShape(Circle())
                                    .flipsForRightToLeftLayoutDirection(true)
                                Text("Journal Name: \(journal.journalName)")
                                    .font(.headline)
                                Text("Style: \(journal.style)")
                                    .font(.caption)
                            }
                        }
                    }
                }
                .onDelete(perform: delete)
                
            }
                
            .navigationBarTitle("My Journals")
            .navigationBarItems(trailing: Button(action: {
                self.showNewJournal = true
            }, label: {
                Image(systemName: "plus.circle")
                    .resizable()
                    .frame(width: 32, height: 32, alignment: .topTrailing)
            }))
                .sheet(isPresented: $showNewJournal) {
                    NewJournal().environment(\.managedObjectContext, self.managedObjectContext)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            
        }
        
    }
    
    func delete(at offsets: IndexSet) {
        var jid = UUID()
        for index in offsets {
            let journal = journals[index]
            jid = journal.journalId
            managedObjectContext.delete(journal)
        }
        do {
            try managedObjectContext.save()
        } catch {
            print("Successfully deleted journal...")
        }
        dl.deleteData(journalId: jid)
    }
}

would be nice if anyone with a bit more experience could let me know what can be done as a workaround for this.


Oh and it is making the app crash 😟

Thank-you! That fixed it for me. Do you know why this fixes this error? I don't understand the relationship between the main queue and this error.

Subclassing UITableView solved the problem for me.
Thanks.
It looks as though this is actually a bug in UITableView.
In my case it is triggered when the parent view lays out subviews but the table view is not yet visible - its height constraint is 0.
At the point the exception is thrown the table view is actually in the view hierarchy - it is just invisible.

IMHO it is not the responsibility of a user of UITableView to check its view state inside the internal implementation of its layoutSubviews method.
Subclassing worked for me as well. Here is the Swift solution:

Code Block
class CustomTableView: UITableView {
    override func layoutSubviews() {
        if (self.window == nil) {
            return
        }
        super.layoutSubviews()
    }
}

I found that the tableView is added to the window after viewWillAppear() and before viewDidAppear(). My code was setting a selection in the tableView and needed to call tableView.cellForRowAtIndexPath() and that was causing the tableView to layout and generated this error. Once I moved my selection code from viewWillAppear() to viewDidAppear() the error went away.

Hello, Worked for me:

  override func viewDidAppear(_ animated: Bool) {

        super.viewDidAppear(animated)

            tableView.reloadData()

    }

Before you you resort to the clever but dangerous trick (shown in some of the answers) that masks the error by overriding the layoutSubviews() method of the table delegate or controller, I'd advise reviewing fundamentals and related documentation for how the table view is constructed and invoked, particularly if a navigation stack is involved. Make sure you're applying the right design pattern in the first place before trying to tackle the obscure downstream consequences of using the wrong paradigm, like I did.

This error was happening to me when I erroneously configured a UITableView delegate (UIViewController) as a tab on the app's root view controller, which I'd made a UITabBarController. In addition to the message quoted in the original question, I was seeing controller "not pushed " errors in the XCode console.

The problems went away after I implemented the approach documented in Apple's document "Combined View Controller Interfaces"

My incorrect approach had been to add the UITableView delegate (UIViewController) directly to the tab bar controller, then use performSegue() in the table delegate's 'didSelect' for index method. I was also using performSegue() as the action of the "+" icon I added to the UINavigationItem (which I obtained from the navigationItem property of UIViewController). The segues invoked the next viewController I'd written to handle (or create) table-related items.

The proper way to do it is to instead add a UINavigationController to the tab bar, not the table view delegate (or table view controller) to the tab bar. Then, add the table view delegate (or table view controller) to the navigation controller. And instead of using performSegue() in the table view delegate's "didSelectRowAt" method use navigationController?.pushViewController() (same for invoking the next navigation item based controller from a button outside the table view instead of a row inside).

Of course the proper way makes more sense, particularly since since my table view was playing with the UINavigationItem components and had configured itself to be used with a navigation bar anyway.

One other note: The error message shown in the question mentions adding a symbolic breakpoint to a certain method to see what's causing the problem. Of course that stops the code and shows a screen with assembly language that will be completely inscrutable to people working with distribution libraries instead of iOS source code that Apple insider's use. So if you try that, you'll want to use 'bt' at the lldb prompt to get the stack backtrace. In my case, all it showed me was that everything started with a tab button press and looked normal and expected. So the problem in my case just came down to a noob ignorance about how some of the pieces go together.

just posted a bug to Apple Developer Feedback...this is still an issue in iPadOS 17 App is using a UISplitViewController with the new column style init. Both primary and secondary child view controllers are subclasses of UITableViewController (yes, I need to move to CollectionViewController ASAP)...

This bug occurs when I am in landscape and select iPadOS "Split View" by tapping on the 3 dots menu on top of the screen. As my app slides to the left, this Xcode console occurs. The backtrace from the symbolic break point indicates there are NO calls in my code in the back trace. This is an apple UIKit bug! Workaround for overriding layouSubviews likely to work...

here's the backtrace...

(lldb) bt

  • thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1

    • frame #0: 0x00000001942cd8dc UIKitCore`UITableViewAlertForLayoutOutsideViewHierarchy

      frame #1: 0x0000000193579fd0 UIKitCore`-[UITableView _updateVisibleCellsNow:] + 208

      frame #2: 0x0000000193579e34 UIKitCore`-[UITableView layoutSubviews] + 148

      frame #3: 0x000000019332797c UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1528

      frame #4: 0x000000019273caa8 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 500

      frame #5: 0x000000019332aa2c UIKitCore`-[UIView(Hierarchy) layoutBelowIfNeeded] + 296

      frame #6: 0x000000019341efac UIKitCore`-[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 128

      frame #7: 0x000000019337b590 UIKitCore`-[_UIViewControllerTransitionContext __runAlongsideAnimations] + 272

      frame #8: 0x0000000193aa0fc4 UIKitCore`-[_UIWindowAnimationController animateTransition:] + 288

      frame #9: 0x00000001936a363c UIKitCore`-[UIWindow _adjustSizeClassesAndResizeWindowToFrame:] + 620

      frame #10: 0x000000019337e29c UIKitCore`-[UIWindow _resizeWindowFrameToSceneBoundsIfNecessary] + 204

      frame #11: 0x000000019346402c UIKitCore`-[UIWindow _sceneBoundsDidChange] + 64

      frame #12: 0x0000000193465490 UIKitCore-[_UIScenefbsSceneBasedMetricsCalculator _updateMetricsOnWindows:animated:] + 940 frame #13: 0x00000001934650bc UIKitCore-[UIWindowScene _computeMetricsForWindows:animated:] + 88

      frame #14: 0x00000001935287b8 UIKitCore`__55-[UIWindowScene _computeMetrics:withTransitionContext:]_block_invoke + 104

      frame #15: 0x00000001935282b0 UIKitCore`-[UIWindowScene _computeTraitCollectionAndCoordinateSpaceForcingDelegateCallback:withAction:] + 296

      frame #16: 0x000000019352817c UIKitCore`-[UIWindowScene _computeMetrics:withTransitionContext:] + 104

      frame #17: 0x00000001943290b8 UIKitCore`-[UIWindowScene _computeMetricsAndCrossFadeInLiveResize:withTransitionContext:] + 192

      frame #18: 0x000000019342dd98 UIKitCore`+[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:] + 736

      frame #19: 0x000000019342d620 UIKitCore`_UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion + 224

      frame #20: 0x0000000193609fac UIKitCore`-[_UIWindowSceneGeometrySettingsDiffAction _updateSceneGeometryWithSettingObserverContext:windowScene:transitionContext:] + 448

      frame #21: 0x000000019342f728 UIKitCore`-[_UIWindowSceneGeometrySettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 1048

      frame #22: 0x00000001937b10a8 UIKitCore`__64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.225 + 612

      frame #23: 0x000000019342c454 UIKitCore`-[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 216

      frame #24: 0x000000019342c2c4 UIKitCore`-[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 244

      frame #25: 0x000000019342c104 UIKitCore`-[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 336

      frame #26: 0x00000001a955c5b4 FrontBoardServices`-[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:] + 660

      frame #27: 0x00000001a955c300 FrontBoardServices`__94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2 + 152

      frame #28: 0x00000001a955c19c FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 168

      frame #29: 0x00000001a955c0b8 FrontBoardServices`__94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke + 344

      frame #30: 0x0000000105d36b34 libdispatch.dylib`_dispatch_client_callout + 20

      frame #31: 0x0000000105d3a530 libdispatch.dylib`_dispatch_block_invoke_direct + 300

      frame #32: 0x00000001a9558520 FrontBoardServices`FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK + 52

      frame #33: 0x00000001a95584a0 FrontBoardServices`-[FBSMainRunLoopSerialQueue _targetQueue_performNextIfPossible] + 240

      frame #34: 0x00000001a9558378 FrontBoardServices`-[FBSMainRunLoopSerialQueue _performNextFromRunLoopSource] + 28

      frame #35: 0x000000019110112c CoreFoundation`CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 28

      frame #36: 0x00000001911003a8 CoreFoundation`__CFRunLoopDoSource0 + 176

      frame #37: 0x00000001910feb5c CoreFoundation`__CFRunLoopDoSources0 + 244

      frame #38: 0x00000001910fd898 CoreFoundation`__CFRunLoopRun + 828

      frame #39: 0x00000001910fd478 CoreFoundation`CFRunLoopRunSpecific + 608

      frame #40: 0x00000001d46564f8 GraphicsServices`GSEventRunModal + 164

      frame #41: 0x000000019352162c UIKitCore`-[UIApplication _run] + 888

      frame #42: 0x0000000193520c68 UIKitCore`UIApplicationMain + 340

      frame #43: 0x0000000104eb4220 MyApp`main at AppDelegate.swift:13:7

      frame #44: 0x00000001b3e22dcc dyld`start + 2240