0

I am navigating between screens in my iOS application.

BaseView.NavigationController.ViewControllers

As I switch screens, I keep a reference to the previous screen in a static variable.

At some point, one of my items gets removed from BaseView.NavigationController.ViewControllers even though it's still a valid viewcontroller and IsLoaded is still set to True/YES.

When I use (pardon my C#/MonoTouch)

BaseView.NavigationController.PopToViewController(CurrentViewController,false); 

to show it again, I get NSInternalInconsistencyException Reason: Tried to pop to a view controller that doesn't exist. This is understandable because it's no longer in the ViewController collection.

The way I am switching screens is I am keeping a reference to he various screens and calling a common method to show the screen. In that method I use this logic to determine if I should push or pop.

        if (CurrentViewController.IsViewLoaded)
        {

            BaseView.NavigationController.PopToViewController(CurrentViewController,false);
        }
        else
        {
            BaseView.NavigationController.PushViewController(CurrentViewController,true);
        }

My question is where did it go and why would it have been removed from ViewControllers collection and when it's StillLoaded=true/YES?

user1060500
  • 1,505
  • 1
  • 21
  • 40

1 Answers1

0

If I understand correctly, you're using NavigationController.PopToViewController(controller); to navigate back to a certain view controller but keep a reference of the View Controllers that are popped from the navigation stack.

What I think is happening is because you're keeping a reference to these View Controllers, they're still in memory and thus the IsViewLoaded property is still true despite the View Controller not actually existing on the navigation stack.

Rather than using the IsViewLoaded property, you should check whether the View Controller exists in the NavigationController.ViewControllers array, if it does then Pop to it, if it doesn't then push it.

E.g.

if (BaseView.NavigationController.ViewControllers.Contains(CurrentViewController))
{
    BaseView.NavigationController.PopToViewController(CurrentViewController,false);
}
else
{
    BaseView.NavigationController.PushViewController(CurrentViewController,true);
}

Edit

So you mention you'd like a view to persist on the navigation stack. Well, using PopToViewController will remove ALL View Controllers between the TopViewController and the specified Controller.

In order to achieve what you're wanting, you could directly manipulate the NavigationControllers.ViewControllers array. Only problem with this is you'll lose the nice animations that the Push/Pop methods provide.

// Changes order of View Controllers currently in the stack. You can also add/remove 
// controllers using this method.
NavigationController.ViewControllers = new UIViewController[]{
          NavigationController.ViewControllers[1],
          NavigationController.ViewControllers[0],
          NavigationController.ViewControllers[3],
          NavigationController.ViewControllers[2]};
Luke
  • 3,665
  • 1
  • 19
  • 39
  • Interesting. Let me try. As a developer, do we have any control over what gets persisted and what doesn't? Let's say I wanted certain screen to always be persisted, can I control this - or do I have to always check to see if they have been removed and create new instances if they have been? – user1060500 Nov 25 '12 at 03:31
  • If you're keeping a reference to the `UIViewController` then it is persisting, it just may not be on the navigation stack. So if you use the reference in the `PushViewController` call, then you're not necessarily creating a new instance when you push it onto the Navigation Stack. – Luke Nov 25 '12 at 03:34
  • I understand this. My point is, as developers, can we control when ViewControllers get removed from the ViewControllers collection. For example, I want a certain ViewController to always be in that collection and not removed automatically for whatever reason. – user1060500 Nov 25 '12 at 03:52
  • So, I mean - I want to keep something persisted on the navigation stack. – user1060500 Nov 25 '12 at 03:52
  • Unfortunately, I need the animations. Could I not manipulate the ViewControllers manually and then pop to the one I just added to the collection? That should keep the animations? I have yet to try this... I will try later – user1060500 Nov 25 '12 at 15:56
  • I'm unsure what use case would require the functionality you describe (I'm not saying what you're doing is *wrong*, just, with the information provided I can't see why you're popping/pushing so often). Could you not present some of the views modally, so you're not changing the navigation stack so frequently? – Luke Nov 26 '12 at 15:59
  • See my other thread here if you want an idea for the use case. http://stackoverflow.com/questions/13436281/architectural-approach-to-handling-swiping-back-and-forth-between-many-screens-i Think about how the Photos app works. You swipe left and right continually to see a video or a photo. In my case the content is a little more than a photo, so it's encapsulated in a view controller which I show. I want to limit the creation of these for memory reasons. If vc exists for specific page (id) pop it, otherwise create it. – user1060500 Nov 27 '12 at 01:57
  • What functionality specifically requires an entire VC to display the content? You can have one View controller, preload 3 views, the visible view, previous view and next view. Use a `UIPanGesutureRecogniser` to detect swipe, when enough of a pan is detected, animate the visible view off screen, animate the preloaded view onto the screen and lazy load the next view in the list. That's how I would do it. – Luke Nov 27 '12 at 02:24
  • Thanks Luke for your patience and your help. I will look into your approach. I'm just learning MonoTouch and iOS development, so there are a lot of concepts in iOS that I am still familiarizing myself with. – user1060500 Nov 27 '12 at 19:53
  • No worries, I was there once and know how awesome it is to have someone help, it's the least I could do. – Luke Nov 28 '12 at 00:30
  • So, as it makes sense to me. My first screen is a menu. That will have it's own View Controller. If they click on "Menu A" it will go into my area of the app where I want to the user to be able to swipe left and right through a history of previous content. This will have it's own ViewController as well, but will display a single view for each item navigated through and display and lazy load others as swiped in. The views will conform to a common interface so that the ViewController can easily interact with a variety of views. Does this make sense from an iOS architecture standpoint? – user1060500 Nov 28 '12 at 02:59