UINavigationController with unified background across all ViewControllers - ios

I've been toying around with an idea of a UINavigationController that has an image as background and every view controller on the stack would have transparent background so the background image of the UINavigationController is visible across all of them.
I tried implementing this the most obvious way (have root view controller with fullscreen image and view controllers with transparent background, but this results in awkward issues with animation).
Digging around I found HotelTonight app managed to implement this. See a recording of their user interface. I made: https://www.youtube.com/watch?v=qvynwhCj5oM&list=UUKnxsyMsRRRJs_Yw9GZVOFw
Does anybody have some suggestion on a right path implementing this?

It's probably better to subclass UIViewController to have a particular setup for its background and navigation controller style. For example, in the viewDidLoad method of your UIViewController subclass, set its background to a certain image. You can also use this to style your navigation bar (certain colors for the barButtonItems, etc). Then each new view controller you create is a subclass of your skinned view controller.
If your background is an image, you will still get the weird animation effects when a new view controller slides in over the top of the old one; the background will slide in as well. Depending on how custom you want to make your transition animations, there are things you can do to play around with the views during the transition. UIPresentationController might present some solutions for making a more custom animated transition.
Edit: To emulate the Hotel now app, I made a UINavigationController with an image as a background, and controllers on top with clear backgrounds. On a push, I animate the alpha of the first controller to 0 while the segue is happening. On returning, I set the alpha back to 1.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.view.alpha = 1;
}
-(void)didClickButton:(id)sender {
[UIView animateWithDuration:.25 animations:^{
self.view.alpha = 0;
}];
// the transition itself is a push segue defined in the storyboard
}
I believe that the Hotel App's background works well with this method since it's very dark. Not sure if it would be very effective for brighter apps or apps with a lot of content in the controller being covered.

Related

View Controllers with transparent background overlapping when segueing (Swift)

I have multiple view controllers set up with push segues, they are all a grey colour with 50% opacity. The problem is when pushing to next VC they overlap and it doesn't look great at all.
I have been looking online and only answer I could find was to remove the animation. I do not want to do this as I have set up tap gesture swipes and the UI do not look that pleasing if there isn't the push animation!
Please see video example of it in action here -
http://tinypic.com/player.php?v=2ez6x7a%3E&s=9#.Vnh6SJOLSX0
Solution I:
If you are ok to remove the animation, Set animated false on UINavigationController.pushViewController in Swift
self.navigationController!.pushViewController(viewController, animated: false)
Solution II:
If you don't, then you may need to hide the current ViewController before you push the new viewcontroller as follows:
yourCurrentViewController.view.hidden = YES;

How to Push a clear or alpha UIViewCotroller

A button in UIViewController VC1,from this button,we need push(not present) a UIViewController VC2,and VC2's backgroundColor is clearColor or with alpha.
Maybe somepeople will answer me:just setBackgroundColor [UIColor clearColor],if this,the VC2 will be clear or with alpha when it pushing,but after animation complete,it's backgroundColor will be black or white strangely,that's my doubt.I just want VC2 above on VC1,and from VC1 i can see VC1,and just push not present!
You should use the below code :
[viewController2.view setBackgroundColor:[UIColor clearColor]];
Pushing a new view controller will cause the first view controller's view to be removed from display. Setting the background color of the second view controller will not help.
You will likely need to manage the overlay as a subview to the first view controller's view.
"Presenting" a view controller means to display one view controller over another. "Pushing" a view controller means to replace one view controller with another. If you want to keep the other view controller around, you must present the new one, not push it.
If by "push" you mean "slide in from the right," then you really mean you want to present it with a custom transition. This is usually done with a UIPresentationController in iOS 8. In iOS 7, you can do it slightly more by hand using a UIViewControllerTransitioningDelegate to provide the animation you want.
You should take a look at Custom UIViewController Transitions by Ash Furrow for a nice introduction to the iOS 7 way. The iOS 8 is nicely covered (in Swift) by "Custom presentations using UIPresentationController in Swift".
Ok,ultimate solution:
From VC1 push to VC2,VC1 just do this:
step1,
[VC1 addChildViewController:VC2];
[VC1.view addSubview:VC2.view];
[VC1 didMoveToParentViewController:self];
setp2,
VC2 setBackgroundColor clearColor or alpha.

Is there any way to cover an iOS 7 status bar with a UIView temporarily?

I have a bit of a weird scenario here and have been trying to find some help but the solutions don't really solve my problem rather make it worse.
I have a container viewController that has three child controllers views inside a horizontal scroll view. My default child controller view is my custom camera page. Now this is what I want to achieve. I would like to keep the status bar hidden on my camera page but would like the other two views aka my other two child controllers to have the status bar showing.
When I drag from either my left child controller to my camera page or from my right child controller to my camera page, I would like my camera page view to overlap the status bar.
The only place I have seen this done is on Snapchat and I've been trying for a couple hours to figure it out but just can't seem to come up with a solution. Any help is appreciated.
You can do it wizh the real Statusbar. Just get the Statusbars UIView you then need to play with the UIWindowLevel like this
//Getting the Statusbar
UIView *statusbar;
NSString *key = #"statusBar";
id object = [UIApplication sharedApplication];
if ([object respondsToSelector:NSSelectorFromString(key)]) {
statusbar = [object valueForKey:key];
}
//Set your Overlapping UIViewController or UIView one Level higher then the Statusbar.
self.navigationController.view.window.windowLevel = UIWindowLevelStatusBar+1; //This will set the Overlapping UIViewControllers WindowLevel over the StatusBar.

Switching between UIViewControllers with UISegmentedControl

Right I have looked at a few SO questions on the subject and I am finding it difficult to come up with the correct solution here.
Requirements
I have a UITabBar based application. One of the tabs has a UINavigation controller with UISegmentedControl at the top allowing the user to switch between three different views.
Each view will have a UITableView which will allow the user to navigate to another view. These views should be pushed onto to the navigation controller.
Problem
Now all the SO questions and Answers on the subject show how to switch between views. However I need the view I switch to, to allow pushing of another view onto the navigation stack. I am not sure this is even possible. I thought about UIViewController containment - however that would show a view being pushed onto the stack in a smaller window that the screen's bounds. Not what I am looking for.
Any ideas how I can solve this with storyboards and UIViewControllers?
UPDATE
Here is what I am trying to do: In the screenshot the container area is where I need to load other view controllers into. The UISegment control cannot go into the navigation bar as that space is used for something else. So that's why I think UIViewController containment might be better here?
So even though this isn't using separate TableViewControllers, you can use different custom UIViews that are hidden by default and become visible when you select it's corresponding button. This will unfortunately make it so you have all three view's logic in the same VC.
To get around this, you can try setting up some delegates and mimicking the TableViewController logic separation by sending out the didSelectTableAtIndexPath, UIGesture touches, etc into classes outside the ViewController to help keep your code cleaner.
User UITabBarController and hide the tab bar.
- (void)viewDidLoad
{
self.tabBar.hidden = YES;
}
Binding the segment control with method valueChanged
- (void)valueChanged:(UISegmentedControl *)seg
{
if ([seg.selectedSegmentIndex == 0]) {
self.selectedIndex = 0;
} else if ([seg.selectedSegmentIndex == 1] {
self.selectedIndex = 1;
}
}
I achieve this by this way, I hope this will help.

pushViewController with scaling animation, that shows background

I have finding way to call pushViewController with scaling animation,
like facebook iPhone app main menu icon click animation.
(new viewController is popup from center, and it scales to original size.)
I searched several way to change animation of pushViewController.
First, I tried this:
viewController.view.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
[UIView animateWithDuration:0.5f animations:^{
viewController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}];
[self.navigationController pushViewController:viewController animated:NO];
but there is problem,
old viewController is disappeared when animation starts, there's only white background.
If I use CATransition to change animation,
I can show both old & new viewController both,
but there's no scaling animation, only move in, push in, fade in animations.
I want to show both new & old view controller like CATransition animations,
and need the way to implement my custom animation.
Here is my last suggestion, dirty way:
[self.view addSubview:viewController.view];
viewController.view.transform = CGAffineTransformMakeScale(0.5f, 0.5f);
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationCurveEaseInOut
animations:^{
viewController.view.alpha = 1.0f;
viewController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
} completion:^(BOOL finished) {
[self.navigationController pushViewController:viewController animated:NO];
[viewController release];
}];
First, I added new viewController.view as subview, I can show animation with new & old view both.
When animation is ended, pushViewController later.
this way I can implement what I thought,
but I think it is dirty way, and there's remain problem:
Navigation bar items are not change immediately, It is changed after animation ends.
Is there any simple, clear way to implement this animation?
I think it is impossible to do that without change original implentation of pushViewController, should I do subclassing pushViewController?
Thanks to read, and your help.
In answer to the original question, I'm not sure if you consider this an improvement or not, but I think you might be able to include the navigator bar in the view that's being transformed (a) in IB, add a navigationBar to your view being transitioned to, (b) animate the hiding of the root navigation controller's navigation bar before you start your animation (so it slides off as your new view is sliding in), and (c) in your completion block of your animation, hide your new view's added navigation bar and unhide the root navigation controller's navigation bar. Maybe this renders something closer to what you intended, though probably different than you originally conceived. It's not perfect, though. The code, might look something like:
MyViewController *newController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
newController.view.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.view addSubview:newController.view];
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationCurveEaseInOut
animations:^{
newController.view.alpha = 1.0f;
newController.view.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
} completion:^(BOOL finished) {
[newController.view removeFromSuperview];
[self.navigationController pushViewController:newController animated:NO];
[newController.tempBar setHidden:YES];
[self.navigationController setNavigationBarHidden:NO animated:NO];
}];
It's adding a little more "dirt" to your "dirty way", but it might render the effect that you're looking for. You just want to make the temporary navigation bar in your new view to look as much like what the eventual root navigation controller's navigation bar will look like, and the user should be none the wiser. If this still doesn't quite achieve what you're looking for, you can just hide the root navigation controller's bar altogether, and always put your navigation on your views themselves, in which case the effect will be entirely seamless as you transition to your new view.
Alternatively, you could stay with your existing code and just set the title of the current view before you transition, that way it might look like the title change happened up front. But then you probably have to do stuff about resetting it when you return, setting the back button, etc., so it might not be worth it.
As an aside, and this is a little tangential to the original question (so I apologize in advance), but in response to the other suggestion that you shouldn't animate the view controller, but rather only views, I would like to voice a word of caution. Yes, technically that's right, but I'm nervous whether this will encourage people to adopt the bad practice of adding new views by (a) creating a new view controller; (b) animate the adding of that view controller's view to be a subview of the current view; but (c) not doing anything with that new view controller to add it to your view controller hierarchy (such as pushViewController or presentViewController).
If that's what was intended to the other answer, I have to say that I think this is a very bad idea. (If that's not what was intended, then my apologies, but I think it's easy for people to misconstrue the suggestion.) Bottom line, I have recently seen people do things like:
MyViewController *newController = [[MyViewController alloc] initWithNibName:#"MyView" bundle:nil];
// do some animation around adding the new view as a subview of your current view
// but neglect to ever invoke pushViewController or presentViewController
[self.view addSubview:newController.view];
This is not recommended for a couple of reasons. First, if it's an ARC project, your view controller will be discarded when newController falls out of scope. (There seem to be a spate of these sorts of questions being posted on SO resulting from exceptions being thrown as people transition to ARC.) Sure, you can fix this by making newController an ivar of your main controller so it won't fall out of scope, but be careful to remember to set it to nil when the main controller is eventually dealloc'ed or as you remove your subview, or else you can get a leak. And if you're in a non-ARC project, it's even easier to leak, so make sure to release the view controller when the subview is removed to prevent leaks.
Second, and probably more importantly, while the above code seems to work (or at least if you make the controller an ivar), but you end up with a disconnect between your hierarchy of your view controllers and that of your views. (In session 102 of WWDC 2011 about a different topic, view controller containment, it includes a lengthy, yet relevant, discussion of the importance of keeping view controller hierarchies and view hierarchies coordinated, e.g. rotation events may not be properly sent to your new view's controller because the controller isn't in the controller hierarchy, so iOS won't know to send them to your controller.) Bottom line, if you use another view controller's view to add as a subview of your current view, it is fragile, is susceptible to breaking on iOS upgrades, and things like rotation events will not be passed to your new view's view controller properly. If you do so, at least be aware that this is not good practice and make your own risk-assessment as to whether you want to do that. It's not necessary to bypass the proper view controller hierarchy, and I personally would like to dissuade people inclined to do so.
Bottom line, if you're transitioning to a view of a different view controller, you really want to stick to pushViewController or presentViewController, and do your animation around the new controller's view (and usually it's nowhere near as complicated as this animation ... generally it's incredibly simple and I've done all sorts of fade and flip animations with much greater ease ... it's the use of the transform that is making this complicated).
Alternative, if you want to animate a new UIView subview and not deal with view controllers at all, just make sure it's not another view controller's view, but rather, for example, a view you create programmatically that uses the same view controller as your originating view. And if you decide to use another view controller's view as a subview of your existing controller, just do so with the knowledge that it's a little fragile and some events may not be transmitted to your new view controller as you expect and it might not be as "future proof" as you may want.
I don't think that animation is done with a view controller. You can just keep the existing view controller. Create a new UIView that is supposed to zoom in. Add that new UIView to the existing view controller and apply animation to it.

Resources