Search code examples
swiftuiviewuiviewcontrolleruinavigationcontrolleruikit

"Can't add self as subview" crash (with sample code) when view not adding itself to its subview


I can reproduce this problem with a simple project (Github Project)

The mainstoryboard has an embedded ViewController transitioned to by a segue from an initial NavigationController. The Nav Controller is the initial root controller and pointed as such in the storyboard view.

The AppDelegate class didFinishLaunchingWithOptions method instantiates another Navigation Controller and adds a root viewcontroller programmatically:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    let mainvc = MainViewController()
    let mainnc = UINavigationController(rootViewController: mainvc)

    self.window?.rootViewController = mainnc
    self.window?.makeKeyAndVisible()

    // Override point for customization after application launch.
    return true
}

The MainViewController instantiated above has the following code.

class MainViewController : UITableViewController, UIAlertViewDelegate {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    convenience init() {
        self.init(nibName:nil, bundle:nil)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableView = UITableView(frame:CGRect(x:0, y:0, width:self.view.bounds.size.width, height:self.view.bounds.size.height), style:.plain)
        self.tableView = tableView
        tableView.delegate = self
        tableView.dataSource = self
        tableView.separatorColor = UIColor(white:0.85, alpha:1)
        tableView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        tableView.register(UITableViewCell.self, forCellReuseIdentifier:"nephews")

        self.view.addSubview(tableView) // CRASH
    }

}

When running the code, I am getting:

2018-03-15 13:35:57.083953-0700 CantAddSelfAsSubview[20953:14423557] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can't add self as subview'

*** First throw call stack:
(
    0   CoreFoundation                      0x000000010957912b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000105900f41 objc_exception_throw + 48
    2   CoreFoundation                      0x00000001095ee245 +[NSException raise:format:] + 197
    3   UIKit                               0x0000000106267582 -[UIView(Internal) _addSubview:positioned:relativeTo:] + 122
    4   UIKit                               0x00000001062c6341 -[UITableView _addSubview:positioned:relativeTo:] + 127
    5   CantAddSelfAsSubview                0x0000000104fdf310 _T020CantAddSelfAsSubview20MyMainViewControllerC11viewDidLoadyyF + 1312
    6   CantAddSelfAsSubview                0x0000000104fdf6c4 _T020CantAddSelfAsSubview20MyMainViewControllerC11viewDidLoadyyFTo + 36
    7   UIKit                               0x000000010634546c -[UIViewController loadViewIfRequired] + 1235
    8   UIKit                               0x000000010638cffc -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 68
    9   UIKit                               0x000000010638d338 -[UINavigationController _startTransition:fromViewController:toViewController:] + 153
    10  UIKit                               0x000000010638e44f -[UINavigationController _startDeferredTransitionIfNeeded:] + 841
    11  UIKit                               0x000000010638f6d3 -[UINavigationController __viewWillLayoutSubviews] + 150
    12  UIKit                               0x00000001065ea4e2 -[UILayoutContainerView layoutSubviews] + 231
    13  UIKit                               0x000000010626ea6d -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439
    14  QuartzCore                          0x000000010cb5e61c -[CALayer layoutSublayers] + 159
    15  QuartzCore                          0x000000010cb627ad _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401
    16  QuartzCore                          0x000000010cae986c _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 364
    17  QuartzCore                          0x000000010cb16946 _ZN2CA11Transaction6commitEv + 500
    18  UIKit                               0x00000001061b826a __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 141
    19  CoreFoundation                      0x000000010951c05c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
    20  CoreFoundation                      0x000000010950083b __CFRunLoopDoBlocks + 203
    21  CoreFoundation                      0x0000000109500014 __CFRunLoopRun + 1300
    22  CoreFoundation                      0x00000001094ff889 CFRunLoopRunSpecific + 409
    23  GraphicsServices                    0x000000010bd069c6 GSEventRunModal + 62
    24  UIKit                               0x000000010619d5d6 UIApplicationMain + 159
    25  CantAddSelfAsSubview                0x0000000104fe2027 main + 55
    26  libdyld.dylib                       0x000000010a717d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Since I am not adding a view to itself, it seems the problem is somewhere else.

I've read many comments on SO with similar issues but no repro code. Some people fear that there is a bug in the NavController when the animation is not done. How do I control the animation of the segue as it's automatically managed by the first nav controller?


Solution

  • Due to the incorrect code in viewDidLoad, self.view, self.tableView, and tableView are all the same view. Hence the error.

    This is a UITableViewController. It already has a table view. Do not create another one. Update viewDidLoad as follows:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.separatorColor = UIColor(white:0.85, alpha:1)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier:"nephews")
    }
    

    You also do not need to add any of those init methods.