iPhone SDK: Advancing from one view to another using a button tap - iphone

I want to make a really simple iphone app: one screen with a single button... when the button is tapped a new screen appears. That's it. No animations, nothing,
I've tried endlessly to make the NavBar sample project do this... and it works but only if I use a UINavigationController with a table that I can tap etc. I've tried all the skeleton projects in XCode too.
I thought I was done when I did this:
[[self navigationController] presentModalViewController:myViewController animated:YES];
But I couldn't do it without the UINavigationController. I just want a simple example.
Thanks so much!

One way you could do this is to create a new UIView and then when the button is pressed add that new UIVIew as a subview, therefore making it what you see.
If you make the new view its own subclass of UIView you would do something like this.
LoginView *login = [[LoginView alloc] initWithFrame: rect];
[mainView addSubview: login];

[self presentModalViewController:myViewController animated:NO];
Will pop up a new view, no animations, nothing. To get rid of it, inside myViewController:
[self dismissModalViewControllerAnimated:NO];
Though I reccomend you use the nice sliding animations (change NO to YES.) And yes, you can stack them up. I think this is better than creating a new UIView, but I may be wrong.

The correct way to do this is set up your project with a UINavigationController. In your root view controller, add your button in the view controllers's view. Then in viewDidLoad, register for UIControlEventTouchUpInside events from you button. Then, in your event callback, call:
[self.navigationController pushViewController:[[[SecondViewControllerClass alloc] initWithNib:nibName bundle:nil] autorelease]];
What kdbdallas suggested will work, but you won't get the nice sliding effects, nor will the navigation bar automatically change and provide your users with a back button.

Related

Creating a View popup over another ViewController on iPhone

I am trying to make some kind of popup view when a button i pressed on the iPhone. And it would be nice if I could manage that popup view with a ViewController. I have found out that the UIPopoverController could have been the solution, but it seems that it only works on the iPad...
But anyway, are there any similar solutions for the iPhone?
I am using storyboard
Check out these repos on Github:
https://github.com/werner77/WEPopover
https://github.com/50pixels/FPPopover
Create a separate view controller and resize its xib file and make it look like a popup.
Then ADD this view controller as a subview, and also add it as childController too.
[self addChildViewController:self.popOverViewController];
[self.view addSubview:self.popOverViewController.view];
Now Make it hidden initially.
self.popOverViewController.view.hidden = YES;
If a user taps on Button then using fade in & Fade out animation you can hide/unhide it.
I can tell you how to fade in and fade out if you want to know it further, I hope you can do it easily.
In interface builder make a UIView size of the screen and then make another in that Uiview with the style, size and the such for your pop over. Make its class, hook everything together.
CustomPopUpView *view = [[CustomPopUpView alloc] initWithFrame.....]
Add this all to your UIViewController with
[self.view addsubview:view]
Then attach a tapGestureRecognizer to the back view that animates the whole view off screen when tapped. So now if they click off your pop over view it close it will animates it off screen.
Hope this makes sense.
BooRanger

Can two UIViewControllers use a single navigation bar?

In my iPhone app I am trying to have the allusion of having a single static navigation bar, so the title and buttons don't ever swipe across when switching views.
I can't think a way of doing it (simply at least), so do you have any suggestions? I need to have a static title and buttons up in the nav bar space (even if I don't use the UINavigationBar, but make something custom) so that when I do something such as push a view controller, when it swipes across my nav bar doesn't move and the buttons change function for the new view.
Edit
Ok, I have thought of a possible method. Each of my views have a secondary view in which gold the view contents, except the nav bar objects. Can I override the pop and push methods to just animate this subview on and off screen?
Just do a push as normal but set it to not animate.
i.e.
[self.navigationController pushViewController:newViewController animated:NO];
Then when you want to go back...
[self.navigationController popViewControllerAnimated:NO];
Then you can have two navigation bars but it will look like it's the same bar as it isn't animating.
EDIT TO ALSO ANIMATE IN THE VIEW AND KEEP THE NAV BAR
I'm not sure of the whole flow of the app but if you want to keep the nav bar and swipe the new UI in then you could create a scroll view (with paging) and put the views of each VC on different frames of the scroll view or something.
Why do you want to keep the nav bar still anyway? There is nothing wrong with animating the nav bar and keeping the same buttons etc on it.
Having said that, if you are using different VCs then the nav bar should change anyway to show the details (i.e. title) of the VC you are currently looking at.
ANOTHER MORE RADICAL APPROACH
OK, thinking laterally now :D
How about, you use the not animated push and pop (as above) but instead of just displaying the UI you can animate it in from the relevant side. (A singleton or a VC subclass which you then subclass for your UI could do this for you across all view controllers).
The next problem is that it will look like the UI has gone instantly blank before animating in the new UI so you need to animate out the old UI. This means both UIs (the old and the new) have to be on the screen at the same time.
You can get round this by converting the entire view of the old UI into an image (not hard to do will find a link) and then passing this image into the new VC. The new VC will then instantly display this image and animate it out of the screen at the same time as animating its own UI onto the screen.
Really not as hard to do as it sounds. Especially if you subclass UIViewController ad give it a function animateUI and a property oldUIImage and direction. Then you can override viewWillAppear in this class to do the animation for you. Then the only thing you have to do is give each VC an image and a direction when you push/pop to it.
This is just giving the illusion of what you're after and means you can still keep a fairly simple object model and flow of the app.
...or something.
Just a riff on #Fogmeister's good idea...
In the presenting view controller, get self.view's image by implementing the suggestion here. Then, when it's time to present...
UIImage *image = [self makeImage]; // what it was called in the other post, consider a better name
MyViewController *newVC = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
newVC.presentationImage = image;
[self.navigationController pushViewController:newVC animated:NO];
In MyViewController, give it a UIImage property called presentationImage, then ...
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIImageView *imageView = [[UIImageView alloc] initWithImage:self.presentationImage];
imageView.frame = self.view.bounds;
imageView.tag = 128;
[self.view addSubview:imageView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIImageView *imageView = (UIImageView *)[self.view viewWithTag:128];
[UIView animateWithDuration:0.5 animations:^{
imageView.frame = CGRectOffset(imageView.frame, -self.frame.size.width, 0);
}];
}
FYI - I didn't test or even compile this. Just liked the idea and decided to stretch my fingers.
To do this in a "clean" way, you'd need to abandon UINavigationController and write a custom container controller that does not push new navigation items onto it's navigation bar when pushing a new view controller (or allows you to push the navigation item in a non-animated fashion while animating the push of the view controllers views).
However doing this will take some time. If you decide to do this, i recommend the WWDC Session on UIViewController containment.
Another alternative that springs to my mind is to (by subclassing or method swizzling) alter the behaviour of UINavigationController to push the navigation items non-animated while animating the viewcontroller-push.
I have in no way tested this, but overriding push (and pop respectively) in a subclass like this might work:
- (void)pushViewController:(UIViewController *)vc animated:(BOOL)animated {
[super pushViewController:vc animated:animated];
[self.navigationBar setItems:self.navigationBar.items animated:NO];
}
If this method doesn't work, it might be worth inspection what animations are going on inside the nav bar directly after the call to pushViewController:animated:. Maybe you can cancel some animations to go to the final state directly.

black bar as the view fades when I am using navigation controller

I'm writing my first iPhone app and I am trying to figure out how to have a MasterView and DetailsView like in the example. However, instead of using a TableView, I want to use a button on the MasterView to go to the SignUpView. I want the MasterView to NOT have a navigation bar but the SignUpView needs to have one.
I have tried putting a NavigationController into the MasterView using the interface builder. This doesn't seem to do anything at all ... I.e. I make the following call:
[self.navigationController pushViewController:signUpViewController animated:YES];
And nothing happens. The SignUpView is never shown.
So then I declared a NavigationController in the AppDelegate. The above call in the same function that it was in before (button handler, button is in MasterView) works now! It takes me to the SignUpViewController.
however, the issue is, when I press back on the navigation bar in the sign up view, the navigation bar shows up again in the MasterView. I tried to set
self.navigationController.navigationBarHidden = YES;
in viewDidLoad and viewDidAppear, but that causes a black bar to appear in the transition from SignUpView to MasterView.
I tried to not set it in one of the two, and that causes the animation to go smoothly, but the navigation bar shows up in the MasterView.
I feel like this should be pretty simple to do ... but I'm at my wits end trying to figure this out. Some help would be really appreciated!
Thanks.
Probably not the answer to your question, but just a small suggestion. In the many apps that I have come across, a sign-up/sign-in view is generally displayed as a modal view (on top of your master view) with a 'cross' in the top-right corner to dismiss it. Probably it results in a better user experience.
Also, did you try self.navigationController.navigationBarHidden = YES; in the MasterView's viewWillAppear ?
HTH,
Akshay
I had this problem too, until I discovered setNavigationBarHidden. You will probably want to use these in viewWillAppear/viewWillDisappear or viewDidAppear/viewDidDisappear. You don't want to call this in viewDidLoad because that is only called once when the view is initialized, not every time it appears.
To hide:
[self.navigationController setNavigationBarHidden:YES animated:YES];
To show:
[self.navigationController setNavigationBarHidden:NO animated:YES];

iOS: confused about removeFromSuperview and switching views

New to iPhone development, but I've been given a big project as a first go and I'm a bit stuck.
Basically the app will start with a settings screen, then you click a button to go to a dashboard with multiple option buttons. Each button will lead to a different Navigation View with tables.
The way I've approached this is to start with a UIViewController with a button, which I've got wired up but when you hit the button and I do:
[self.view removeFromSuperview];
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
the second view isn't loading. I just get a blank screen. Do I need to make a reference in the first controller to the second?
Also, am I approaching this in the right way? As long as I removeFromSuperview will I be able to load the navigation controllers on the press of a button?
Sorry if this isn't too clear, I've been through books and lots of websites but don't seem to be able to get my head around this.
Thanks
There is nothing here with the new view, rather the problem is with current view. You have removed the self.view from super view.
[self.view removeFromSuperview];
So anything added to self.view will not be shown, as self.view itself is removed.
When presenting child controller/view from a parent controller, you should consider using presentViewController. Eventually, use dismissViewControllerAnimated when you want child to disappear and parent to reappear.
In parent view controller:
ChildViewController * child = [[ChildViewController alloc] init];
[self presentViewController:child animation:YES completion:Nil];
In child view controller, ie. in some action handler:
-(IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:Nil];
}
IMHO you should also get in the habit of naming instance variables to what they are instantiated from. In your example you name the instance newView, when it should be something like newViewController. That way you make sure you don't mix up views with view controllers.
[self.view removeFromSuperview];
You've removed the view from the superview
[self.view addSubview:newView.view];
But you're adding the new view to the same view that you have just removed from the superview. It's not displaying anywhere.
Your third line adds newView as a subview of self.view, but you just removed self.view from it's superview.
I'd suggest reading more about view controllers. You'll want to have one view controller per "screen", so one for your settings screen, one for your dashboard, one for each table, and so on. Then, manage which one is visible by pushing and popping these view controllers from the nav controller's stack.
This removes self.view, which will most likely destroy the object since there will be no other references to it:
[self.view removeFromSuperview];
Here you are creating an UIViewController, and adding it's view to self.view, which is probably not what you want:
UIViewController *newView = [[UIViewController alloc] initWithNibName:#"Dashboard" bundle:nil];
[self.view addSubview:newView.view];
Look into UINavigationController so that you can easily swap screens in and out with some built in animations. Here's a bit more about them. Here's a tutorial.
The UIViewController's view should not be removed from or added to a view hierarchy outside the control of the view controller. While you might be able to get that manipulation to work now it won't in the future.
Read up on view controllers here.
The basic idea is that you present the view controller then it will take care of manipulating the view hierarchy for you.
So a better approach to get started would be to do something like this;
[viewController1 presentModalViewController:viewController2 animated:YES];
This line of code will present viewController2 with the default modal animation (slide in from the bottom). If you'd like a different animation you can change the modalPresentationStyle to one of the constants in the UIModalPresentationStyle enum on viewController1 (note thats a viewController1, not viewController2).
If you want something more like the Clock app look into the tab bar controller. If you want something more like the Mail app look into the navigation controller.

Keep new view in a tab when switching to another tab

This is the situation:
I have a tab bar with 2 tabs. Tab01 and Tab02.
In Tab01 I have a button which pushes repVC:
repVC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:repVC animated:YES];
[(UIViewController *)[tabController.viewControllers objectAtIndex:0] setView:repVC.view];
[repVC release];
Inside repVC I have another button which pushes an MFMailComposerViewController:
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
[self presentModalViewController:mail animated:YES];
[mail release];
The problem is: when mailView is shown(in Tab01) and I click Tab02, then back to Tab01, the mailView is hidden and even if I click the email button again, the view won't be presented.
So what I have is: Tab01.view -> repVC.view -> mail.view
For repVC, I use this line when I push the view so that even if I go switch tabs, that view will still be activated:
[(UIViewController *)[tabController.viewControllers objectAtIndex:0] setView:repVC.view];
But I can't do the same for mail because tabController is declared in another class which I cannot import. So I can't access the tabController and set the view for Tab01.
Hope the edit helped the understanding.
Hmm,
I still would suggest to use a Navigationcontroller. Would make things way easier, is conform to apple guidelines and suggestions and is pretty fast implemented. (Just create a Navigationcontroller, put the View of Tab1 as main view and hand it over to the TabbarController. Then for the mailView use [self.navigationController pushViewController:mail animated:YES]; Then the navcontroller "saves" the present view for you when u switch tabs)
But if for some Reason you have to use a modalViewcontroller you could either just deactivate the tabbar while the ModalView is shown or try to implement a switch or a simple if...else case in your ViewWillAppear where u check what screen to load.
Then Clean out the Window and load the right screen.
Hope you get the idea of what I mean, sometimes my way of writing seems to confuse people. ^^
A little more information would be great.
How did u set up your TabbarController?
How do u push the new view? Within a UINavigationController? If not, then do it with a navController, he should save the actual state of view and your problem should be solved.
If u already use a navController please post your ViewDidLoad and ViewWillAppear of the Viewcontroller of Tab 1
As #Amandir points out you could probably solve your problems by using a UINavigationController. I get a feeling that you are trying to abuse the modal view controller concept a bit and that's why it doesn't work as you expect. When you use presentModalViewController:animated: the intention should be that you are displaying a view that is modal, i.e. the user must interact and dismiss the modal view before she can continue.
What the paragraph above means that when you present a modal view controller it shouldn't be possible to use the tab bar. Since you are using the word push I'm guessing that you would like change the view of Tab01 while still being able to use the functionality of the tab bar. The problem is that there isn't any built-in method of pushing view controllers besides UINavigationController. persentModalViewController:animated: should only be used in case where you want a modal view, which on the iPhone means a full screen view.
The easiest way would probably be to use an UINavigationController and hide the navigation bar. Then you would get the functionality I think you are after. The other option is to manually add and remove sub views.
[self.view addSubview:repVC.view];
and
[repVC.view removeFromSuperview];
[self.view addSubview:mail.view];
You can use block animations if you want some fancy transitions.