I have a tabbar -> navigationcontroller structure. In one of these tabs, I want to switch between two UIViewControllers (a KalViewController and a UITableViewController to be be exact), using a UISegmentedControl located in the Navigation Bar.
Currently, I have a third UIViewController, that pops and pushes the appropriate ViewControllers on segment value change. I don't think thats the right way to do it and it also destroys the navigation stack (when I tap on the bar item, the navigation controller goes the root controller, which won't work). And there's even another bug, related to the Kal Component.
So, what's the right way to do it?
The right way to do it is to have the controller handling the UISegmentedControl add the views of the controllers as subviews.
[self.view addSubview:controller.view];
It's your responsibility to send viewWillAppear: and so on.
EDIT: The offset you're talking about can be adjusted using:
controller.view.frame = CGRectMake(x, y, width, height);
EDIT 2: In response to tc.'s comment:
From the documentation of UISplitViewController:
Message Forwarding to Its Child View Controllers
A split view controller interposes itself between the application’s window and its child view controllers. As a result, all messages to the visible view controllers must flow through the split view controller. This works generally as you might expect and the flow of messages should be relatively intuitive. For example, view appearance and disappearance messages are sent only when the corresponding child view controller actually appears on screen. Thus, when a split view controller is first displayed in a portrait orientation, it calls the viewWillAppear: and viewDidAppear: methods of only the view controller that is shown initially. The view controller that is presented using a popover does not receive those messages until the popover is shown or until the split view controller rotates to a landscape orientation.
This is not magical and there is no reason why you wouldn't be able to write a similar controller yourself. In fact I've done it and it worked just fine.
Related
Given the below code
self.view.backgroundColor = [UIColor yellowColor];
MyViewController *myVC = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil]
myVC.view.backgroundColor = [UIColor clearColor];
myVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:myVC animated:NO completion:nil];
What happens under the hood when we call presentViewController ? When myVC is visible I cannot see yellow color, then I checked myVC.view.superView in it's viewDidAppear method and it is UIWindow.
Q1. Is that mean until the modal window is up presentingViewController.view (self.view in above case) is removed from the View hierarchy and presentedViewController.view (myVC.view in above case) is added over UIWindow ?
Q2. What will be the case if myVC.modalPresentationStyle != UIModalPresentationFullScreen ?
Q3. Does iOS also remove all the views from UIWindow except presentedViewController.view until the full screen modal dialog is up for optimization ? If NO why not ?
First, let's discuss the case without animation.
Before calling present:
Your window has one view hierarchy, starting from its rootViewController view.
After calling present:
The view hierarchy still exists without change.
A special full-screen view called "dimming view" is added to the window (that is, not inside the rootViewController's view but inside the window (window is a UIView, too). This view is transparent, dims the presenting controler and blocks user interaction.
The presented (modal) controller's view is then added also to the window.
There are some other views added in between the window and the presented controller's window. If you log your view hierarchy, you'll see classes named _ControllerWrapperView or something similar. However, this has changed between iOS versions and you shouldn't rely on the view structure.
Note that that the modal controller can't ever be transparent because it is not direct subview of the window and the wrappers between the controller and the window are not transparent.
The animated case is almost the same. Only there are some fancy animations between the steps.
Edit 2:
The answer was really a bit incorrect. There is a big difference between iPhone and iPad presented controllers.
On iPhone, the presented controllers are always displayed full screen and the presenting controllers are actually removed from the window.
On iPad, if the presented controller is not fullscreen (see UIModalPresentationStyle), the presenting controller stays in the window.
Your questions:
Is that mean until the modal window is up presentingViewController.view (self.view in above case) is removed from the View hierarchy and presentedViewController.view (myVC.view in above case) is added over UIWindow ?
If the controller is full screen, then this claim is true. Otherwise, the presenting view controller stays there but the whole contents are overlapped by other views (even if they are semi-transparent). Also, there are always some views between the presented and the presenting controller views.
What will be the case if myVC.modalPresentationStyle != UIModalPresentationFullScreen ?
See the answer to the previous question - on iPhone, there would be no difference.
Does iOS also remove all the views from UIWindow except presentedViewController.view until the full screen modal dialog is up for optimization ? If NO why not ?
From my tests, only the presenting controller is removed from the window hierarchy. This is probably to optimize drawing performance. This is the only controller the system can safely remove. Removing any other view could cause problems (e.g. views that should be always visible).
Edit:
If you want to make a transparent controller, you can:
Add the view directly to your view hierarchy (either to the controller's view or to the window) with a transition animation (+[UIView transition...])
The same but also adding a child controller to your controller.
Im writing an application which the main view controller is a UIViewController. It has some icons in a grid and I want to dismiss (sliding down) this grid when one of the icons is clicked. This I've done already. The problem is: when the grid is dismisseed I want another View to come from the top of the screen. This view is in this same root view controller. But I want to display the content of other view controllers in this view. For example: I want this view to show a UINavigationController with a UITableView inside it, so the user can navigate through TableViews.
I'm doing this:
HorariosViewController *horarios = [[HorariosViewController alloc] init];
[vuashView addSubview:horarios.view];
HorariosViewController is a UINavigationViewController. It shows me only a blue NavigationBar and changes like self.navigationItem.title = #"Title" won't work.
Thanks!
You can show another view controller's views as subviews but their outlets and actions remain linked to their original view controller unless you write code to make new connections, so self.whatever shouldn't be expected to affect the other view controller's properties.
(Also, if HorariosViewController is a UINavigationController, it shouldn't be created as a UIViewController.)
One approach is to have the navigation controller already there, with the icon grid presented modally on top of it. (you can set the view up this way without animations, so the user doesn't see the navigation controller underneath).
Then, when it's time for the grid to go away, it can call dismissModalViewController on itself with animation.
I have an TabBar application with 4 tabs. All four tabs have navigation controllers. In the settings tab i have a table with a cell for "Feedback". When the cell is clicked a FeedBackView controller is pushed which contains a feedback form with a few fields. This has a textfield for Category. When the textfield is touched, a modal view controller (FeedBackModalView) is presented with a picker. In the viewDidLoad method of the FeedBackModalView controller I typed NSLog(#"%#", self.parentViewController). In the console it shows the parentViewController as TabBar controller. Why is that? Shouldn't it be showing the FeedBackView controller as the parentView since I'm presenting the modal view in that controller?
I hope i was clear.
Using presentModalViewController with UITabBarController has some issues, and I believe the internal behavior of the method has kept changing in recent SDK versions. The bottom line is, you are supposed to use the root view controller to modally present a view controller. In case you are using tab bar interface, that becomes the UITabBarController object.
In an old version of SDK, when I presented a modal view in a view controller inside a tab bar controller the modal view did not appear in full screen, which wasn't an expected or a documented behavior. Now a modal view seems to appear in full screen anywhere, and I wouldn't be surprised if [self presentModalViewController:animated:] method internally checks self and if it has non-nil parentViewController property, send the message to the parent view controller (which will explain your observation).
My memory is vague and perhaps somebody has to correct me. However, I still believe it's the straightforward thing to understand (and also maybe practice) presentModal... only works with the root view controller.
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 have an app that uses a UINavigationController as its main way of showing data.I want a Settings screen to pop up, but I also want to be able to push new views for Settings. From what I've found, I can't use a UIViewController do to this. How can I present a view by sliding it, and also have content pushed onto it?
You can display the view for your view controller subclass either by presenting it modally (slides from the bottom and takes over the whole screen) or pushing it onto the navigation stack with animation (slides from the right and keeps the navigation bar).
In either case, you control the content of the view using your view controller subclass. Typically you update labels and controls in the viewWillAppear method.