Makaille
Makaille

Reputation: 1666

Obscure crash on layoutIfNeeded()

A crash appears to this :

navigationController.view.layoutIfNeeded()

log : fatal error: unexpectedly found nil while unwrapping an Optional value

The crash appears for the first launch of the app (after the xcode build), and when my user disconnect and reconnect from his account. If i relaunch the app with an user already connected it will be ok.

Full function code :

func prepareControllers(){
    homeTabs = [homeView, teamView, rankView, generalView]

    for view in homeTabs{
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ABMainViewController.didTapTab(_:))))
    }

    mainControllers = [
        UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "home") as! ABHomeViewController,
        UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "teamController") as! ABTeamViewController,
        UIStoryboard(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "leaderboardController") as! ABLeaderboardViewController,
        ABInformationViewController(nibName: "ABInformationViewController", bundle: Bundle.main) 
    ]

    var previousController: UINavigationController? = nil

    for viewController in mainControllers{
        viewController.delegate = self;
        let navigationController = UINavigationController(rootViewController: viewController)
        navigationControllers.append(navigationController)
        navigationController.view.frame = scrollView.bounds
        navigationController.setNavigationBarHidden(true, animated: false)
        navigationController.view.translatesAutoresizingMaskIntoConstraints = false

        scrollView.addSubview(navigationController.view)

        scrollView.addConstraint(NSLayoutConstraint(item: navigationController.view, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: navigationController.view, attribute: .height, relatedBy: .equal, toItem: scrollView, attribute: .height, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: navigationController.view, attribute: .width, relatedBy: .equal, toItem: scrollView, attribute: .width, multiplier: 1, constant: 0))

        if(previousController != nil){
            scrollView.addConstraint(NSLayoutConstraint(item: navigationController.view, attribute: .left, relatedBy: .equal, toItem: previousController!.view, attribute: .right, multiplier: 1, constant: 0))
        }else{
            scrollView.addConstraint(NSLayoutConstraint(item: navigationController.view, attribute: .leading, relatedBy: .equal, toItem: scrollView, attribute: .leading, multiplier: 1, constant: 0))
        }

        navigationController.view.layoutIfNeeded()  // CRASH HERE
        previousController = navigationController
    }

    scrollView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .trailing, relatedBy: .equal, toItem: previousController!.view, attribute: .trailing, multiplier: 1, constant: 0))
}

In the debugging navigator :

    0x102b691f8 <+116>: bl     0x102a5cb80               ; function signature    specialization <preserving fragile attribute, Arg[1] = [Closure Propagated : reabstraction thunk helper from @callee_owned (@unowned Swift.UnsafeBufferPointer<Swift.UInt8>) -> () to @callee_owned (@unowned Swift.UnsafeBufferPointer<Swift.UInt8>) -> (@out ()), Argument Types : [@callee_owned (@unowned Swift.UnsafeBufferPointer<Swift.UInt8>) -> ()]> of generic specialization <preserving fragile attribute, ()> of Swift.StaticString.withUTF8Buffer <A> ((Swift.UnsafeBufferPointer<Swift.UInt8>) -> A) -> A
 ->  0x102b691fc <+120>: brk    #0x1    //HERE

is there a way to found where is this unwrapping value ? If someone have an idea, how can i solve it or how can i debug it more clearly. Thank's !

Upvotes: 0

Views: 4965

Answers (2)

Hackbarth
Hackbarth

Reputation: 19

As Eperrin95 said maybe you are trying to layout something that doesn't exists yet. The layout methods should be called after viewDidAppear or at least viewWillAppear.

Upvotes: 1

EPerrin95
EPerrin95

Reputation: 654

Log say that one of your instance in navigationController.view.layoutIfNeeded() is nil. Means that either navigationController or view is nil.

All views of a controller is nil before the method viewDidLoad is called. Or, in your code, your instantiate the UINavigationController and call it's main view directly after that.

If you really want to call layoutIfNeeded method, subclass UINavigationController and call the method in viewDidLoad, viewWillAppear or viewDidAppear, even if I think you don't have to call it if your constraints are well implemented

class CustomNavigationController: UINavigationController {
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        self.view.layoutIfNeeded()
    }
}

Upvotes: 0

Related Questions