UINavigationBar hiding animation interferes with UIScrollView contentOffset animation - ios

In an iPhone layout, I am hiding the UINavigationBar and at the same time I want the content of the UIScrollView beneath it to stay in the same place (scroll negatively while animating the nav bar height to zero):
[UIView animateWithDuration:0.3 animations:^{
CGFloat navBarHeight = CGRectGetHeight(weakSelf.navigationController.navigationBar.frame);
[[weakSelf navigationController] setNavigationBarHidden:YES animated:NO];
weakSelf.scrollView.bounds = CGRectOffset(weakSelf.scrollView.bounds, 0, -navBarHeight);
}];
The end result of the animation is ok - but during the animation, the scroll view jumps to its new position (- navbar height) and remains there until the end.
Do these two property animations interfere with each other?

Related

Does UIScrollview works correct with UIStatusbar?

my problem is related with UIScrollview. lets describe it,
I have signup screen which has a scrollview, initially scrolling is not enabled. when keyboard appears I will enable scrollview and when keyboard hides again I am disabling scrolling. my scrollview’s width and height is same as its default view, I have applied horizontal, and vertical centre in container as well as top, bottom, leading and trailing edges are zero (i.e. it is equal to default view). I have a signup button which navigates to signup screen, and applied bottom constraints (bottom space constraints = 0), also I m using keyboard notification for appear and hide.
Actual Problem:
when tap textfield keyboard appears, scrollview scrolls, and when I dismiss the keyboard the scrollview came down, but this time the signup button will move little bit up (like bottom space has 20 points constraints).
First time its like scrollview starts after status bar, but when keyboard appears and hides its like scrollview is renders over view including status bar.
Do I need to add any constraints related to Top/Bottom Layout Guide in IB?
or do I need to add any constraints related to in viewDidLoad
Code for Keyboard notification.
-(void)keyboardWillShow:(NSNotification *)notification {
[self.navigationController.navigationBar setBackgroundImage:nil
forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = nil;
self.ContentScrollView.scrollEnabled=YES;
NSDictionary *userInfo = [notification userInfo];
CGRect keyboardFrameInWindow;
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];
// the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
CGRect keyboardFrameInView = [self.ContentScrollView convertRect:keyboardFrameInWindow fromView:nil];
CGRect scrollViewKeyboardIntersection = CGRectIntersection(self.ContentScrollView.frame, keyboardFrameInView);
UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);
// this is an old animation method, but the only one that retains compatibility between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
[UIView animateWithDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue] delay:0.0 options:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue] animations:^{
self.ContentScrollView.contentInset = newContentInsets;
self.ContentScrollView.scrollIndicatorInsets = newContentInsets;
/*
* Depending on visual layout, _activeField should either be the input field (UITextField,..) or another element
* that should be visible, e.g. a purchase button below an amount text field
* it makes sense to set _activeField in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
*/
if (_activeField) {
CGRect controlFrameInScrollView = [self.ContentScrollView convertRect:_activeField.bounds fromView:_activeField]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, 0); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.
CGFloat controlVisualOffsetToTopOfScrollview = (controlFrameInScrollView.origin.y - self.ContentScrollView.contentOffset.y)+10;
CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;
// this is the visible part of the scroll view that is not hidden by the keyboard
CGFloat scrollViewVisibleHeight = self.ContentScrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;
if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
// scroll up until the control is in place
CGPoint newContentOffset = self.ContentScrollView.contentOffset;
newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);
// make sure we don't set an impossible offset caused by the "nice visual offset"
// if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
CGFloat maxScrollViewHeight = MAX(self.ContentScrollView.frame.size.height, self.ContentScrollView.contentSize.height);
newContentOffset.y = MIN(newContentOffset.y, maxScrollViewHeight - scrollViewVisibleHeight);
[self.ContentScrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
} else if (controlFrameInScrollView.origin.y < self.ContentScrollView.contentOffset.y) {
// if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
CGPoint newContentOffset = self.ContentScrollView.contentOffset;
newContentOffset.y = controlFrameInScrollView.origin.y;
[self.ContentScrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
}
}
} completion:NULL];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
[UIView animateWithDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]doubleValue ] delay:0.01 options:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]intValue] animations:^{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
self.ContentScrollView.contentInset = contentInsets;
self.ContentScrollView.scrollIndicatorInsets = contentInsets;
CGPoint scrollPoint;
self.ContentScrollView.scrollEnabled=NO;
scrollPoint = CGPointMake(0.0, 0.0);
[self.ContentScrollView setContentOffset:scrollPoint animated:YES];
} completion:^(BOOL finished){
__weak typeof(self) weakSelf=self;
[weakSelf.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
weakSelf.navigationController.navigationBar.shadowImage = [UIImage new];
}];
}
Image
If further required I will send the screen shots of before and after keyboard notification.
Thank You.
From Documentation
A Boolean value that indicates whether the view controller should
automatically adjust its scroll view insets.
Declaration
#property(nonatomic, assign) BOOL automaticallyAdjustsScrollViewInsets
Discussion
Default value is YES, which allows the view controller to adjust its
scroll view insets in response to the screen areas consumed by the
status bar, navigation bar, and toolbar or tab bar. Set to NO if you
want to manage scroll view inset adjustments yourself, such as when
there is more than one scroll view in the view hierarchy.
Write self. automaticallyAdjustsScrollViewInsets=NO; in viewDidLoad and try

Content offset in scrollview

I have created my own UITabBar and I'm changing the views with
[self transitionFromViewController: oldC toViewController: newC
duration: 0.1 options:0
animations:^{
newC.view.frame = oldC.view.frame;
}
completion:^(BOOL finished) {
[oldC removeFromParentViewController];
[newC didMoveToParentViewController:self];
}];
This app is working in iOS 7 and 6. In iOS 7 is working great.
But in iOS 6 when I have a UIScrollView the view controller, if I scroll down to the bottom and I change the tab and I come back again the UIScrollView shows a extra gap in the bottom:
I know the problem is that all the views have been moved up, because their frames are changed. But the content offset and inset are the right ones and the content size in the scrollview it's the same.
That happens in all view controller with UIScrollView, so it's not a autolayout problem, not at least in my rules.
Any idea what could I test?

iOS - using UISearchDisplayController with UISearchBar that is UIBarButtonItem in UIToolbar

Has anyone tried using a UISearchDisplayController with a UISearchBar that is a UIBarButtonItem in a UIToolbar?
I would appreciate tips on how to do this successfully.
Currently whenever the search controller pops up it redraws the UISearchBar and I'm struggling to maintain a similar look to the UIToolbar
Usually you don't want to put a search bar inside a toolbar, however, it seems you want to do something similar to what I did.
So here is how I did it, it may be called a hack, but it works like a charm :)
First you have to set it up in interface builder like this:
Notice that the search is not a child of toolbar, instead it is above.
The search bar should have "clear color" background and flexible left, right and width autoresizing masks.
You put a 1-pixel label with black background below the toolbar, ie. [x=0, y=44, width=320 or frame width, height=1], also flexible left, right and width autoresizing masks.This is to hide the one visible pixel you get, after the search display controller shows the table view. Try it without it to understand what I mean.
You setup any tool bar items and be sure to have outlets for them, since you will be needing those.
and now for the code ...
when you start searching:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// animate the search bar to the left ie. x=0
[UIView animateWithDuration:0.25f animations:^{
CGRect frame = controller.searchBar.frame;
frame.origin.x = 0;
controller.searchBar.frame = frame;
}];
// remove all toolbar items
[self.toolbar setItems:nil animated:YES];
}
when you end searching
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
// animate search bar back to its previous position and size
// in my case it was x=55, y=1
// and reduce its width by the amount moved, again 55px
[UIView animateWithDuration:0.25f
delay:0.0f
// the UIViewAnimationOptionLayoutSubviews is IMPORTANT,
// otherwise you get no animation
// but some kind of snap-back movement
options:UIViewAnimationOptionLayoutSubviews
animations:^{
CGRect frame = self.toolbar.frame;
frame.origin.y = 1;
frame.origin.x = 55;
frame.size.width -= 55;
controller.searchBar.frame = frame;
}
completion:^(BOOL finished){
// when finished, insert any tool bar items you had
[self.toolbar setItems:[NSArray arrayWithObject:self.currentLocationButton] animated:YES];
}];
}
With this I get the following with a nice animation :)

UISearchBar with UISearchDisplayController animates outside screen

I have standard iPad view controller which has a custom navigation bar at the top. In the xib-file I've added a UISearchBar aligned to the right edge of the view. The search bar is 320px in width. I init a searchdisplaycontroller like this:
// Search display controller
self.mySearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar
contentsController:self];
_mySearchDisplayController.delegate = self;
_mySearchDisplayController.searchResultsDataSource = self;
_mySearchDisplayController.searchResultsDelegate = self;
The problem is that when I press the search bar, the bar resizes to be the full width of the entire view, but keeps its x-position. This means that it stretches far outside the screen. I'm guessing it has something to do with the "Cancel" button that slides in next to a search bar. If I place the search bar to the far left in the screen, it animates to the full width of the screen and the cancel button is visible.
Anyone has a solution for this?
You can animate the frame of your UISearchBar in the searchDisplayControllerWillBeginSearch method to correct its position like this:
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
// animate the search bar to the left ie. x=0
[UIView animateWithDuration:0.25f animations:^{
CGRect frame = controller.searchBar.frame;
frame.origin.x = 0;
controller.searchBar.frame = frame;
}];
// remove all toolbar items if you need to
[self.toolbar setItems:nil animated:YES];
}
and the animate it back again when finished searching:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
// animate search bar back to its previous position and size
// in my case it was x=55, y=1
// and reduce its width by the amount moved, again 55px
[UIView animateWithDuration:0.25f
delay:0.0f
// the UIViewAnimationOptionLayoutSubviews is IMPORTANT,
// otherwise you get no animation
// but some kind of snap-back movement
options:UIViewAnimationOptionLayoutSubviews
animations:^{
CGRect frame = self.toolbar.frame;
frame.origin.y = 1;
frame.origin.x = 55;
frame.size.width -= 55;
controller.searchBar.frame = frame;
}
completion:^(BOOL finished){
// when finished, insert any tool bar items you had
[self.toolbar setItems:[NSArray arrayWithObjects: /* put your bar button items here */] animated:YES];
}];
}
I have answered a similar question, you can check it here, I've put some images too.
The only thing you have to do is to adapt the code for the iPad.

Default iOS UINavigationBar animation isn't smooth

I'm working on an application and need to hide the UINavigationBar (and toolbar) to provide a fullscreen mode in the in-app browser.
When the app run this code the animation work just fine.
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.navigationController setToolbarHidden:YES animated:YES];
When I want to exit from the full-screen mode the animation isn't smooth at all.
[self.navigationController setNavigationBarHidden:NO animated:YES];
[self.navigationController setToolbarHidden:NO animated:YES];
During the animation a black rectangle is visible under the navigation bar, I think it is the UIWebView that resize itself (the toolbar animation work just fine.)
Any idea on how I can solve this problem?
Instead of using setNavigationBarHidden:animated: for hiding the navigation bar, try this:
In your view controller's viewDidLoad compute different frames for your navigation bar and your view:
// The normal navigation bar frame, i.e. fully visible
normalNavBarFrame = self.navigationController.navigationBar.frame;
// The frame of the hidden navigation bar (moved up by its height)
hiddenNavBarFrame = normalNavBarFrame;
hiddenNavBarFrame.origin.y -= CGRectGetHeight(normalNavBarFrame);
// The frame of your view as specified in the nib file
normalViewFrame = self.view.frame;
// The frame of your view moved up by the height of the navigation bar
// and increased in height by the same amount
fullViewFrame = normalViewFrame;
fullViewFrame.origin.y -= CGRectGetHeight(normalNavBarFrame);
fullViewFrame.size.height += CGRectGetHeight(normalNavBarFrame);
When you want to go fullscreen:
[UIView animateWithDuration:0.3
animations:^{
self.navigationController.navigationBar.frame = hiddenNavBarFrame;
self.view.frame = fullViewFrame;
} completion:^(BOOL finished) {
}];
When you want to return to normal:
[UIView animateWithDuration:0.3
animations:^{
self.navigationController.navigationBar.frame = normalNavBarFrame;
self.view.frame = normalViewFrame;
} completion:^(BOOL finished) {
}];
Tested this in the iOS 5.1 emulator. Hope you can use that. The "black rectangle" must be the default background color of your window, i.e. a gap between your navigation bar and your view.

Resources