0
votes

I know it's a common question but it seems i can't find the right solution that fits my needs.

Ok i've got a UIViewController (which parent view is a UINavigationController and then a UITabBarController) forced to be in a portrait mode. This guy has the ability to present modally another UIViewController which is been forced to stay in landscape mode when the user rotate the device. I've decided to use a manual segue to present this last guy. Here is the code for the presentation:

- (void)viewDidAppear:(BOOL)animated {
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self   selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (void)orientationChanged:(NSNotification *)notification {
    // Respond to changes in device orientation
    if (UIDeviceOrientationIsLandscape([[UIDevice currentDevice] orientation])) {
        [self performSegueWithIdentifier:@"gallerySegue" sender:self];
    }
}

-(void) viewDidDisappear {
    // Request to stop receiving accelerometer events and turn off accelerometer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

To return to the first view controller i use these pieces of code

- (void)viewDidAppear:(BOOL)animated {
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(returnBack:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

-(void) viewDidDisappear {
    // Request to stop receiving accelerometer events and turn off accelerometer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

- (void)returnBack:(NSNotification *)notification {
    // Respond to changes in device orientation
    if (UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {
        [self dismissViewControllerAnimated:NO completion:nil];
    }
}

The warning is generated by the second view controller when i rotate the device.

WHY??

Thanks

3

3 Answers

1
votes

Warning: Attempt to present ViewController on TabBarController whose view is not in the window hierarchy .This warning is because when you push: or present: a viewController from the viewController Which is not in the view. Meaning Your first view controller has not been loaded or appeared fully but from that ViewController you are presenting or Pushing another.

0
votes

Solved!

Instead of this

  - (void)viewDidAppear:(BOOL)animated {
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] addObserver:self   selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

I put the code above in the viewWillAppear.

Regarding this

-(void) viewDidDisappear {
    // Request to stop receiving accelerometer events and turn off accelerometer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

I placed it in the prepareForSegue method and it worked!

0
votes

I am a swift user but I think worth mentioning here:

If you trying to do UI stuff on viewDidLoad() you will get this message because all the stuff you are doing will loaded to memory but cannot perform anything until view hierarchy loaded properly. Simply you are trying to do something before attaching to view heirarchy.

So, the solution is to wait few seconds until view hierarchy load properly. Here is a not recommended way (also this will execute after showing the view) but I need to show you that you can do something like this too:

override func viewDidLoad() {
    super.viewDidLoad()
    //your stuff....
    Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(doSomeStuff), userInfo: nil, repeats: false)
}

@objc func doSomeStuff() {
    //your stuff....
    self.performSegue(withIdentifier: "signInTabBarVC", sender: nil) // i just want to perform transition if logged in
}

As I said it is not recommended way and here is the recommended way:

override func viewDidAppear(_ animated: Bool) {
    //if Auth.auth().currentUser != nil {
        self.performSegue(withIdentifier: "signInTabBarVC", sender: nil)
    //}
}

Above is a override method that apple recommended way to perform UI stuff. Because it execute after view heirarchy initialized and attached.