UINavigationBar Fade Position Problems - objective-c

So, here's an interesting little problem I've had to deal with. I coded a navigationBar to be translucent and the view underneath to be fullScreen. When I load the view, I can tap on a clear button in the view to "activate" an animation that fades in the bar and other ui elements.
When I rotate the device WITH THE UI ELEMENTS VISIBLE it works perfectly.
But if I tap again to "turn off" the elements with a fade out animation, then rotate, it pushes the naivgationbar up into the status bar.
I don't understand why this happens. I don't want to turn off the statusBar, but if I have to, I will. Can anyone help me with the bar's autorotation positioning?
EDIT SOLVED

SOLVED, this code animates the 20 pixels needed to move the bar down.
- (void)showToolbar
{
if (toolbar.hidden == YES)
{
[self.navigationController.view layoutSubviews];
[UIView animateWithDuration:0.25 delay:0.0
options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction
animations:^(void)
{
[[UIApplication sharedApplication]setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];
toolbar.hidden = NO;
toolbar.alpha = 1.0f;
self.navigationController.navigationBar.alpha = 1.0f;
CGRect frame = self.navigationController.navigationBar.frame;
frame.origin.y = 20.0;
self.navigationController.navigationBar.frame = frame;
}
completion:NULL
];
}
if ([self.navigationController.navigationBar isHidden]) {
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
}

Related

How to hide/show status bar and navigation bar by fading in/out at the same time like the Photos app in iOS 7?

I'm trying to hide and show the status bar and the navigation bar by fading them in and out at the same time like the Photos app in iOS 7. I've got the hiding part working, but I am having trouble with the showing part. The problem is that when I show the navigation bar, it is initially positioned as if the status bar is not there. At the end of fading in, it is positioned correctly (it is shifted down to make room for the status bar). How can I get the navigation bar to be positioned correctly throughout the animation?
Here's some code sketching out my current approach:
In some view controller, I control whether or not the status bar is hidden by overriding some UIViewController methods:
- (BOOL)prefersStatusBarHidden {
return self.forcedStatusBarHidden;
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
return UIStatusBarAnimationFade;
}
To hide the status bar and navigation bar at the same time, I do both in the same animation block:
void (^animations)() = ^() {
theNavigationController.navigationBar.hidden = YES;
someViewController.forcedStatusBarHidden = YES;
[someViewController setNeedsStatusBarAppearanceUpdate];
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
duration:0.5
options:UIViewAnimationOptionCurveEaseInOut
| UIViewAnimationOptionTransitionCrossDissolve
| UIViewAnimationOptionAllowAnimatedContent
animations:animations
completion:nil];
(Notice I use theNavigationController.navigationBar.hidden = YES instead of [theNavigationController setNavigationBarHidden:YES animated:YES] because I want the navigation bar to fade instead of sliding up. Also, for some reason, not including the UIViewAnimationOptionAllowAnimatedContent option does not make a difference.)
But if I do something similar to show the status bar and navigation bar together, I get the problem I described earlier.
void (^animations)() = ^() {
someViewController.forcedStatusBarHidden = NO;
[someViewController setNeedsStatusBarAppearanceUpdate];
theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
duration:0.5
options:UIViewAnimationOptionCurveEaseInOut
| UIViewAnimationOptionTransitionCrossDissolve
| UIViewAnimationOptionAllowAnimatedContent
animations:animations
completion:nil];
The closest I've gotten to getting it to look right is to show the bars in sequence instead of in the same animation block:
someViewController.forcedStatusBarHidden = NO;
[someViewController setNeedsStatusBarAppearanceUpdate];
void (^animations)() = ^() {
theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
duration:0.5
options:UIViewAnimationOptionCurveEaseInOut
| UIViewAnimationOptionTransitionCrossDissolve
| UIViewAnimationOptionAllowAnimatedContent
animations:animations
completion:nil];
But now the bars do not fade in together. (EDIT: If I put the first two lines in their own animation block to force the animation duration of the status bar fading in, I get the original problem with the navigation bar.) How do I fix this?
Note: I'm using a custom background image for the navigation bar. If I just use the default frosted/blurred background for the navigation bar, another problem is that the background is invisible when it is supposed to be fading in and suddenly appears at the end of the fade-in animation. If I can get this working for the frosted/blurred background as well, that would be great.
Another note: Just in case it makes a difference, the navigation controller is presented with theNavigationController.modalPresentationStyle = UIModalPresentationCustom.
I figured out the answer to my own question. The trick is to disable the animation just for setting the frame, bounds, and center for the navigation bar during the fade-in animation. This is done by subclassing UINavigationBar and conditionally using [UIView performWithoutAnimation...] whenever the frame, bounds, and center are set. For example:
- (void)setFrame:(CGRect)frame
{
if (self.shouldAnimateDimensions) {
[super setFrame:frame];
}
else {
[UIView performWithoutAnimation:^{
[super setFrame:frame];
}];
}
}
The fading-in code then becomes:
void (^animations)() = ^() {
// myNavigationBar is theNavigationController.navigationBar
myNavigationBar.shouldAnimateDimensions = NO;
someViewController.forcedStatusBarHidden = NO;
[someViewController setNeedsStatusBarAppearanceUpdate];
myNavigationBar.shouldAnimateDimensions = YES;
theNavigationController.navigationBar.hidden = NO;
};
[UIView transitionWithView:theNavigationController.navigationBar.superview
duration:0.5
options:UIViewAnimationOptionCurveEaseInOut
| UIViewAnimationOptionTransitionCrossDissolve
| UIViewAnimationOptionAllowAnimatedContent
animations:animations
completion:nil];

Animating with Auto Layout and iOS7 jumps to end of animation (fine in iOS6)

I am using Autolayout and animating by changing the constraints, however, in iOS7 the view simply jumps to the end position - in iOS6 I get a nice animation.
Is should be noted these views are UICollectionViews and I have checked the Storyboard and there are no Layout errors.
All I can think is there is something and am or am not setting on the Storyboard or something that I am doing wrong with the Constant settings in the Storyboard.
primaryMenuYContraints.constant = BUTTOMX;;
leftMenuYContraints.constant = 136.0f;
leftMenuBottomConstraint.constant = 5.0f;
[UIView animateWithDuration:0.7f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^
{
// Move in menus
[self.primaryOptionCollection layoutIfNeeded];
[self.menuOptionCollection layoutIfNeeded];
}
completion:^(BOOL finished)
{
}];
I changed to and now works in both iOS7 and 6, still not sure why it does/did it though! I still think I am setting something up wrong in the Storyboard. I am add another view (nothing to do with this lot) programmatically so I believe that is based around frames until I convert it (which I am not doing).
primaryMenuYContraints.constant = BUTTOMX;;
leftMenuYContraints.constant = 136.0f;
leftMenuBottomConstraint.constant = 5.0f;
[UIView animateWithDuration:0.7f
delay:0.0f
options:UIViewAnimationOptionCurveLinear
animations:^
{
// Move in menus
[self.view layoutIfNeeded];
}
completion:^(BOOL finished)
{
}];

Resize UITableView when I slide up a UIView, just like how it happens when the keyboard is shown

I am sliding up a UIView which has a UIDatePicker as a subview. This is added above my UITableView, but unfortunately some of the tableView rows are still under my UIView.
UITableView is pushed up with keyboard:
UITableView is NOT pushed up with my view, covers up last few fields:
Is it possible to resize the UITableView dynamically when I slide up my view, just like when the keyboard is shown when in a tableView and all the rows are still able to be seen?
EDIT:
Just found a great Apple example: http://developer.apple.com/library/ios/#samplecode/DateCell/Introduction/Intro.html
Yes, it is possible. You could just go ahead and change the size of the UITableView dynamically as part of the animation that slides up the view. If you want to do what the UITableView actually does in response to the keyboard, leave its size alone and instead change its content inset and scroll indicator insets. See my answer here and the examples linked to:
uitableview not resizing with keyboard
Ok, it was easier than I thought. In my case, when I touch a row, I want the picker to show and the tableView to change its height, so I use:
[UIView animateWithDuration:0.4 delay:0.0 options: UIViewAnimationCurveEaseOut animations:^{
self.pickerView.frame = CGRectMake(0, self.view.bounds.size.height-200, self.view.bounds.size.width, self.pickerView.frame.size.height);
// shrink the table vertical size to make room for the date picker
CGRect newFrame = self.tableView.frame;
newFrame.size.height -= self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
} completion:nil];
Then when I click the done button, and want to return the tableView back to the full height and hide my datePicker, I use:
- (void)doneWithPicker:(BOOL)remove {
CGRect newFrame = self.tableView.frame;
newFrame.size.height += self.pickerView.frame.size.height;
self.tableView.frame = newFrame;
[UIView animateWithDuration:0.4 delay:0.0 options: UIViewAnimationCurveEaseOut animations:^{
self.pickerView.frame = CGRectMake(0, self.view.bounds.size.height+300, self.view.bounds.size.width, self.pickerView.frame.size.height);
} completion:^(BOOL finished){
if (remove) {
[self.pickerView removeFromSuperview];
self.pickerView = nil;
}
}];
}
Also, when adding the pickerView as a subview, make sure to add it to the window:
[self.view.window addSubview:self.pickerView];

Pop up tutorial page like Photosynth's

I want to create something that is basically a clone of what photosynth does for their tutorial page. A small "?" button pops up what looks like a new view in a frame that is slightly smaller than the first view, so that you can still see the first view around the edges.
It's a little tough to see from the pic above, but the part around the edges is the old view that the tutorial display popped up over.
My first guess is that I need to use a container view somehow, but I can't find anything on the web about exactly how to do this. I can currently create a container view, hook it up to a new view controller via a segue, and do whatever I want in that new view controller, but the container view is always visible on the view it is contained within. Any help?
BTW, I'm using storyboarding with ARC.
You can add a transparent view to the key window, add a tap gesture recognizer that would dismiss it and the subviews to show the content:
#define OVERLAY_TAG 997
-(void)showTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlay.backgroundColor = [UIColor clearColor];
overlay.userInteractionEnabled = YES;
[keyWindow addSubview:overlay];
UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissTutorial)];
CGFloat border = 10;
CGRect frame = overlay.bounds;
// 20 is the status bar height (sorry for using the number)
frame = CGRectMake(border, border + 20, frame.size.width - border * 2, frame.size.height - border * 2 - 20);
// the black view in the example is probably a scroll view
UIView *blackView = [[UIView alloc] initWithFrame:frame];
blackView.backgroundColor = [UIColor blackColor];
blackView.alpha = 0.0;
[overlay addSubview:dimView];
// add all the subviews for your tutorial
// make it appear with an animation
[UIView animateWithDuration:0.3
animations:^{dimView.alpha = 1;}
completion:^(BOOL finished){[overlay addGestureRecognizer:tapRecognizer];}];
}
-(void)dismissTutorial
{
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
UIView *overlay = [keyWindow viewWithTag:OVERLAY_TAG];
[UIView animateWithDuration:0.3
animations:^{
overlay.alpha = 0.0;
}
completion:^(BOOL finished){
[overlay removeFromSuperview];
}];
}
This way you would remove the tutorial with a simple tap but you can use a button for instance.

How do I stop a modal view from disappearing when rotating on iPad?

I'm using willRotateToInterfaceOrientation to swap views when my iPad rotates. If I have a modal view or an alert view open when my device rotates and swaps views, the view swaps and the alert disappears and does not reappear, even if the alert is "presented" again later.
Edit:
I've narrowed this problem a bit. When a modal view is presented with UIModalPresentationFullScreen, the modal view "survives" rotations.
What can I do to fix this?
Here is my implementation of willRotateToInterfaceOrientation:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
//
// Load an alternate view depending on the orientation
//
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) {
[UIView beginAnimations:#"" context:nil];
[self setView:theLandscapeView];
self.view.bounds = CGRectMake(0, 0, 1024, 768);
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (-90));
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[UIView beginAnimations:#"" context:nil];
[self setView:theLandscapeView];
self.view.bounds = CGRectMake(0, 0, 1024, 768);
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (90));
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationPortrait) {
[UIView beginAnimations:#"" context:nil];
[self setView:thePortraitView];
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (0));
self.view.bounds = CGRectMake(0, 0, 768, 1024);
[UIView commitAnimations];
}else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[UIView beginAnimations:#"" context:nil];
[self setView:thePortraitView];
self.view.transform = CGAffineTransformMakeRotation(kDegreesToRadians * (180));
self.view.bounds = CGRectMake(0, 0, 768, 1024);
[UIView commitAnimations];
}
}
If I were solving this problem, I would do one of the following
Add the alternate views to a parent view, and not change the view property
Create a design for the views that does not require a full view swap, but rearranges or hides subelements of the view.
Present any modal from the root ViewController of the hierarchy
I would work very hard not to swap out the view entirely for the sake of orientation. It seems like something that will continue to present problems even after you have solved this one.
If you swap views, you should also swap modal views I think.
For example, if you present popover controller - it'll automatically dismissed and then appeared with UI rotation.
Here's an idea: keep you main view constant, but change the subview to your portrait or landscape view. something like:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Remove the current subview from the main view
if (self.view.subviews.count) {
[self.view.subviews objectAtIndex:0] removeFromSuperview];
}
// Use your if-else block, but change [self setView:] for [self.view addSubview:]
}
So now when you create your modal, it will be linked to your controller, which now has a constant main view.
Note that I didn't test this, as I'm getting my head back into coding after two weeks off...
Good luck!