How to hide UINavigationBar and UITabBar animated? - ios

I have a UIViewController with UINavigationBar and UITabBar. I want to hide them animated: UINavigationBar flip upward and UITabBar flip downward. During the animation, I need the UIViewController's view resizing automatically to fulfil the new frame.
How to do it?
My structure is: UIViewController is placed in a UINavigationController as rootViewController, and the UINavigationController is placed in a UITabBarController as a childViewController.
I have tried resize the AppDelegate.window.frame directly but it will make other views.frame incorrect.

Since iOS8 you can use:
self.navigationController.hidesBarsOnSwipe = YES;
INFO
There is a bunch of nice new methods since iOS8:
UINavigationController
hidesBarsOnTap: A Boolean value indicating whether the navigation controller allows hiding of its bars using a tap gesture.
hidesBarsOnSwipe: A Boolean value indicating whether the navigation bar hides its bars in response to a swipe gesture.
hidesBarsWhenVerticallyCompact: A Boolean value indicating whether the navigation controller hides its bars in a vertically compact environment.
hidesBarsWhenKeyboardAppears: A Boolean value indicating whether the navigation controller hides its bars when the keyboard appears.

Related

View shifts down when presenting view controller with status bar on iOS 9

I have a view controller (view1) that prefers a hidden status bar. I have a button that presents another view controller (view2) modally from the bottom of the screen (a 'Show' segue in my storyboard) and view2 prefers a visible status bar. On iOS 8, this is a smooth transition from view1 to view2, but on iOS 9 the status bar immediately appears in view1 when I press the button and the entire view of view1 shifts down to accommodate it.
This is an ugly effect and I wish to avoid it. For some reason iOS 8 handles this much more gracefully than iOS 9. Is there a fix for this?
In Swift, you can set a global variable on view2 as
isStatusBarHidden = false
on viewWillAppear of view2 change it to true and update status bar
isStatusBarHidden = true
setNeedsStatusBarAppearanceUpdate()
the status bar delegate function will look like
func prefersStatusBarHidden() {
return isStatusBarHidden
}
this only works in Swift, not Objective-C.

UIView animation, condition of when view reached point x

In my current animation, I'm moving an UIView from the bottom to the top. The view is added to the navigation controller's view so it will show on top of the navigation bar.
A simplified version of the animation:
UIView.animateWithDuration(0.5) { self.bar.frame.origin.y = 0 }
In this animation, the view will eventually reach the navigation bar. Is it possible to somehow get this condition and do something with the navigation bar?

UINaviagtionBar subview doesn't fade out during UIViewController "Back" transition

I have a UINavigationBar based app. I've created a custom UIView with some titles and added it as a subview to the navigation bar:
[self.navigationController.navigationBar addSubview:_navbarView];
Everything works ok until I hit the back button in the navigation bar and the UIViewController transition occurs.
The problem is that my custom view doesn't fade away like the others elements in the UINavigationBar, it just stays the same and disappears when the transition is complete.
I want it to fade away during the transition like the native elements of the UINavigationBar, is there any way to achieve this?
If you add a subview to the navigation bar, then it will just stay there; the navigation controller doesn't know to do anything special with it. You say your custom view has "some titles" - have you tried doing this instead?
self.navigationItem.titleView = _navbarView;
Then the navigation controller knows that the view should be used in place of your controller's title, and it should animate in and out.
If that doesn't work, you'll need to look at becoming the navigation controller's delegate. Since iOS7, this can get quite complex.
If you need custom navigation bar it could be a good idea to create UINavigationController with custom UINavigationBar
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
in your navigation bar class you can implement
- (UINavigationItem *)popNavigationItemAnimated:(BOOL)animated
{
if (animated) {
//your_problem_view animation here
}
[super popNavigationItemAnimated];
}

alternating between toolbar / tab bar

my app is structured as follow: UITabBarController > UINavigationController > ViewControllerOne > ViewControllerTwo.
the UINavigationBar has at the bottom the tab bar, now when the user navigates into the second view controller, i want to be able to hide the tab bar and replace is with a tool bar. i tried this code:
[self.navigationController.tabBarController.tabBar setHidden:YES];
[self.navigationController.toolbar setHidden:NO];
when i run the app the tab bar is hidden but the toolbar doesn't appear. plus, since the last VC is a table view controller, when i scroll through the cells there is a white gap between the table and the bottom of the view. how can i fix that?
That won't work because when you hide the tab bar like that the subviews won't be adjusted properly (that's why you get the white space). You'll have to use
self.hidesBottomBarWhenPushed = YES;
In your init method or awakeFromNib... and then
[self.navigationController setToolbarHidden:NO animated:YES];
In the viewDidLoad for example.
That way the tab bar controller's view is going to layout correctly it's subviews when you hide the tab bar. Just remember to call self.hidesBottomBarWhenPushed = NO; in your first view controller otherwise the tab bar is still going to be hidden when the second view controller is popped from the navigation stack.
Try to assigning toolbar with appropriate frame and adding it to self.tabBarController.view

Explaining difference between automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars, edgesForExtendedLayout in iOS7

I have been reading a lot about iOS7 UI transition.
I am not able to get what these three properties automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars, edgesForExtendedLayout??
For example I am trying to make my view controllers start below the status bar but I am not able to achieve it.
Starting in iOS7, the view controllers use full-screen layout by default. At the same time, you have more control over how it lays out its views, and that's done with those properties:
edgesForExtendedLayout
Basically, with this property you set which sides of your view can be extended to cover the whole screen. Imagine that you push a UIViewController into a UINavigationController. When the view of that view controller is laid out, it will start where the navigation bar ends, but this property will set which sides of the view (top, left, bottom, right) can be extended to fill the whole screen.
Let see it with an example:
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor redColor];
UINavigationController *mainNavigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
Here you are not setting the value of edgesForExtendedLayout, therefore the default value is taken (UIRectEdgeAll), so the view extends its layout to fill the whole screen.
This is the result:
As you can see, the red background extends behind the navigation bar and the status bar.
Now, you are going to set that value to UIRectEdgeNone, so you are telling the view controller to not extend the view to cover the screen:
UIViewController *viewController = [[UIViewController alloc] init];
viewController.view.backgroundColor = [UIColor redColor];
viewController.edgesForExtendedLayout = UIRectEdgeNone;
UINavigationController *mainNavigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
And the result:
automaticallyAdjustsScrollViewInsets
This property is used when your view is a UIScrollView or similar, like a UITableView. You want your table to start where the navigation bar ends, because you wont see the whole content if not, but at the same time you want your table to cover the whole screen when scrolling. In that case, setting edgesForExtendedLayout to None won't work because your table will start scrolling where the navigation bar ends and it wont go behind it.
Here is where this property comes in handy, if you let the view controller automatically adjust the insets (setting this property to YES, also the default value) it will add insets to the top of the table, so the table will start where the navigation bar ends, but the scroll will cover the whole screen.
This is when is set to NO:
And YES (by default):
In both cases, the table scrolls behind the navigation bar, but in the second case (YES), it will start from below the navigation bar.
extendedLayoutIncludesOpaqueBars
This value is just an addition to the previous ones. By default, this parameter is set to NO. If the status bar is opaque, the views won't be extended to include the status bar, even if you extend your view to cover it (edgesForExtendedLayout to UIRectEdgeAll).
If you set the value to YES, this will allow the view to go underneath the status bar again.
If something is not clear, write a comment and I'll answer it.
How does iOS know what UIScrollView to use?
iOS grabs the first subview in your ViewController's view, the one at index 0, and if it's a subclass of UIScrollView then applies the explained properties to it.
Of course, this means that UITableViewController works by default (since the UITableView is the first view).
Not sure if you are using storyboards, but if you are, to make your view controllers start below the status bar (and above the bottom bar):
Select the view controller in IB,
In the attributes inspector, deselect 'Extend Edges - Under Top Bars' and 'Extend Edges - Under Bottom Bars'.
I am using storyboards and using the above advice worked however I wasn't exactly sure how to implement it. Below is a short example in swift of how it cleared up the problem by putting the recommended solution into the ViewController.
import Foundation
import UIKit
// This ViewController is connected to a view on a storyboard that
// has a scrolling sub view.
class TheViewController: UIViewController {
// Prepares the view prior to loading. Putting it in viewDidAppear didn't work.
override func viewWillAppear(animated: Bool) {
// this method is an extension of the UIViewController
// so using self works as you might expect.
self.automaticallyAdjustsScrollViewInsets = false
// Default is "true" so this sets it to false tells it to use
// the storyboard as you have it placed
// and not how it thinks it should place it.
}
}
My Problem:
Auto Adjust set to true by default causing a difference between storyboard design and simulator
Resolved:
Code above applied, turning off the auto-adjust.
I solved this problem by adding this line, but my problem was related to a UIView, not UIScrollView
self.navigationController.navigationBar.translucent = NO;
Just bare in mind that
automaticallyAdjustsScrollViewInsets
property works only if some kind of scroll view (table view, collection view,...) is either
The view of VC, or
First subview of this view
Other suggested, that it doest work even if it is the first subview, but there are other scroll views in the view hierarchy.
EDIT (extension DIY)
If you want similar behaviour even if you can't fulfil these conditions (e.g. you have a background image below the scroll view), you can adjust the scroll view insets manually. But please don't set it to constant like 44 or 64 or even 20 like many suggest around SO. You can't know the size ever. There can be the incall/gps/audio notification, navigation bar doesn't have to be always 44 pts etc.
I think the best solution is to use layoutGuide length in didLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentInset = UIEdgeInsets(top: topLayoutGuide.length, left: 0, bottom: 0, right: 0)
scrollView.scrollIndicatorInsets = scrollView.contentInset
}
You can use the bottomLayoutGuide in the same way.

Resources