Unable to pushViewController for subview - iphone

I have a UINavigationController and I have seperate UIViews that I switch between using a UISegmentControl. On switching the views, I add the view as a subview to my navigation controller's view:
[self.view addSubview:segmentTab1.view];
and
[self.view addSubview:segmentTab2.view];
Then, in the subViews, each has a UITableView, but my issue is, that I am unable to push a new viewController into view in the didSelectRowAtIndexPath method.
The method is called correctly and by setting breakpoints, I can see the method for pushing the view gets called as well, but nothing happens. This is my code for pushing it:
[self.navigationController pushViewController:detailsViewController animated:YES];
I also tried
[super.navigationController pushViewController:detailsViewController animated:YES];
What am I doing wrong - or is is just not possible to do it with a subview?

When you call -pushViewController, which view controller is self? If you are calling that from within one of your tab subviews, self likely doesn't have a reference to the navigation controller from the top level view that you added it to. You can verify this by getting the memory address of the navigation controller from within the top level view and comparing it to what you have in the subview. Obviously if it's nil or doesn't match, then that's your problem.
You only get a navigation controller "for free" when it's been added to the navigation stack itself with -pushViewController which your subviews haven't been added that way.

I had a similar issue when implementing a common header for all the views
After many tries , i have fixed it by this -
In all the viewController
- (void)viewWillAppear:(BOOL)animated {
[self.view addSubview:[[(NavAppAppDelegate *)[[UIApplication sharedApplication] delegate] headerview] view]];
[[(NavAppAppDelegate *)[[UIApplication sharedApplication] delegate] headerview] viewWillAppear:YES];
}
I have referred following post to implement the header view
Common XIB in multiple View Controller in iPhone
[self.view addSubview:[[(NavAppAppDelegate *)[[UIApplication sharedApplication] delegate] headerview] view]]; // line will load the header subview
[[(NavAppAppDelegate *)[[UIApplication sharedApplication] delegate] headerview] viewWillAppear:YES]; // this is to call the viewWillAppear method of HeaderController where we can write code to check the user is logged or not and change the login button to logout button etc ..

Instead of
[self.view addSubview:segmentTab1.view];
and
[self.view addSubview:segmentTab2.view];
you may use
[self pushViewController: segmentTab1 animated: NO];
and
[self pushViewController: segmentTab2 animated: NO];
to add your viewControllers to the navigation hierarchy, and make [super.navigationController pushViewController:detailsViewController animated:YES]; work.

It is not entirely clear to me what your view hierarchy is, but in general if your navigation controllers view is not the first subview of a window or an element of one of Apple's collection views (either another navigation view controller's content view or a tab controller's content view) it won't work correctly.

One possibility, if you are not averse to singletons, is to make your product's UINavigationController object be a singleton, accessible from (for example) your application delegate.
You would invoke it thus:
[[((MyApplicationDelegate*)[UIApplication delegate]) navController]
pushViewController: viewControllerToPush animated: YES];
where within MyApplicationDelegate, navController returns the singleton object.

LogEntryDetailViewController *leController = [[LogEntryDetailViewController alloc] initWithNibName:#"LogEntryDetailView" bundle:[NSBundle mainBundle]];
[leController setTitle:[NSString stringWithFormat:#"%#", [logEntriesArray objectAtIndex:row]]];
[[self navigationController] pushViewController:leController animated:NO];
[leController release], leController = nil;

Related

Dismiss ModalViewController from subview

I have a problem with the function call:
[self dismissModalViewControllerAnimated:YES];
In MainViewController, I can launch a image picker and dismiss as usual by clicking the cancel button.
(IBAction) LaunchInMain:(id)sender{
MainAppDelegate *app = (MainAppDelegate *)[[UIApplication sharedApplication] delegate];
//elcPicker is a customized image picker
[app.viewController presentModalViewController:elcPicker animated:YES];
[elcPicker release];
[albumController release];
- (void)elcImagePickerControllerDidCancel:(ELCImagePickerController *)picker {
[self dismissModalViewControllerAnimated:YES];
}
Now, instead of launching it direct it in Main, I add a subview first and launch the image picker from the subview using the same launch method.
Problem:
The image picker cannot be dismissed and the subview cannot be shown again. So the screen will remain at the image picker no matter what I click.
I have been trying with some other calls like without any success:
[self dismissModalViewControllerAnimated:YES];
I am happy with any help or idea. If you think more information should be provided, I can add more codes.
May be try
[app.viewController dismissModalViewControllerAnimated:YES];
Hope this helps.
This may work for you:
[self.view dismissModalViewControllerAnimated:YES];
This works if you are presenting a modal view from a UISplitViewController. It can also be applied in so many other ways...
First, create an instance in your .h file for your appDelegate, (AppDelegate_iPad *appDelegate) then put this in your viewDidLoad or comparable method:
ipadDelegate = (AppDelegate_iPad *)[[UIApplication sharedApplication] delegate];
Now, present the first modal view like this:
YOURVC *vc = [[YOURVC alloc] initWithNibName:#"YOURVC" bundle:nil];
[ipadDelegate.splitViewController presentModalViewController:vc animated:YES];
[vc release];
Say you have a subview, like a UITableView, and want to dismiss the modal from the didSelectRowAtIndexPath. All you have to do to dismiss your modal with a subview is create another ipadDelegate instance inside your subview's .h (if needed), reference the [[UIApplication sharedApplication] delegate] again, and dismiss:
[ipadAppDelegate.splitViewController dismissModalViewControllerAnimated:YES];
Essentially, as long-winded as it may be, use your appDelegate's controller to present and dismiss the the modal if you need to maintain a persistent reference to the presentingViewController...because all the things above just don't work in my case.
If you're presenting with your ipadDelegate, make sure you check the mode of presentation in your MainWindow_iPad.xib. Your "Transition Style" should be "Cover Vertical" and "Presentation" should be "Current Context" or your modal may present behind other views.

Animate change of view controllers without using navigation controller stack, subviews or modal controllers?

NavigationControllers have ViewController stacks to manage, and limited animation transitions.
Adding a view controller as a sub-view to an existing view controller requires passing events to the sub-view controller, which is a pain to manage, loaded with little annoyances and in general feels like a bad hack when implementing (Apple also recommends against doing this).
Presenting a modal view controller again places a view controller on top of another, and while it doesn't have the event passing problems described above, it doesn't really 'swap' the view controller, it stacks it.
Storyboards are limited to iOS 5, and are almost ideal, but cannot be used in all projects.
Can someone present a SOLID CODE EXAMPLE on a way to change view controllers without the above limitations and allows for animated transitions between them?
A close example, but no animation:
How to use multiple iOS custom view controllers without a navigation controller
Edit: Nav Controller use is fine, but there needs to be animated transition styles (not simply the slide effects) the view controller being shown needs to be swapped completely (not stacked). If the second view controller must remove another view controller from the stack, then it's not encapsulated enough.
Edit 2: iOS 4 should be the base OS for this question, I should have clarified that when mentioning storyboards (above).
EDIT: New answer that works in any orientation.
The original answer only works when the interface is in portrait orientation. This is b/c view transition animations that replace a view w/ a different view must occur with views at least a level below the first view added to the window (e.g. window.rootViewController.view.anotherView).
I've implemented a simple container class I called TransitionController. You can find it at https://gist.github.com/1394947.
As an aside, I prefer the implementation in a separate class b/c it's easier to reuse. If you don't want that, you could simply implement the same logic directly in your app delegate eliminating the need for the TransitionController class. The logic you'd need would be the same however.
Use it as follows:
In your app delegate
// add a property for the TransitionController
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
MyViewController *vc = [[MyViewContoller alloc] init...];
self.transitionController = [[TransitionController alloc] initWithViewController:vc];
self.window.rootViewController = self.transitionController;
[self.window makeKeyAndVisible];
return YES;
}
To transition to a new view controller from any view controller
- (IBAction)flipToView
{
anotherViewController *vc = [[AnotherViewController alloc] init...];
MyAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate.transitionController transitionToViewController:vc withOptions:UIViewAnimationOptionTransitionFlipFromRight];
}
EDIT: Original Answer below - only works for portait orientation
I made the following assumptions for this example:
You have a view controller assigned as the rootViewController of your window
When you switch to a new view you want to replace the current viewController with the viewController owning the new view. At any time, only the current viewController is alive (e.g. alloc'ed).
The code can be easily modified to work differently, the key point is the animated transition and the single view controller. Make sure you don't retain a view controller anywhere outside of assigning it to window.rootViewController.
Code to animate transition in app delegate
- (void)transitionToViewController:(UIViewController *)viewController
withTransition:(UIViewAnimationOptions)transition
{
[UIView transitionFromView:self.window.rootViewController.view
toView:viewController.view
duration:0.65f
options:transition
completion:^(BOOL finished){
self.window.rootViewController = viewController;
}];
}
Example use in a view controller
- (IBAction)flipToNextView
{
AnotherViewController *anotherVC = [[AnotherVC alloc] init...];
MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
[appDelegate transitionToViewController:anotherVC
withTransition:UIViewAnimationOptionTransitionFlipFromRight];
}
You can use Apple's new viewController containment system. For more in-depth information check out the WWDC 2011 session video "Implementing UIViewController Containment".
New to iOS5, UIViewController Containment allows you to have a parent viewController and a number of child viewControllers that are contained within it. This is how the UISplitViewController works. Doing this you can stack view controllers in a parent, but for your particular application you are just using the parent to manage the transition from one visible viewController to another. This is the Apple approved way of doing things and animating from one child viewController is painless. Plus you get to use all the various different UIViewAnimationOption transitions!
Also, with UIViewContainment, you do not have to worry, unless you want to, about the messiness of managing the child viewControllers during orientation events. You can simply use the following to make sure your parentViewController forwards rotation events to the child viewControllers.
- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{
return YES;
}
You can do the following or similar in your parent's viewDidLoad method to setup the first childViewController:
[self addChildViewController:self.currentViewController];
[self.view addSubview:self.currentViewController.view];
[self.currentViewController didMoveToParentViewController:self];
[self.currentViewController.swapViewControllerButton setTitle:#"Swap" forState:UIControlStateNormal];
then when you need to change the child viewController, you call something along the lines of the following within the parent viewController:
-(void)swapViewControllers:(childViewController *)addChildViewController:aNewViewController{
[self addChildViewController:aNewViewController];
__weak __block ViewController *weakSelf=self;
[self transitionFromViewController:self.currentViewController
toViewController:aNewViewController
duration:1.0
options:UIViewAnimationOptionTransitionCurlUp
animations:nil
completion:^(BOOL finished) {
[aNewViewController didMoveToParentViewController:weakSelf];
[weakSelf.currentViewController willMoveToParentViewController:nil];
[weakSelf.currentViewController removeFromParentViewController];
weakSelf.currentViewController=[aNewViewController autorelease];
}];
}
I posted a full example project here: https://github.com/toolmanGitHub/stackedViewControllers. This other project shows how to use UIViewController Containment on some various input viewController types that do not take up the whole screen.
Good luck
OK, I know the question says without using a navigation controller, but no reason not to. OP wasn't responding to comments in time for me to go to sleep. Don't vote me down. :)
Here's how to pop the current view controller and flip to a new view controller using a navigation controller:
UINavigationController *myNavigationController = self.navigationController;
[[self retain] autorelease];
[myNavigationController popViewControllerAnimated:NO];
PreferencesViewController *controller = [[PreferencesViewController alloc] initWithNibName:nil bundle:nil];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.65];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:myNavigationController.view cache:YES];
[myNavigationController pushViewController:controller animated:NO];
[UIView commitAnimations];
[controller release];
Since I just happened across this exact problem, and tried variations on all the pre-existing answers to limited success, I'll post how I eventually solved it:
As described in this post on custom segues, it's actually really easy to make custom segues. They are also super easy to hook up in Interface Builder, they keep relationships in IB visible, and they don't require much support by the segue's source/destination view controllers.
The post linked above provides iOS 4 code to replace the current top view controller on the navigationController stack with a new one using a slide-in-from-top animation.
In my case, I wanted a similar replace segue to happen, but with a FlipFromLeft transition. I also only needed support for iOS 5+. Code:
From RAFlipReplaceSegue.h:
#import <UIKit/UIKit.h>
#interface RAFlipReplaceSegue : UIStoryboardSegue
#end
From RAFlipReplaceSegue.m:
#import "RAFlipReplaceSegue.h"
#implementation RAFlipReplaceSegue
-(void) perform
{
UIViewController *destVC = self.destinationViewController;
UIViewController *sourceVC = self.sourceViewController;
[destVC viewWillAppear:YES];
destVC.view.frame = sourceVC.view.frame;
[UIView transitionFromView:sourceVC.view
toView:destVC.view
duration:0.7
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished)
{
[destVC viewDidAppear:YES];
UINavigationController *nav = sourceVC.navigationController;
[nav popViewControllerAnimated:NO];
[nav pushViewController:destVC animated:NO];
}
];
}
#end
Now, control-drag to set up any other kind of segue, then make it a Custom segue, and type in the name of the custom segue class, et voilĂ !
I struggled with this one for a long time, and one of my issues is listed here, I'm not sure if you have had that problem. But here's what I would recommend if it must work with iOS 4.
Firstly, create a new NavigationController class. This is where we'll do all the dirty work--other classes will be able to "cleanly" call instance methods like pushViewController: and such. In your .h:
#interface NavigationController : UIViewController {
NSMutableArray *childViewControllers;
UIViewController *currentViewController;
}
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion;
- (void)addChildViewController:(UIViewController *)childController;
- (void)removeChildViewController:(UIViewController *)childController;
The child view controllers array will serve as a store for all the view controllers in our stack. We would automatically forward all rotation and resizing code from the NavigationController's view to the currentController.
Now, in our implementation:
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
currentViewController = [toViewController retain];
// Put any auto- and manual-resizing handling code here
[UIView animateWithDuration:duration animations:animations completion:completion];
[fromViewController.view removeFromSuperview];
}
- (void)addChildViewController:(UIViewController *)childController {
[childViewControllers addObject:childController];
}
- (void)removeChildViewController:(UIViewController *)childController {
[childViewControllers removeObject:childController];
}
Now you can implement your own custom pushViewController:, popViewController and such, using these method calls.
Good luck, and I hope this helps!
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController *viewController = (UINavigationController *)[storyboard instantiateViewControllerWithIdentifier:#"storyBoardIdentifier"];
viewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentViewController:viewController animated:YES completion:nil];
Try This Code.
This code gives Transition from a view controller to another view controller which having a navigation controller.

Accessing a Top Navigation Controller from a Subview Navigation Controller

I have a my views and controllers set up like so.
A Tab/Bar controller
Within 1. is a root view controller
within 2. is a programmatically created navigation controller, that is displayed as a subview in the root view controller.
What I am trying to do is access the top tab bar/navigation controller so that i can push a view onto it.
I tried parentViewController but all it did was push the view onto the programmed nav controller.
any suggestions?
This is how i set up my root view controller:
-(void)viewDidAppear:(BOOL)animated{
NSLog(#"ROOT APPEARED");
[super viewDidAppear:animated];
WorklistViewController *worklistController = [[WorklistViewController alloc]initWithNibName:#"WorklistView" bundle:[NSBundle mainBundle]];
UINavigationController *worklistNavController = [[UINavigationController alloc] initWithRootViewController:worklistController];
worklistNavController.navigationBar.barStyle = UIBarStyleBlackOpaque;
worklistNavController.view.frame = watchlistView.frame;
[worklistNavController.topViewController viewDidLoad];
[worklistNavController.topViewController viewWillAppear:YES];
[self.view addSubview:worklistNavController.view];
GetAlertRequestViewController *alertsController = [[GetAlertRequestViewController alloc]initWithNibName:#"AlertsView" bundle:[NSBundle mainBundle]];
UINavigationController *alertsNavController = [[UINavigationController alloc] initWithRootViewController:alertsController];
alertsNavController.navigationBar.barStyle = UIBarStyleBlackOpaque;
alertsNavController.view.frame = alertsView.frame;
[alertsNavController.topViewController viewDidLoad];
[alertsNavController.topViewController viewWillAppear:YES];
[self.view addSubview:alertsNavController.view];
}
A nested ViewController (ie, inside a view controlled by a ViewController that's actually on the NavController stack) doesn't have direct access to the UINavigationController that its parent's view's controller is a stack member of. That's one MOUTHFUL of a sentence, but the sense of it is: you can't get there from here.
Instead you've got to get at the app's NavController via the App delegate.
YourAppDelegate *del = (YourAppDelegate *)[UIApplication sharedApplication].delegate;
[del.navigationController pushViewController:nextViewController animated:YES];
You're using your UIApplication's singleton (contains all sorts of good info about your app), which has a .delegate property pointing to the AppDelegate, and that contains a reference to the NavigationController.
This is how the "Navigation-based Application" Xcode template sets up NavController ownership, anyway. YMMV if you rolled your own--though if you did, you probably wouldn't need to ask this question.
You can use the follow instruccion:
[(UINavigationController *)self.view.window.rootViewController pushViewController:vc animated:YES];
It works for me :D
Have a look at UIViewController's navigationController and tabBarController properties. These will return the corresponding navigationController or tabBarController that the given UIViewController 'belongs' to.
So you can do something like:
[customController.navigationController pushViewController:newController animated:YES];
// Similarly for tabBarController ...

In iPhone development how to access a method in the ParentViewController

I am a newbie iPhone Programmer and have a question regarding how to access methods of a Parent View Controller.
In my program when the program first loads (applicationDidFinishLaunching) I do the following code:
[window addSubview:rootViewController.view];
[window makeKeyAndVisible];
which basically calls this
- (void)viewDidLoad {
HomeViewController *homeController=[[HomeViewController alloc] initWithNibName:#"HomeView" bundle:nil];
self.homeViewController=homeController;
[self.view insertSubview:homeController.view atIndex:0];
[homeController release];
[super viewDidLoad];
}
Now, I have an IBAction call on HomeViewController that I want to have it call a method in root View Controller
I want to call this method
- (void)loadNewGame
{
self.questionViewController = [[QuestionViewController alloc] initWithNibName:#"QuestionView" bundle:nil];
//[homeViewController.view removeFromSuperview];
[self.view insertSubview:questionViewController.view atIndex:0];
}
So my question is how do I call a method from the Parent View controller?
I've tried
[self.view removeFromSuperview];
[self.parentViewController loadNewGame];
but that doesn't seem to work. Could someone please ploint me in the right direction.
Thanks in advance
Scott
First off, typically you call [super viewDidLoad] first in your viewDidLoad.
You will have to have an instance variable in your homeController class for your rootViewController. Then you could have a method in homeController:
- (void) loadNewGame
{
[self.rootViewController loadNewGame];
}
This is one of many different ways to accomplish this. You may want to move the method to homeController completely. Or you may wish to have IB use the rootViewControllers' methods directly...
Here is another discussion of this.
First off your code doesn't really make sense. Why are you adding your HomeViewController in a -viewDidLoad call. If you want to load the HomeViewController as the initial view, you should set that in Interface Builder instead of RootViewController. When you want to display a new view controller, you should be using a navigation controller stack and pushing the new view controller onto it with [[self navigationController] pushViewController:newViewController animated:YES].
Assuming you get that sorted, you should create a delegate (id) field for your child view controller that you can set when you instantiate the new view controller. So your code might look something like this:
HomeViewController *homeController=[[HomeViewController alloc]
initWithNibName:#"HomeView" bundle:nil];
[homeController setDelegate:self];
[[self navigationController] pushViewController:homeController animated:YES];
[homeController release];
Then, when your action gets fired in the HomeViewController, you can check to see if the delegate is set and if so, call the selector in question, like this:
- (IBAction)action:(id)sender;
{
if (delegate && [delegate respondsToSelector:#selector(loadNewGame)])
[delegate performSelector:#selector(loadNewGame)];
}
You might want to read Apple's docs on how to use the navigation controller stack. This might help clarify some things.

UIView disappears after modalViewController is removed from superview

In my app I have a tabBarController and in it a navigationController. One of my view controllers is a TableViewController and under the navigationBar i added a uiView as a subview to the view like this:
rectangleInfo = [[UIView alloc] initWithFrame:CGRectMake(0,0,[[UIScreen mainScreen] applicationFrame].size.width,26)]; rectangleInfo.autoresizingMask = (UIViewAutoresizingFlexibleWidth); rectangleInfo.backgroundColor = [UIColor darkGrayColor]; [self.view addSubview: rectangleInfo];
when I click on a cell in the tableView I push an UIViewController like this:
[feedViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[[self navigationController] presentModalViewController:feedViewController animated:YES];
After i pop the modal view for a couple of times with it from the tableViewNavigationController disappears the rectangleInfo UIView.
I pop my modalview like this:
[[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
[self setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self dismissModalViewControllerAnimated:YES];
any idea why that subview (rectangleInfo) of the tableViewController dissapears after i remove the modal view from the superview?
thank you in advance.
I'm curious as to what you are trying to do with your rectangleInfo view? By the size of it, it looks like you are trying to mimic the status bar. Why? You can just hide the status bar if you want.
Another thing you can try is to create the UIView visually in Interface Builder. Don't actually add it to your main view, but create it as a separate UIView in the XIB with the appropriate size, etc. Then create an outlet for it in Xcode and connect it. Next, add it as a subview in code when your view controller loads. See if that makes a difference. This is especially strange since you say it only disappears after popping it several times. Do you get the same problem if you push and pop the non-modal way, e.g.:
[[self navigationController] pushViewController:feedViewController animated:YES];
[[self navigationController] popViewControllerAnimated:YES];
i solved the problem by implementing correctly the viewDidLoad and viewDidUnload methods for creating and releasing my subviews.