7

In my app I programmatically change root view controllers based on user actions e.g. login/logout functionality.

In iOS 8 - I'm noticing a strange issue. Even after setting rootViewController on the window, the old hierarchy still persists. I just verified it by capturing view hierarchy.

- (void) logout{
     [self.window setRootViewController:[self loadLoginView]];
}

-(UIViewController *) loadLoginView{
      WelcomeScreenVC *wsVC;
      wsVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:nil];
      UINavigationController *onboardingVC = [[UINavigationController alloc]initWithRootViewController:wsVC];
      return onboardingVC;
 }

Even after executing this line of code, the old logged in view hierarchy still persists. Would appreciate if anybody can suggest what's happening behind the scenes.

Edit: I just looked at UIWindow setRootViewController documentation and here's what Apple has to say about it:

The root view controller provides the content view of the window. Assigning a view controller to this property (either programmatically or using Interface Builder) installs the view controller’s view as the content view of the window. If the window has an existing view hierarchy, the old views are removed before the new ones are installed.

Naz Mir
  • 1,028
  • 1
  • 9
  • 19
  • Can you share the code for `[self loadLoginView]` please? – Josh Heald Nov 07 '14 at 07:03
  • @JoshHeald added it to the question – Naz Mir Nov 07 '14 at 07:06
  • Is that copied out of your app directly, or did you type it? I ask because the `loadLoginView` declares a `void` return type, which wouldn't be helping matters – Josh Heald Nov 07 '14 at 07:11
  • @JoshHeald, sorry I copied it from the app, but removed some sensitive info while editing. The return type of that method is UIViewController – Naz Mir Nov 07 '14 at 07:12
  • no problem. I'm struggling to recreate this, but wanted to know how you're checking the hierarchy - you say you "verified it by capturing view hierarchy", is this using the new Xcode 6 View Hierarchy Debugger, or some other means? – Josh Heald Nov 07 '14 at 07:24
  • @JoshHeald yes I did use the Xcode 6 view hierarch debugger – Naz Mir Nov 07 '14 at 07:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/64464/discussion-between-josh-heald-and-naz-mir). – Josh Heald Nov 07 '14 at 07:27
  • For me, this problem shows up if I present something https://github.com/onmyway133/notes/issues/251 – onmyway133 Dec 13 '16 at 14:30

3 Answers3

2

I have noticed the very same thing.

Basically, I have a fairly complicated storyboard that acts as a login/welcome interface. This interface sits in a navigation controller, which presents another navigation controller modally.

After a certain point, the user takes an action that transitions him to the main interface. Using the iOS 8 view debugger I noticed that the old view hierarchy was still around after setting the rootViewController property of the window.

My solution, for now is to use the following code, right before I re-assing the window.rootViewController property:

for (UIView* subView in self.window.rootViewController.view.subviews) {
    [subView removeFromSuperview];
}
[self.window.rootViewController.view removeFromSuperview];

It ain't pretty, but it works.

Another odd thing I noticed is that the welcome interface's modally presented viewController is not properly cleaned up using this method. I have to manually dismiss it AND do this clean up.

TMart
  • 39
  • 2
  • this seems to work in terms of the UI that's displayed but the correct screen now has no interaction enabled for some reason.. so I can't tap any of the buttons or text fields... any ideas? I had a similar issue previously where the correct UI wasn't shown and the previous UI became somewhat unresponsive so the tab bar items would show that they'd been selected but not action any of their changes. – CMash Dec 19 '14 at 11:39
  • Got an alternative solution to my situation, where swapping the root view controller while a lengthy set of network connections were being carried out I'd end up with the old UI remaining (if I was quick enough), with buttons functional but tab bar buttons not changing the tab controller's view. CGRect windowFrame = self.window.frame; self.window.rootViewController = nil; self.window = nil; UIWindow* newWindow = [[UIWindow alloc] initWithFrame: windowFrame]; [newWindow makeKeyAndVisible]; self.window = newWindow; – CMash Feb 02 '15 at 14:27
1

The best way to fix is:

self.window.subviews.forEach { $0.removeFromSuperview() }

or, in old style:

for view in self.window.subviews {
   view.removeFromSuperview()
}
KostiaZzz
  • 170
  • 14
0
var loginNavigationController: OnBoardViewController?{
    willSet{
        if newValue == nil {
            loginNavigationController?.view.removeFromSuperview()
        }
    }
}

loginNavigationController = nil Then set window.rootviewcontroller = {Different VC}

Ankish Jain
  • 11,305
  • 5
  • 36
  • 34