I have an app where I have an imageview displayed within a UIView that the user has scaled and moved around, and I'd like to pass this exact same imageview to the next UIViewController that gets pushed onto the navigation stack. What is the best way to go about doing this? Do I have make some sort of deep copy?
controller.imageView = imageView;
There's not much special about this. The only thing you need to do is to add the view to your view hierarchy. Views can only have a single superview, so when you add it to a new view hierarchy, it is automatically removed from the old view hierarchy. The only tricky thing is when you pop back up the stack. Do you expect the view to still be available in the old view controller? (It won't be.)
In general, I'd tend to recommend passing the parameters rather than the actual view (i.e. the image and its transform). This gets rid of any issues going up or down the stack, but either way can work.
Give the view reference to the next controller, retaining it.
You could have some problem to deal with its bounds or frame, but if you just want the same, that should be ok.
In the new controller, get that passed view and add it as a subview of your main view. Don't forget at the end to remove it from its superview, and to release it before returning to the previous controller.
Related
Is it possible to reload the main view of a view controller from its XIB file in its viewWillAppear method? The method make significant changes to the view, which would be difficult to reverse. When the view controller gets pushed off the navigation stack, the method has to deal with a changed view. It would be much easier if I could just relax the view from the XIB. It seems that the Apple implementation assumes that the view will remain static, and can therefore be reused.
You can update large portions of your view in the -viewWillAppear. There is no stipulation what you can or can't update there.
Just make sure you account for everything, when you do. I have used -viewWillAppear to refresh my view with information, so you can use that to load an initialize what you need rather than -viewDidLoad.
I have application with multiple UIViewControllers using navigation controller. UIViewController contains tableView, searchbar (that I can show/hide) and toolbar. All of this is added as subviews to its view. All this subviews are created after UIViewController is initialized and their content depend on UIVievController's content.
It works fine expect one problem. When I play with my app a little, move back and forth, open some modal views etc sometimes after navigating back to my root VC all it's subviews dissapear and all I get is white screen.
I double checked all my code and I can't find source of problem (I certainly don't remove them myself). I wasn't able to find exact patern how to reproduce this, it seems random. Any idea why iphone would remove my subviews from VC? I would post some code, but I don't want to put it all here and I am not sure which part is important, so if you wish to see some, let me know
Add your views in loadView or viewDidLoad: when viewDidUnload is called, the view is released, so they need to be created again when the view is shown again.
Using a UINavigationViewController, how do I find out how a view has appeared?
The view has either appeared in a straightforward manner, as the first view in the UINavigationController stack. Or it has appeared because a second view has been popped and the first view has revealed itself again. How do you find out which of these happened?
The only reliable way to do this, as far as I'm aware, is to subclass UINavigationController and override the UINavigationBarDelegate methods:
– navigationBar:shouldPushItem:
– navigationBar:didPushItem:
– navigationBar:shouldPopItem:
– navigationBar:didPopItem:
Don't forget to call super, of course.
Simple approach is to add a property to your RootViewController to track whether or not it has pushed another view onto the navigationController.
-(BOOL)hasPushedSecondView;
Initialize to NO in your init method.
Before pushing secondViewControllers view onto the stack, update the property to YES.
In viewWillAppear, check the value and update your view accordingly. Depending on how you want the application to behave you may need to reset the hasPushedsecondview property back to NO.
you could take a look at the leftBarButtonItem or backBarButtonItem, based on how your application is written and determine how the view appeared. If it is on top, unless you have a custom leftBarButtonItem, there would be no object there.
You can determine this directly via a couple of methods on your UIViewController subclass.
From Apple's documentation:
Occasionally, it can be useful to know why a view is appearing or
disappearing. For example, you might want to know whether a view
appeared because it was just added to a container or whether it
appeared because some other content that obscured it was removed. This
particular example often appears when using navigation controllers;
your content controller’s view may appear because the view controller
was just pushed onto the navigation stack or it might appear because
controllers previously above it were popped from the stack.
The UIViewController class provides methods your view controller can
call to determine why the appearance change occurred.
isMovingFromParentViewController: view was hidden because view controller was removed from container
isMovingToParentViewController: view is shown because it's being added to a container
isBeingPresented: view is being shown because it was presented by another view controller
isBeingDismissed: view is being hidden because it was just dimissed
So I'm trying to find out the best way to swap views for an iPhone game I'm making. I have a "root view controller" that has a reference to all the view controllers I want to swap between. So I add the main menu view to this root view controller - [self.view addSubview:mainMenuController.view]; - Then in the main menu view I have an instructions button. I define an IBAction in the mainMenuController to respond to the instructions button being clicked. From there, I call [self.view removeFromSuperview] which works great. It gets rid of the main menu. So next I want to add the instructions view. I figured that it would be as simple as [super.view addSubview:super.instructionsController.view], but no luck! I've thought of a few ways to get around this, but they all seem very inelegant, as I would like to keep all references to my view controllers in one place, the root view controller. Any thoughts?
Could you not have your main menu view controller tell the root view controller that it's closed, along with a reference to what view it should load next? I'm assuming you're not doing something akin to what UITableView does, in which case you'd likely want to use a stack.
Are you mixing super (an Objective-C keyword) with self.superview (an UIView property)?
At any rate it's indeed a better design to inform your main controller to switch to another view, rather than making that decision from the added subview.
I just got around it by setting a delegate property in each of my controllers to the root view controller.
At the beginning of my app, I am initializing a UIViewController with a NIB.
At some point, I am removing the view from the screen, then releasing it, so it no longer takes up memory. (At this point I am also saving the state of the view in the UIViewController)
At a later point, I want to recreate the view and bring it back on screen. I will then restore its state. (Using the same UIViewController, not a new one, since it saved the state)
My question, is when I recreate the view, how do I do so from the NIB, or is this not possible?
To me, the obvious remedies are:
Don't save state in the UIViewcontroller (Where does convention dictate that I do save state?)
Don't release the view (maybe just release all of its subviews?)
Don't load my view from a NIB, create programmatically (seems to go against using IB for everything)
Note:
I am not using a UINavigationController, I am handling the swapping of the views myself, since thee are only 2 of them.
If you need that level of control, you dont really want to use IB. Because, as you noted, if remove the view and then later recreate it, you would have to do it in code then anyway. Just design most of the views in IB, then write some code that generates just this view. Then you can call that same method again later to recreate that view when you need it.
You may be able to archive it and later turn it back into an object, but that seems like an inelgant solution. IB does not allow for dynamic creation of controls at runtime, even if they used to exist but don't anymore. There is no shame in leaving IB out of loop for this. In fact it's probably a good idea.
OR
If its a complicated view with a lot of pieces, put the view in it's own nib, and make a view controller for it. Then you can simply instatiate the view controller with the nib name, and add the controllers view as a subview to you main view. Then your view controller handles loading of the nib, and you get to design it in IB. Nothing says the view of a view controller has to take up the entire screen either.
self.otherController = [[OtherController alloc] initWithNibName:#"Other" bundle:nil];
[self.view addSubview:otherController.view];
Don't create the view controller and the view in the same nib file; either create the controller in code or put the view in a separate nib file. If you later nil out your controller's view property, the controller should recreate the view.
But why are you so worried about this? There's already a mechanism to automatically free up the view if you're low on memory: the low memory warning. It will only destroy and recreate the view if you actually need to do so, and it's built in to the system so you don't have to do anything. Remember the saying about premature optimization?