I am trying to push a view when a user clicks on an annotation, I have the following code in place:
- (void) mapView: (MKMapView *) mapView annotationView:(MKAnnotationView *) view calloutAccessoryControlTapped:(UIControl *) control
{
childController = [[NeighborProfileViewController alloc] initWithNibName:#"NeighborProfileViewController" bundle:nil];
childController.title = view.annotation.title;
childController.uid = [((NeighborMapAnnotation *)(view.annotation)) uid];
[self.navigationController pushViewController:childController animated:YES];
}
I know that the code executes inside this fragment as I tried to print out something and it did print. However, why isn't it changing views to the view that I already push?
Is this because this view is actually a subView of the main view, which actually has the navigation controller? If this is the case, then how do I get around this. Here's the code that loads the subView:
-(IBAction) toggleAction:(id) sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
if(self.navigationItem.rightBarButtonItem.title == #"List"){
self.navigationItem.rightBarButtonItem.title = #"Map";
[mapViewController.view removeFromSuperview];
}else {
[self.view addSubview:mapViewController.view];
[self.view sendSubviewToBack:self.view];
self.navigationItem.rightBarButtonItem.title = #"List";
}
[UIView commitAnimations];
}
in other words the calloutAccessoryControlTapped is inside the mapViewController
Your code is insufficient to answer the following questions:
Is your navigation controller initialized?
Even though you are calling self.navigationController, if it is nil, then nothing will happen.
Is the xib name spelled correctly? If you push a controller onto the navigation stack with an incorrect xib name, an error may not be thrown and your controller will not be pushed onto the stack.
EDIT:
You need to create an instance of a UINavigationController before you show your initial view. Here is an example of presenting a modal view:
YourViewController* rootViewController; /* however you are creating the initial view */;
UINavigationController navController =
[[UINavigationController alloc] initWithRootViewController:rootViewController];
[rootViewController release];
[self presentModalViewController:navController animated:YES];
[navController release];
your mapviewcontroller doesn't have a reference to the navigationcontroller. your main view controller does. so what you need to do is pass the reference to your navigation controller onto the maview controller, and push the view onto that. Let me know if you have any questions
Related
I am trying to make a custom login view popup like an alert view. I am simulating the alertview popup with the following function. This function is found in the viewDidload in mine loginViewController.m
-(void)initialDelayEnded {
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
self.view.alpha = 1.0;
[UIView animateWithDuration:1.0/1.5 animations:^{
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
}completion:^(BOOL complete){
[UIView animateWithDuration:1.0/2 animations:^{
self.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.9, 0.9);
}completion:^(BOOL complete){
[UIView animateWithDuration:1.0/2 animations:^{
self.view.transform = CGAffineTransformIdentity;
}];
}];
}];
}
- (void)viewDidLoad
{
[self initialDelayEnded];
[super viewDidLoad];
}
And I'm calling in my firstViewController my loginViewController in the following way.
LoginViewController *login = [[LoginViewController alloc]initWithNibName:#"LoginViewController" bundle:NULL];
[self presentViewController:login animated:YES completion:NULL];
But it crashes with the following error.
'UIViewControllerHierarchyInconsistency', reason: 'A view can only be associated with at most one view controller at a time! View <UIView: 0x8674bf0; frame = (0 20; 320 460); autoresize = W+H; layer = <CALayer: 0x8670620>> is associated with <LoginViewController: 0x868a7d0>. Clear this association before associating this view with <LoginViewController: 0x8451e70>.
Can somebody help me?
Thanks in advance !
Here:
[self presentViewController:login animated:YES completion:NULL];
you are presenting a viewController by Self which i guess is a viewcontroller itself.
instead you should use:
[self presentModalViewController:login animated:YES];
if you want to present your viewcontroller rather pushing it on the navigation stack.
In which class you are using this code.
Unless you have special reason, please keep [super viewDidLoad]; call to be done as early as possible within your local implementation. Meaning: call [self initialDelayEnded]; AFTER [super viewDidLoad];
Please make sure your .xib file named LoginViewController has only one File's Owner in Placeholders and NO ViewController object in Objects panels. And make sure File's Owners custom class is LoginViewController. Can you please upload the screenshot of your .xib, specifically showing Document Outline? It will be much easier to figure what could have been wrong
Did you check your LoginViewController xib? Is there a possibility you are mapping the same view with more than 1 view controllers there?
The error message implies that there are two instances of 'LogInViewController'. Are you using story boards? If so you could add the view controller to the main storyboard file (rather than a separate nib), give it a tag/identifier (but don't connect it to the other VCs). You can then grab the storyboard created instance and present that. Like so:
//Get the stroyboard
UIStoryBoard *mainStoryBoard = [UIStoryBoard storyboardWithName:<STORYBOARD_NAME> bundle:nil];
//Get the VC
LogInViewController *login = [mainStoryBoard instantiateViewControllerWithIdentifier:<VIEWCONTROLLER_TAG>];
//Present
[self presentModalViewController:login animated:YES];
Also, you should not try to present other view controllers in viewDidLoad:, it will not work. Move the code to present the new controller into viewDidAppear:
I solved my apps login problem in the following way >
Used Alert View to show up in AppDelegate on applicationDidBecomeActive
Add a UIView Controller with XIB to project named LoginSubView. Kept it blank and added Tag to it say 99
Loaded the LoginSubView controller before the alert box shows up.
AFLoginViewController *LoginSubView = [[AFLoginViewController alloc] init];
[_window addSubview:LoginSubView.view];
[_window makeKeyAndVisible];
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"This is an example alert!" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Login", nil];
alert.alertViewStyle = UIAlertViewStyleSecureTextInput;
[alert show];
If login was successful I removed the LoginSubView just before anything else with following lines
for (UIView *subView in _window.subviews)
{
if (subView.tag == 99)
{
[subView removeFromSuperview];
}
}
could also add the same subview on applicationDidEnterBackground as well. to avoid flicker the sensitive screen view when coming back to foreground next time
To put it simply, I've built an app which started without a Navigation Controller so everything was controlled through my ViewController. Since adding the Navigation Controller, I added a button to flip to my main Navigation Controller window from my ViewController. However, when I push the button, it reloads the same window with the Navigation Bar now across the top(I hid it in the original view). If I push the same button again, it then loads the proper view. What did I do wrong to cause this loop?
My button's code below
-(IBAction) btnSettings_Clicked: (id) sender {
self.navigationController.navigationBarHidden = FALSE;
[UIView beginAnimations:#"Flip View" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
MyAppDelegate* appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:[self.navigationController view] cache:YES];
[UIView commitAnimations];
[appDelegate.navigationController popViewControllerAnimated:NO]; }
If this code is in ViewController and, as you say, this controller is not pushed on a navigation controller, then the error is:
[self.navigationController view] ;
because there are no navigation controller for self. You have to replace it with:
[appDelegate.navigationController view] ;
As I asume that is the correct navigation controller to use.
And then, why are you popping the top view controller? If you want to remove self.view, then you only have to do it:
[self.view removeFromSuperview];
I believe it is this line
[self.navigationController view]
You should set the animation to the controller you wish to show and not to the current controller.
As you guess am still a newbie, getting my head around iphone development.
I am just trying out basic view loading on demand, which i cant get to work
I have an app, with 2 view controllers, each view controller connected a different nib file.
I am trying to switch between view manually; there is no navigation control involved.
How can i manually push the second view to the first view?
self.navigationController pushViewController wont work since there is no navigation controller.
How else can I push the second view on top of the first view and destroy the first view; and ofcourse vice versa?
I have done this in the first view's button action:
SecondView *sv=[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self.navigationController pushViewController:sv animated:YES];
obviously, it didn't work.
window addSubView didn't work either, because the first view controller is the root view controller (not sure i said that right). In other words, when i run the app, the first view is what I see with a button that is supposed to load the second view.
I have spent hours searching for a simple example, and I couldn't find any.
Any suggestions?
in the first view controller you need this:
- (IBAction)pushWithoutViewController:(id)selector {
NextNavigationController *page = [[NextNavigationController alloc] initWithNibName:NextNavigationController bundle:nil];
CGRect theFrame = page.view.frame;
theFrame.origin = CGPointMake(self.view.frame.size.width, 0);
page.view.frame = theFrame;
theFrame.origin = CGPointMake(0,0);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.8f];
page.view.frame = theFrame;
[UIView commitAnimations];
[self.view addSubview:page.view];
[page release];
}
and then link it to the button in nib. :)
try :
SecondView *sv=[[SecondView alloc] initWithNibName:#"SecondView" bundle:nil];
[self presentModalViewController:sv animated:YES];
IF you have first xib and you want to give navigation to another controller then you have to declare navigation in to appdelegate.m
write following code to app
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
self.window.rootViewController = navController;
then in ViewController.m
- (IBAction)NextButtonClicked:(id)sender
{
yourNextViewController *objyourNextViewController = [[yourNextViewController alloc] initWithNibName:#"yourNextViewController" bundle:nil];
[self.navigationController pushViewController:objStartUpViewController animated:TRUE];
}
I have a small problem with UINavigationController animation between two view.
My application build more than two view,
first view contains login information, second view contains root menu, last view contains sample data and so on..
My MainWindow.xib contains a UINavigationController component which is contains all navigation structure.
When my login view loaded, i use this lines of code
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
for hide UINavugationConttoller (I don't need to show to user navigation bar during the login.)
After that when the user execute the login submit button on the login view
i use this code for push the RootmenuView to the UINavigationController's stack.
RootMenuController *rootMenuController = [[RootMenuController alloc] initWithNibName:#"RootMenuController" bundle:0];
[self.navigationController pushViewController:rootMenuController animated:NO];
[rootMenuController release];
It's working very well. And when the Rootmenuview loaded user have to show the navigation bar then i'm showing the UINavigation's tool bar with this code
- (void)viewDidAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
But i don't like the default animation transitions of UINavigationController then i changed above code with below
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:0.50];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:
UIViewAnimationTransitionFlipFromRight
forView:self.navigationController.view cache:YES];
RootMenuController *rootMenuController = [[RootMenuController alloc] initWithNibName:#"RootMenuController" bundle:0];
[self.navigationController pushViewController:rootMenuController animated:NO];
[UIView commitAnimations];
[rootMenuController release];
It's working too but UINavigationController flickering during between two view naimation transition.
I didn't solve this problem.
Any suggestions ?
Thank you
Have you tried [setAnimationTransition:forView:cache:NO]? I got some odd behavior similar to yours when I used to mess around with UIView animations and used caching.
In short: I want to have two fullscreen views, where I can switch between view A and view B. I know I could just use an Tab Bar Controller, but I dont want to. I want to see how this is done by hand, for learning what's going on under the hood.
I have an UIViewController that acts as an root controller:
#interface MyRootController : UIViewController {
IBOutlet UIView *contentView;
}
#property(nonatomic, retain) UIView *contentView;
#end
The contentView is hooked up to an UIView which I added as an subview to the "view" of the Nib. This has green color and I see it fullscreen. Works fine.
Then, I created two other View Controllers pretty much the same way. ViewControllerA and ViewControllerB. ViewControllerA has a blue background, ViewControllerB has a black background. Just to see which one is active.
So, in the implementation of myRootController, I do this:
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
ViewControllerA *vcA = [[ViewControllerA alloc] initWithNib];
[self.contentView addSubview:vcA.view];
[cvA release];
}
By the way, the -initWithNib method looks like this:
- (id)initWithNib { // Load the view nib
if (self = [super initWithNibName:#"ViewA" bundle:nil]) {
// do ivar initialization here, if needed
}
return self;
}
That works. I see the view from ViewControllerA when I start the app. But now the big question is: A View Controller typically has all those methods like:
(void)viewWillAppear:(BOOL)animated;
(void)viewDidDisappear:(BOOL)animated;
(void)viewDidLoad;
...and so on. Who or what, or how would those methods be called if I do it "my" way without a tab bar controller? I mean: If I allocate that ViewController's class and the view get's visible, would I have to take care about calling those methods? How does it know that viewWillAppear, viewDidDisappear, or viewDidLoad? I believe that the Tab Bar Controller has all this "cleverness" under the hood. Or am I wrong?
UPDATE: I've tested it. If I release the view controller (for example: ViewControllerA), I will get no log message on viewDidDisappear. Only when allocating and initializing the ViewControllerA, I get an viewDidLoad. But that's it. So all signs stand for the cleverness of UITabBarController now ;) and I have to figure out how to replicate that, right?
There's a nice example of switching views in Chapter 6 of Beginning iPhone Development. You can see the source code for it here:
http://iphonedevbook.com/
SwitchViewController has the code to change views programatically.
- (IBAction)switchViews:(id)sender
{
if (self.yellowViewController == nil)
{
YellowViewController *yellowController = [[YellowViewController alloc]
initWithNibName:#"YellowView" bundle:nil];
self.yellowViewController = yellowController;
[yellowController release];
}
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
UIViewController *coming = nil;
UIViewController *going = nil;
UIViewAnimationTransition transition;
if (self.blueViewController.view.superview == nil)
{
coming = blueViewController;
going = yellowViewController;
transition = UIViewAnimationTransitionFlipFromLeft;
}
else
{
coming = yellowViewController;
going = blueViewController;
transition = UIViewAnimationTransitionFlipFromRight;
}
[UIView setAnimationTransition: transition forView:self.view cache:YES];
[coming viewWillAppear:YES];
[going viewWillDisappear:YES];
[going.view removeFromSuperview];
[self.view insertSubview: coming.view atIndex:0];
[going viewDidDisappear:YES];
[coming viewDidAppear:YES];
[UIView commitAnimations];
}
You can begin from the simplest removeFromSuperview/insertSubview and add code to it little by little.
//SwitchViewController.h
#import
#class BlueViewController;
#class YellowViewController;
#interface SwitchViewController : UIViewController {
IBOutlet BlueViewController *blueViewController;
IBOutlet YellowViewController *yellowViewController;
}
- (IBAction)switchViews:(id)sender;
#property (nonatomic, retain) BlueViewController *blueViewController;
#property (nonatomic, retain) YellowViewController *yellowViewController;
#end
//1. remove yellow view and insert blue view
- (IBAction)switchViews:(id)sender {
if(self.blueViewController.view.superview == nil)
{
[yellowViewController.view removeFromSuperview];
[self.view insertSubview:blueViewController.view atIndex:0];
}
}
//2. appear=insert, disappear=remove
if(blueViewController.view.superview == nil)
{
[blueViewController viewWillAppear:YES];
[yellowViewController viewWillDisappear:YES];
[yellowViewController.view removeFromSuperview];
[self.view insertSubview:self.blueViewController.view atIndex:0];
[yellowViewController viewDidDisappear:YES];
[blueViewController viewDidAppear:YES];
}
//3. now add animation
[UIView beginAnimations:#"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
//blue view will appear by flipping from right
if(blueViewController.view.superview == nil)
{
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight
forView:self.view cache:YES];
[blueViewController viewWillAppear:YES];
[yellowViewController viewWillDisappear:YES];
[yellowViewController.view removeFromSuperview];
[self.view insertSubview:self.blueViewController.view atIndex:0];
[yellowViewController viewDidDisappear:YES];
[blueViewController viewDidAppear:YES];
}
[UIView commitAnimations];
If I understand correctly, what you are trying to accomplish is pretty straightforward.
Just add a UINavigationController on your application delegate and do:
[navigationController pushView:vcA];
Delegates will be called accordingly:
(void)viewWillAppear:(BOOL)animated;
(void)viewDidDisappear:(BOOL)animated;
(void)viewDidLoad;
And when you want to pop the view and push another one:
[navigationController popViewControllerAnimated:true];
[navigationController pushView:vcB];
If you don't want the navigationController showing just use:
[navigationBar setHidden:YES];
Where navigationBar is the UINavigationBar corresponding to your UINavigationController.
This may be an old issue, but I recently came across the same problem and had a hard time finding something that worked. I wanted to switch between two complementary view controllers, but I wanted the switch to be animated (built in animations work fine), and I wanted it to be compatible with storyboards if possible.
For taking advantage of built-in transition, UIView's +transitionFromView:toView:duration:options:completion: method works beautifully. But, it only transitions between views, not view controllers.
For the transition to be between whole view controllers, not just views, creating a custom UIStoryboardSegue is the way to go. Whether or not you use storyboards, this approach lets you encapsulate the whole transition and manage the passing of relevant information from one view controller to the next. It only involves subclassing UIStoryboardSegue and overriding a single method, -perform.
For a reference implementation, see RAFlipReplaceSegue, the exact custom segue I put together using this approach. As a bonus, it also replaces the old view controller with the new if it is in a UINavigationController stack.