Ola Folks,
In an iPhone application I am displaying different views by using the addSubView:SomeViewController.view method.
I want to be able to, at the very least, log the view controllers that are in the view hierarchy that is being displayed. I would prefer to be able to get a handle to a specific view controller.
I know how to iterate the views, I just do not see how to access the view controllers of those views. I am looking for something that will give me the type of access to the view controllers that UINavigationController::ViewControllers does.
I thought I could get away with:
for (UIViewController* oVC in [self.view subviews])
but this is not having the intended effect.
If someone has a way of doing this, please share it with me.
-isdi-
A view doesn't keep a reference to its view controller (or know anything about view controllers in general), so you'll have to keep track of that mapping yourself. If you keep all of your view controllers in an array viewControllers, you could do something like:
- (UIViewController *) viewControllerForView:(UIView *)view {
for (UIViewController *viewController in viewControllers)
if (viewController.view == view)
return viewController;
return nil;
}
The standard way for a view to interact with the view controller that owns it is by having the view controller set as the delegate or action target of the view. The view is designed not to have any details about the delegate or action target.
If you have implemented your own view, just add a member to hold a reference to the view controller. Or adopt a delegate model for the view so that it does not matter what class the delegate is.
If you are treating the views as a stack and want to maintain a stack of view controllers along side it, similar to what UINavigationController does for you, you must do so manually. Couple every call of addSubview:viewController.view with a call to [myViewControllerArray addObject:viewController] and remove the viewController form the array when the view is removed from the view hierarchy.
Related
Can I load a UiViewController in a UIView in another UIViewcontrller.
suppose UIViewControllerA has a UIView named subuiview. Can I load UIViewControllerB into subbuiview?
Thanks!
Starting with iOS 5
"Container view controllers" have been added in iOS 5. You can add a view controller as a child of another one with addChildViewController:.
You also are responsible for adding its view to its parent's view.
Everything is well documented in the iOS SDK documentation: Implementing a Custom Container View Controller.
To add a child view controller:
childViewController.frame = ...
[self.view addSubview:childViewController.view];
[self addChildViewController:childViewController];
[childViewController didMoveToParentViewController:self];
and to remove it:
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
Prior to iOS 5
It's possible to load another view controller and add its view as a subview of another controller's view.
UIViewController *subController = ...
[self.view addSubview:subController.view];
Although it's not recommended by Apple's guidelines:
Each custom view controller object you
create is responsible for managing all
of the views in a single view
hierarchy. [...] The one-to-one
correspondence between a view
controller and the views in its view
hierarchy is the key design
consideration. You should not use
multiple custom view controllers to
manage different portions of the same
view hierarchy.
(from the View Controller Programming Guide)
Your sub-controller won't receive rotation events, or viewWillAppear, viewWillDisappear, etc (except viewDidLoad).
So Apple advises us to use a single view controller managing the entire view hierarchy (but doesn't forbid to use multiple ones).
Each view may still be a custom subclass of UIView. Maybe you don't need another view controller but rather a custom view.
[self addSubview:viewControllerB.view];
try this in the sub view
It has always been problematic to simply use addSubview to add a view controller's view as a subview of another's. It's especially bad when people use it to transition between views, rather than relying upon other, more robust, solutions like presentViewController or pushViewController.
If you really want to add one view controller's view as a subview of another's, iOS5 introduced "view controller containment". Containment is discussed in the View Controller Programming Guide as well as WWDC 2011 session 102. Bottom line, you want to ensure you keep your view controller hierarchy synchronized with your view hierarchy, by calls to addChildViewController, didMoveToParentViewController, etc. See the documentation and the video for specifics.
Well you cant technically load a viewcontroller. You can load a viewcontroller's view as a subview of any view.
You need to also retain the view controller incase you have any actions attached to it. Otherwise it may result in crashes.
addsubview:controller.view is the method you want to look at.
I'm getting confused on view controllers and would love a straight example. Here's the preamble:
I have a UIViewController with a matching .xib.
By default IB gives me a single View in the Document window.
I can make it appear by telling my UIWindow to addSubview:controller.view and bringSubviewToFront:controller.view
Here's the questions:
Should I add another View to the ViewController in IB? Or is there a better, programmatical way?
How do I tell the ViewController to switch between the Views?
From the ViewController downward, what does the code look like to achieve this?
I'm trying things but just making a mess so I thought I'd stop and ask...
Note that every button, label, image, etc. in your main view controller is actually a view in itself, however I've interpreted your question to mean that you want to manage multiple full-screen views or "screens". Each screen should have its own view controller to manage it. So to get the terminology right, a view-controller is an object that manages a single full-screen view (or almost full screen if it's nested inside a navigation controller or tab bar controller for example) and a view is the big area managed by the view controller as well as all the sub-views (images, buttons, labels, etc.) within it (they are all UIView sub-classes). The view controller manages all of them on that screen, if you want another screen/page then you should create a new view controller to manage it.
The root view controller (the one you add to the window) can be a plain old normal view controller that you've designed in IB, however it's probably more useful if you use a navigation controller or a tab bar controller and add your designed view controller to that - then you can push additional view controllers as needed.
Another way (if you don't want navigation or tab-bar style) would be to transition to other view controllers directly in the main window using whatever transitions you like (or just replace the old one). We'll leave that for now though.
Any sub-views of your main view controller (the one you've designed in IB) will be automatically loaded from the nib file, but you can also add your own views programatically if you want (typically you would use one or the other, i.e. nibs or programatically, but you can mix and match if you want). To do it programatically, override loadView in the view controller and then call [super loadView]; then do [self.view addSubView:myOtherView]; (create the myOtherView first of course). Note that the first time .view is accessed on your view controller, it actually calls loadView to create the view, so inside loadView it's important to call [super loadView]; before trying to access self.view :D
To switch between views, using the navigation or tab bar controllers makes it very easy. So put your main view controller inside (for example) a navigation controller and put the navigation controller in the window, so you've got window->navigationController->myController. Then from an action method in your view controller (you can hook up the action methods in IB), for example when an "about" button is pressed do this:
- (void)doAbout
{
// Create the about view controller
AboutViewController* aboutVC = [AboutViewController new];
// Push the view controller onto the navigation stack
[self.navigationController pushViewController:aboutVC animated:YES];
[aboutVC release];
}
Note that the about view controller is created programatically here - if your about view is designed in IB then instead use initWithNibName:bundle: to create it.
And that's how you manage multiple screens.
I've been reading the Head First iPhone Development book and I understand how to get to a new view from a table but how exactly would I be able to get to a new view or view controller, by just simply pressing a button? Is that even possible?
I mean there are some apps where you click a button, not a table cell and it loads a new view. How exactly is that done? If someone could help out a newbie it would be greatly appreciated!
I think what you're looking for is a modal vew controller. THis presents a modal view like you described on top of everything else. If rootViewController is the view controller that is displaying your current view, and myNewViewController the view controller you want to display modally:
[rootViewController presentModalViewController:myNewViewController animated:YES];
There's plenty of examples of this kind of thing on the net, just search for presentModalViewController
Like bpapa said in the comments, it's hard to be specific without code. However, generally what you want to do is:
Build a navigation controller that contains one original view.
Create a button in your original view using the Interface Builder.
Build a callback method (usually defined with IBAction) that is run when the button is pushed.
In that callback method, create a new view and push it onto the navigation controller the same way you would using a table view cell.
Alternately, if you only want one level of hierarchy, you could use a modal view controller; instead of pushing onto the navigation controller in the last step, just present the modal view controller.
The general answer is that you have an object that manages which view controller loads when.
The most commonly used is the UINavigationController. It is a UIViewController that instead of controlling views, controls other view controllers. It works like a simple stack. You push views you want to display onto the nav's controller stack and when you want them to disappear you pop them off.
A common (though sloppy) way of using a nav is to make it a property of your app delegate. Then anywhere in your app you can references it by:
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
The view controller for the first the user sees is held in the nav's topViewController property. If you want to load a view based on a user action in the topViewController.view, you would have something like this:
- (IBAction) loadNextView:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
UIViewController *nextViewController=...// load from nib, connect with IBOutlet, create programmatically
[nav pushViewController:nextView animated:YES];
}
The first view disappears to be replaced by the next one. To return to the first view, you have a method in the next view controller like so:
- (IBAction) unloadSelf:(id) sender{ // Action called by a a UI event such as a button press.
UINavigationController *nav=[[[UIApplication sharedApplication] delegate] navigationController];
[nav popViewControllerAnimated:YES];
}
... and the nav returns you automatically to the previous view regardless of what that view was.
When you first start out, especially if you use Interface Builder, the structure of the app is largely hidden. Behind the scenes all view controllers and their views exist in a hierarchy of some kind that leads back up to the app delegate. You should train yourself to think in hierarchal terms even if it is not immediately obvious how that hierarchy is constructed.
I am sure this is an easy question, but one that has escaped me for some time now.
Say I have a UIViewController, either defined as a root in an XIB or on a stack. At some point in my code I want to replace it with another view controller. Just flat out replace it. How would I do that?
I have tried defining the controller and assigning, but not sure what actually makes it push on the screen with the absence of a navigation controller.
I think when you say that you want to replace the view controller, what you actually mean is that you want to replace the view. Bear in mind that view controllers aren't visible, but every view controller maps to a view, which can become visible by getting added as a subview of a visible view.
Your solution of replacing self.view with the new view controller's view may work in your particular case, but it's probably not the "correct" answer to your question. There are going to be cases where this solution won't work for you.
Let's say you have a simple view based application with no navigation controller and no tab bar controller. In your app delegate you construct an instance of YourFirstViewController, and you call [window addSubview:yourFirstController];. Your view hierarchy now consists of a UIWindow with a single subview -- the view for YourFirstViewController.
Now let's say the user presses a button on that view, which is handled by an IBAction defined in YourFirstViewController. You want to respond by "replacing" YourFirstViewController's view with a view associated with YourSecondViewController. I put "replacing" in quotes because we more commonly present a view by pushing its view controller onto a navigation stack, or calling presentModalViewController:animated: to present the view modally, but let's assume that you've rejected those options for some reason, and you actually do want to manually replace YourFirstViewController's view with YourSecondViewController's view.
This is a simple matter of manipulating the view hierarchy. You want to remove YourFirstViewController's view from its superview (the UIWindow in this case), and you want to add YourSecondViewController's view as a subview to replace it. Your action would therefore look something like this:
- (IBAction)replaceButtonClicked {
UIView *mySuperview = self.view.superview;
YourSecondViewController *secondController = [[YourSecondViewController alloc] init];
[mySuperview addSubview:secondController.view];
[self.view removeFromSuperview];
[secondController release];
}
When we use a methods like -pushViewController:animated: or -presentModalViewController, the receiving controller manipulates the view hierarchy for us. This may make it seem like we're looking at view controllers on the screen, but we're not. We're just looking at a big hierarchy of nested views going all the way up to a UIWindow at the top.
You can present a new view controller modally:
[self presentModalViewController:aViewController animated:YES];
This won't outright replace the current VC, but it will display a new view over the current view.
Fundamentally, what I want to do is within, for example, ViewControllerA display ViewControllerB and ViewControllerC. How would I do that?
Thanks in advance.
You don't display view controllers, you display views. Having said that, you can do something like this:
UIViewController *a = ...;
UIViewController *b = ...;
[a.view addSubview:b.view];
Now, having said that, you shouldn't do it. Tons of stuff does not behave properly, because there are tons of undocumented interactions between UIView, UIWindow, and UIViewController. There is nothing in the documentation that says it won't work, but random things stop behaving properly (viewWillAppear: on the interior view's VC doesn't get called, etc).
If you need this functionality, you should file a bug with Apple.
The default template for a navigation view controller should do what you want assuming you want two different screens (not two different sections on the same screen). Whenever you want to change the view from the current one to another, just tell the navigation controller to push it on the stack:
[self.navigationController pushViewController:viewBoards animated:YES];
The default navigation view controller gives you a root view controller with a navigation view controller in it. It also gives you one view controller called MainWindow. Just add as many copies of MainWindow as you need to get your functionality.