I'd like to show and hide the statusBar and the navigationBar simultaneously using a slide effect.
This is how I tried:
[[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide];
[self.navigationController setNavigationBarHidden:hide animated:animated];
However, the duration of both animation is not the same. The status bar animation takes longer.
I found no way how to specify the duration of either animation.
Did I miss something obvious?
ios-lizard's answer is almost what I wanted but the navigation bar re-appears when rotating the device unless hidden is set correctly. So this worked for me:
Hidding animating works/looks nice YEAH!!.
Showing animation is OK, (I wish I could make the status bar slide with the navigation bar but at least we don't see gaps anymore. :D )
- (void)toggleFullscreen {
UINavigationBar *navBar = self.navigationController.navigationBar;
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
float animationDuration;
if(statusBarFrame.size.height > 20) { // in-call
animationDuration = 0.5;
} else { // normal status bar
animationDuration = 0.6;
}
_fullscreen = !_fullscreen;
if (_fullscreen) {
// Change to fullscreen mode
// Hide status bar and navigation bar
[[UIApplication sharedApplication] setStatusBarHidden:YES
withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:animationDuration animations:^{
navBar.frame = CGRectMake(navBar.frame.origin.x,
-navBar.frame.size.height,
navBar.frame.size.width,
navBar.frame.size.height);
} completion:^(BOOL finished) {
[self.navigationController setNavigationBarHidden:YES animated:NO];
}];
} else {
// Change to regular mode
// Show status bar and navigation bar
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:animationDuration animations:^{
navBar.frame = CGRectMake(navBar.frame.origin.x,
statusBarFrame.size.height,
navBar.frame.size.width,
navBar.frame.size.height);
} completion:^(BOOL finished) {
[self.navigationController setNavigationBarHidden:NO animated:NO];
}];
}
}
This is how I fixed this problem for my app.
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
// delta is the amount by which the nav bar will be moved
delta = statusBarFrame.size.height + self.navigationController.navigationBar.frame.size.height;
if(statusBarFrame.size.height>20) { // in-call
animationDuration = 0.5;
}
else { // normal status bar
animationDuration = 0.6;
}
// hide status bar
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
// hide nav bar
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
self.navigationController.navigationBar.frame = CGRectOffset(self.navigationController.navigationBar.frame, 0.0, -delta);
[UIView commitAnimations];
Clearly, there's no easy solution to do this right. Apple has to fix it.
Of course, one work-around is to use alpha fading as Ephraim suggests. If you're insisting on the sliding behavior, I found it best to just animate the navigation bar and hide/show the statusBar without any animation. This looks much better than sliding the status bar because the gap between the bars during the animation is quite noticeable.
Here's a more concise method that uses system constants for animation duration and also handles incoming calls.
Note that navigationBar is an outlet and statusBarHeight is an instance-variable float.
- (IBAction)toggleControls:(id)sender {
BOOL isHidden = ! [UIApplication sharedApplication].statusBarHidden;
if (isHidden)
statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
[UIView animateWithDuration:[UIApplication sharedApplication].statusBarOrientationAnimationDuration animations:^{
self.navigationBar.frame = CGRectMake(self.navigationBar.frame.origin.x,
isHidden ? -self.navigationBar.frame.size.height : statusBarHeight,
self.navigationBar.frame.size.width,
self.navigationBar.frame.size.height);
}];
[[UIApplication sharedApplication] setStatusBarHidden:isHidden withAnimation:UIStatusBarAnimationSlide];
}
nacho4d's answer is almost what I wanted .but,He changes navBar's frame before navBar is visible. So we can't see transition animation.It looks like navBar appear suddenly. What is more, when showing, statusBarFrame.size.height is equal to 0. Here is his code:
[[UIApplication sharedApplication] setStatusBarHidden:NO
withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:animationDuration animations:^{
navBar.frame = CGRectMake(navBar.frame.origin.x,
statusBarFrame.size.height,
navBar.frame.size.width,
navBar.frame.size.height);
} completion:^(BOOL finished) {
[self.navigationController setNavigationBarHidden:NO animated:NO];
}];
when Showing , we wish we could make the status bar slide with the navigation bar. here is my answer:
UINavigationBar *navBar = self.navigationController.navigationBar;
[[UIApplication sharedApplication] setStatusBarHidden:hidden withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
// make navigationBar visual
if (!hidden)
{
[self.navigationController setNavigationBarHidden:hidden animated:NO];
}
navBar.frame = CGRectMake(navBar.frame.origin.x,
hidden ? -navBar.frame.size.height : 20,
navBar.frame.size.width,
navBar.frame.size.height);
} completion:^(BOOL finished) {
if (hidden)
{
[self.navigationController setNavigationBarHidden:hidden animated:NO];
}
}];
when hidding ,and hidden equal to NO. we should change navBar's frame first,then make navBar hidden.
when showing , and hidden equal to YES. we make navBar visual first,then change frame.
we choose UIViewAnimationOptionCurveEaseOut, to make it looks better.
You can use a instance variable to do this:
self.navigationController setNavigationBarHidden:hide animated:animated];
_shouldHideStatusBar = hide;
And implement the following function:
- (BOOL)prefersStatusBarHidden{
return _shouldHideStatusBar;
}
The setNavigationBarHidden:animated function will automatically call prefersStatusBarHidden function. If it doesn't you can call it with the following UIViewController's method:
[self setNeedsStatusBarAppearanceUpdate];
And of course you can choose your status bar hiding animation style with:
- (UIStatusBarAnimation) preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationSlide;
}
This isn't much of an answer but it works. So what I did is:
// This method gets called by whatever action you want
- (void) toggleShowStatusNavBars:(id)sender {
// Assuming you have a ivar called barsHidden
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4]; // This is IMPORTANT, 0.4s
self.navigationController.navigationBar.alpha = (barsHidden?1.0:0.0);
barsHidden = !barsHidden;
[UIView setAnimationDelegate:self];
[UIView setAnimationWillStartSelector:#selector(setStatusBarHidden)];
[UIView commitAnimations];
}
- (void) setStatusBarHidden {
[[UIApplication sharedApplication] setStatusBarHidden:barsHidden animated:YES];
}
This will basically synchronize the start of the animation (since you are calling setStatusBarHidden at the start of the navigation bar animation. The key part is that the status bar animation seems to take 0.4 seconds.
This works for me but if you find a better way, do post here.
Related
I am trying to give up MPMoviePlayerController and switch to AVPlayer but facing problem on 'AVPlayer(Layer) Full Screen animation'.
Project Source Code: http://www.kevin-and-idea.com/avplayer.zip
Goal: Currently, AVPlayer(Layer) is part of the elements on ViewController. The play is need to be able to switch between 'Small' and full screen and when it full screen, it need to be above (cover) statue bar and navigation bar. Also, the player need to be rotate-able depends on Device Orientation
Problem: Don't know how to 'take out' the AVPlayerLayer and 'Cover' the whole screen including statue bar & navigation bar.
Currently: I set UINavigationBar hide and status bar hide to archive but this is not the goal and rotate without issue
THANK YOU SO MUCH!!!
p.s. Click the info icon to switch to full screen
https://c1.staticflickr.com/1/388/18237765479_7d3c292449_z.jpg
Code
- (IBAction)goFullScreen:(id)sender {
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
if (topSpaceConstraints.priority == 999) {
videoContainerSizeRatioConstraints.priority = 250;
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.navigationController setNavigationBarHidden:YES];
topSpaceConstraints.priority = 250;
} else {
videoContainerSizeRatioConstraints.priority = 999;
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.navigationController setNavigationBarHidden:NO];
topSpaceConstraints.priority = 999;
}
[self.view layoutIfNeeded];
}
completion:nil];
}
You have two options (maybe more):
You create a view that is higher in the view hierarchy than your navigation controller view therefore you can just put something that is 'above'. Probably this would be the most visually appealing one and I'm sure most professional apps use this.
The other option you have is just to hide the navigationbar when someone pushes the fullscreen button.
UPDATE:
Maybe a 'better' way for options 1:
I looked at prev. project of mine and maybe you want to use this:
Create a new window to contain your avplayer.
Subclass UIView and implemnt a 'show' method like this:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.alpha = 0;
self.window.windowLevel = UIWindowLevelAlert;
self.window.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.0f];
[self.window addSubview:self];
[self.window addSubview:self];
[self.window makeKeyAndVisible];
[UIView animateKeyframesWithDuration:0.3 delay:0 options:UIViewKeyframeAnimationOptionBeginFromCurrentState animations:^{
[UIView addKeyframeWithRelativeStartTime:0. relativeDuration:0.7 animations:^{
// PROBABLY MORE ANIMATION HERE...
self.alpha = 1;
}];
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:1 animations:^{
self.window.backgroundColor = [UIColor colorWithWhite:0.0f alpha:self.targetDimmDensity];
}];
} completion:^(BOOL finished) {
}];
The self.window is a new #property (nonatomic, strong) UIWindow *window; I created!
i have implemented code in which when i press button UIView Contains datepicker come from bottom and displayed
but it make navigation bar black
and when i press on hide button it make navigation bar normal
navigation bar contains just title
i have uiscroll view before UIView contains date picker
- (void)datePickerShow
{
[UIView animateWithDuration:0.5 animations:^{
CGRect location = [self.view1 frame];
location.origin.y =[self.scrollViewProfile frame].size.height - location.size.height;
NSLog(#"%f",location.origin.y);
[self.view1 setFrame:location];
// [self.scrollViewProfile setContentOffset:(CGPointMake(0,location.size.height) ) animated:YES];
} completion:^(BOOL finished) {
}];
}
//-----------------------------------------------------------------------
- (void) datePickerHide
{
[UIView animateWithDuration:0.5 animations:^{
CGRect location = [self.view1 frame];
location.origin.y =[self.scrollViewProfile frame].size.height +location.size.height;
NSLog(#"%f",[self.scrollViewProfile frame].size.height);
[self.view1 setFrame:location];
[self.scrollViewProfile setContentOffset:(CGPointMake(0,0))animated:true];
} completion:^(BOOL finished) {
}];
}
Try to disable View Controller-based status bar appearance. See this: https://stackoverflow.com/a/18549998/3429577
The following is my custom VC presentation code:
-(void)presentViewController:(UIViewController*)vc
{
UIWindow *w = [[[UIApplication sharedApplication] delegate] window];
UIViewController *parentController = (TabBarViewController *)[w rootViewController];
[parentController addChildViewController:vc];
if ([vc respondsToSelector:#selector(beginAppearanceTransition:animated:)]) // iOS 6
{
[vc beginAppearanceTransition:YES animated:YES];
}
UIView *toView = vc.view;
[parentController.view addSubview:toView];
toView.frame = parentController.view.bounds;
CGAffineTransform tr = CGAffineTransformScale(self.view.transform, 1.0f, 1.0f);
toView.transform = CGAffineTransformScale(self.view.transform, 0.01f, 0.01f);;
CGPoint oldCenter = toView.center;
toView.center = ((RootViewControllerEx*)vc).cellCenter;
[UIView animateWithDuration:4.5 animations:^{
toView.transform = tr;
toView.center = oldCenter;
} completion:^(BOOL finished) {
[vc didMoveToParentViewController:parentController];
if ([vc respondsToSelector:#selector(endAppearanceTransition)]) // iOS 6
{
[vc endAppearanceTransition];
}
}];
}
It works fine, however, in presented VC I am hiding status bar:
- (BOOL)prefersStatusBarHidden {
return YES;
}
When I present my VC using built-in presentViewController:animated:completion:, status bar in presented VC is hidden. But with my code on iOS 7 status bar is not hidden at all, on iOS 6 it is even more strange - status bar is hidden, but my view size is shorter from top by the size of status bar. So I can see a black gap from top on iOS 6. What should I do to properly hide status bar when using custom VC presentation?
you should try this in your viewDidLoad for differencing the IOS 6/7 status bar problem
if ([self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)])
{
//IOS 7 - Status Bar Hidden
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate)];
self.statusBarHidden = YES;
}
else
{
// iOS 6 - Status Bar shown
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
self.statusBarHidden = NO;
}
and an method for hiding status Bar
- (BOOL)prefersStatusBarHidden{
return YES;}
and also add an property for status Bar
#property BOOL statusBarHidden;
then make sure that your view bounds to the screen size and fits correctly
I think this solves your problem :)
Try this
in view did load
[UIApplication sharedApplication].statusBarHidden = YES;
and set value in plist like
set this in project summary
and this in your interface builder
I am creating Animated splash screen in "didFinishLaunchingWithOptions" method. Animation Splash screen duration is 2 seconds. After two seconds i am hiding the Animated Splash screen. When Animated screen appears i want to hide the status bar and when animated screen disappear i want to show the status bar.
how to do that?
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Here i am creating Animated Splash screen
***** Here i want to hide Status bar*******
splashView =[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 320, 580)];
splashView.backgroundColor =[UIColor whiteColor];
[self.window addSubview:splashView];
logoView = [[UIImageView alloc] initWithFrame:CGRectMake(logoX,0, 225, 25)];
logoView.image = [UIImage imageNamed:#"logoImage"];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:window cache:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(startupAnimationDone:finished:context:)];
splashView.alpha = 1.0;
logoView.frame = CGRectMake(logoX, logoY, 225, 25);
[window addSubview:logoView];
[window bringSubviewToFront:logoView];
[UIView commitAnimations];
// Hiding Animated splash screen After 2 second Here
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
************* Here i want to show Status bar Again ***************
[splashView removeFromSuperview];
[logoView removeFromSuperview];
}
You go into ProjectSettings -> General. There is an option Status Bar Style.
EDIT
Use block. They provide really simple syntax for animations.
[UIView animateWithDuration:2.0 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];
//your animation code here
//all changes made here to frame, bounds, alpha etc. are animated
} completion:^(BOOL finished) {
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
//this is called after animation finishes
}];
Add the following entries in your plist file:
"Status bar is initially hidden" = Yes: Hides the status bar at application start and in the splash screen
"View controller-based status bar appearance" = No: Prevents that the view controller classes show the status bar
You can create a category for UIViewController
#implementation UIViewController (HideStatusBar)
-(BOOL)prefersStatusBarHidden
{
return YES;
}
I have a view controller with multiple child view controllers in it (set up using Storyboards), and I move the one on top to the right (with the status bar as well) to display the underlying sidebar view controller.
This works perfectly with frames, as shown below:
- (void)displaySidebar {
self.fullScreenSnapshotOverlay = [self takeFullScreenSnapshot];
[self.postsView addSubview:self.fullScreenSnapshotOverlay];
[self hideStatusBar];
[UIView animateWithDuration:0.4 animations:^{
CGRect newFrame = self.postsView.frame;
newFrame.origin.x += 200.0;
self.postsView.frame = newFrame;
}];
}
(hideStatusBar simply called the UIApplication method and layoutIfNeeded.)
Giving me this (perfect) result:
However, if in the Storyboard I go to the container view controller and make a constraint from its leading space to the left of the view controller it's embedded in, and then adjust that constant, it really messes up the navigation bar, I assume due to hiding the status bar and taking a screenshot. I'm using this code:
- (void)displaySidebar {
self.fullScreenSnapshotOverlay = [self takeFullScreenSnapshot];
[self.postsViewController.view addSubview:self.fullScreenSnapshotOverlay];
[self hideStatusBar];
[UIView animateWithDuration:0.4 animations:^{
self.postsViewControllerDistanceFromLeftSideConstraint.constant = 270.0;
[self.view layoutIfNeeded];
}];
}
Giving me this messed up result:
Now I know the simple thing to do would be to just continue with frames, but I'd like to learn how do it properly with Auto Layout. What am I doing wrong here?
I'm not sure how what I did is any different from what you're doing. I modified the code I posted to your other question (Why does hiding my status bar completely break my simple animation?) to what's below, and it worked fine.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self performSelector:#selector(displaySidebar) withObject:nil afterDelay:1];
}
-(void)displaySidebar {
self.snapshotView = [self takeSnapshot];
[self.PostsView addSubview:self.snapshotView];
[self hideStatusBar];
self.leftCon.constant = 270;
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
}];
}
-(void)moveOutMenu { // called from a button in the menu controller
self.leftCon.constant = 0;
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.snapshotView performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.01];
}];
}
-(UIView *)takeSnapshot {
UIView *v = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
return v;
}
-(void)hideStatusBar {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.view layoutIfNeeded];
}
What are all the constraints you have on the container view that you're moving? Maybe there's something different there (I have top, leading, bottom and width constraints on mine -- leftCon is the outlet to the leading constraint).