TableView-keyboard animation issue with autolayout - ios

I have a tableView inside a view. Below tableView is a toolbar(with textField in it) and below that is the tabbar. This screen is basically for chatting.
I change view's height when keyboard is displayed (decreasing the height) and also when hidden (increasing the height back to original). It works fine when keyboard is displayed.
Issue is when keyboard is hidden, tableView goes little up with a jerk.
The issue is not with the view's animation because when I put delay in animation, then also tableView goes up with jerk right away (even before view animation has started).
When keyboard is displayed :
While hiding keyboard :
Code to animate decrease in height when keyboard is being displayed (THIS WORKS FINE) :
// Remove constraint from view
// Change constraint constant
// Add constraint to view
.
.
.
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
// If at least one chat message
if ([chatData count] != 0)
{
[chatTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([chatData count] - VALUE_ONE) inSection:VALUE_ZERO] atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
Code to animate increase in height when keyboard is being hidden :
// Remove constraint from view
// Change constraint constant
// Add constraint to view
.
.
.
[UIView animateWithDuration:animationDuration delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];

Instead of changing the frame, just set the content inset when your keyboard appears and when the keyboard is hidden use the content offset of table.
tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, heightOfYourKeyboardAndStuff, 0.0);
[tableView setContentOffset: CGPointMake(0.0, -heightOfYourKeyboardAndStuff) animated: YES];
&
[tableView setContentOffset: CGPointMake(0.0, preferedValue) animated: YES];
tableView.contentInset = UIEdgeInsetsZero;
I think it should work fine and smooth.

I see that it was due to change in height of tableView. I was decreasing and increasing it when keyboard is displayed. Also I was scrolling the cells through code using below method, which was changing contentOffset and it was causing jerk.
[chatTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([chatData count] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
The solution is to not to change height of tableView and use below code.
When keyboard is displayed :
// Change table contentInset
UIEdgeInsets contentInset = self.chatTable.contentInset;
contentInset.bottom = keyboardHeight;
UIEdgeInsets scrollIndicatorInsets = self.chatTable.scrollIndicatorInsets;
scrollIndicatorInsets.bottom = keyboardHeight;
[UIView animateWithDuration:animationDuration animations:^{
self.chatTable.contentInset = contentInset;
self.chatTable.scrollIndicatorInsets = scrollIndicatorInsets;
}];
// Optional : Display last cell with animation
if ([chatData count] != 0)
{
[chatTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([chatData count] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
When keyboard is hidden :
// Reset table contentInset
UIEdgeInsets contentInset = self.chatTable.contentInset;
contentInset.bottom = 0.0f;
UIEdgeInsets scrollIndicatorInsets = self.chatTable.scrollIndicatorInsets;
scrollIndicatorInsets.bottom = 0.0f;
[UIView animateWithDuration:animationDuration animations:^{
self.chatTable.contentInset = contentInset;
self.chatTable.scrollIndicatorInsets = scrollIndicatorInsets;
}];
// Optional : Display last cell with animation
if ([chatData count] != 0)
{
[chatTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([chatData count] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

instead of changing the frame animated you should adjust the tableView's contentInset / contentOffset when displaying keyboard
tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, heightOfYourKeyboardAndStuff, 0.0);
[tableView setContentOffset: CGPointMake(0.0, -heightOfYourKeyboardAndStuff) animated: YES];
and vice versa when hiding the keyboard
[tableView setContentOffset: CGPointMake(0.0, preferedValue) animated: YES];
tableView.contentInset = UIEdgeInsetsZero;
in my experirience this works very smoothly! calculate heightOfYourKeyboardAndStuff and preferedValue with custom arithmetics to fit it to your needs.
ps: you might need to store / restore the current contentOffset before showing / after hiding the keyboard.

having this issue in Xamarin when adding fixed height for the ListView Solve it by adding custom renderer then
UIKit.UIKeyboard.Notifications.ObserveWillShow((sender, args) =>
{
//to extend the space between your content and the edges
Control.ContentInset = new UIEdgeInsets(0,0,0,0);
});
UIKit.UIKeyboard.Notifications.ObserveWillHide((sender, args) =>
{
Control.ContentInset = new UIEdgeInsets(0,0,0,0);
});

Related

How to create a shrink animation in uinavigationbar?

I use this code in objective c to make a fade in fadeout animation when the user scrolls up or down the uitableview. Is there any way to make a nice animation like shrinking the navigationbar slowly while the user scrolls to top slowly? The more he scrolls to the top the more the navigationbar should shrink.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollPos = self.tableView.contentOffset.y ;
if(scrollPos >= _currentOffset ){
//hide navbar and fadeout
[UIView animateWithDuration:0.50 animations:^{
[self.navigationController setNavigationBarHidden:YES animated:YES];
self.navigationController.navigationBar.alpha = 0.0f;
}];
} else {
//Slide it up incrementally and fadein, etc.
[UIView animateWithDuration:0.50 animations:^{
[self.navigationController setNavigationBarHidden:NO animated:YES];
self.navigationController.navigationBar.alpha = 1.0f;
}];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
scrollView = self.tableView;
_currentOffset = self.tableView.contentOffset.y;
}
You cannot customize the size of UINavigationBar on your own. You can only hide/appear it. Apple does not allow you to do that.
You can use UIView instead! Just hide your UINavigationBar and put the same frame of UIView. Since UIView is highly customizable, you can do whatever you want.

Objective C: how to resize uitextview height with animation?

I have UITextView (textView) in some other view (extView) in my Objective C project with some text in it. The text in textView is quite long so it could by maximised and minimised. Of course, I'd like to do it with some animation.
I change the height of extView with code:
- (void)setExtViewHeight:(CGFloat)newHeight withAnimation:(CGFloat)duration
{
[self setNeedsUpdateConstraints];
[UIView animateWithDuration:duration
animations:^
{
self.extViewHeight.constant = newHeight;
[self layoutIfNeeded];
[self.textView layoutIfNeeded];
}
completion:^(BOOL finished)
{
[self layoutSubviews];
}
];
}
My problem is that animation are actually works only for extView. So when I try to minimise my view the textView jumps to new height and after that extView height is changing with animation. This jump of textView is really annoying and doesn't look good.
What did I do wrong? Why height of textView doesn't follow the animation?
Update your autolayout constant outside of the animation block.
Something like this should work.
Edit: As Duncan C. suggested if you don't want to update the textView height constant as well, you will at least need your textView to have top and bottom relational constraints to your extView
- (void)setExtViewHeight:(CGFloat)newHeight withAnimation:(CGFloat)duration
{
self.extViewHeight.constant = newHeight;
[UIView animateWithDuration:duration animations:^{
[self layoutIfNeeded];
} completion:nil];
}

Hide NavigationControllerBar when scrolling the tableview

I have implemented a UITableViewController.
The first section is a large image. When the view controller appeared initially, I set the navigationBar to be translucent.
When the tableview is scroll down, the navigationBar.translucent is set to NO and the tableview content frame is moved upwards so that the first section is out of the screen. I implemented the movement in the scrollview delegate :
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
When the tableview is scroll up, the navigation becomes translucent again and the tableview frame is restored.
The problem is, when the scrollview delegate catches the scroll gesture. Once the tableview and navigationBar begins the animation. The scroll action of the tableview stops. Therefore if I want to scroll the tableview to bottom I have to scroll twice, the first time animates the frames and then scroll again, and I think it can be enhanced.
Here is the code.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView == _subTable) {
NSIndexPath * indexPath ;
CGFloat offset = scrollView.contentOffset.y;
if ((offset - currentOffset)>40) {
if (!scrollAnimate) {
self.navigationController.navigationBar.translucent = NO;
[UIView animateWithDuration:0.5 animations:^{
[_mainTable setContentOffset:CGPointMake(0, headHeight) animated:YES];
[_subTable setFrame:CGRectMake(CGRectGetWidth(self.menuTable.frame) , 0, kScreen_Width/3.5*2.5, kScreen_Height-schedualHeight-48)];
[_mainTable setFrame:CGRectMake(0, 0, kScreen_Width, _mainTable.frame.size.height+headHeight)];
[_menuTable setFrame: CGRectMake(0, 0, kScreen_Width/3.5, _mainTable.frame.size.height+headHeight-48)];
}];
if (CGRectGetMaxY(_checkOutBar.frame)!= kScreen_Height-44)
{
[_checkOutBar setFrame:CGRectOffset(_checkOutBar.frame, 0, -44)];
}
scrollAnimate = !scrollAnimate;
frameOffset = !frameOffset;
[_mainTable reloadData];
_checkOutBar.tag = 1000;
}
}
else if((offset - currentOffset)<-40)
{
if (scrollAnimate) {
self.navigationController.navigationBar.translucent = YES;
[UIView animateWithDuration:0.5 animations:^{
[_mainTable setFrame:CGRectMake(0, 0, kScreen_Width, kScreen_Height+self.navigationController.navigationBar.frame.size.height+headHeight)];
[self.mainTable setFrame:CGRectOffset(_mainTable.frame, 0, -(self.navigationController.navigationBar.frame.size.height))];
[_subTable setFrame:CGRectMake(CGRectGetWidth(self.menuTable.frame), 0, kScreen_Width/3.5*2.5, tableHeight)];
[_menuTable setFrame:CGRectMake(0, 0, kScreen_Width/3.5, tableHeight)];
}];
scrollAnimate = !scrollAnimate;
frameOffset = !frameOffset;
[_mainTable reloadData];
if (CGRectGetMaxY(_checkOutBar.frame)!= kScreen_Height) {
[_checkOutBar setFrame:CGRectOffset(_checkOutBar.frame, 0, 44)];
}
_checkOutBar.tag = 2000;
}
}
if ((offset - currentOffset)>0)
{
indexPath = [[_subTable indexPathsForVisibleRows]lastObject];
}
else
{
indexPath = [[_subTable indexPathsForVisibleRows]firstObject];
}
if (indexPath) {
if (indexPath.row == 0) {
selected = indexPath.section;
[_menuTable reloadData];
}
}
currentOffset = offset;
}
}
From Apple documents. set NavigationController. hidesBarsOnSwipe = YES;
hidesBarsOnSwipe
Property
A Boolean value indicating whether the navigation bar hides its bars in response to a swipe gesture.
Declaration:
SWIFT:
var hidesBarsOnSwipe: Bool
OBJECTIVE-C:
#property(nonatomic, readwrite, assign) BOOL hidesBarsOnSwipe
Discussion:
When this property is set to YES, an upward swipe hides the navigation bar and toolbar. A downward swipe shows both bars again. If the toolbar does not have any items, it remains visible even after a swipe. The default value of this property is NO.
Availability:
Available in iOS 8.0 and later.

Animating constraints is breaking other constraints

I have the following tableview at the bottom of my view
It has a height constraint (priority 250) and a constraint to the bottom of the view (priority 1000). The height constraint points to a `IBOutlet in my view controller.
I want to change the height of the table view from 44.0f to 7*44.0f, so what I am doing is this;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.categoriesShown) {
[self hideCategories];
} else {
[self showCategories];
}
self.categoriesShown = !self.categoriesShown;
}
- (void)showCategories
{
self.categoriesHeightConstraint.constant = self.categories.count * 44.0f;
}
- (void)hideCategories
{
self.categoriesHeightConstraint.constant = 44.0f;
}
It works fine. But when I'm trying to animating all this with the following code:
- (void)showCategories
{
[self.categoryTableView layoutIfNeeded];
[UIView transitionWithView:self.categoryTableView duration:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
self.categoriesHeightConstraint.constant = self.categories.count * 44.0f;
[self.categoryTableView layoutIfNeeded];
} completion:nil];
}
- (void)hideCategories
{
[self.categoryTableView layoutIfNeeded];
[UIView transitionWithView:self.categoryTableView duration:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
self.categoriesHeightConstraint.constant = 44.0f;
[self.categoryTableView layoutIfNeeded];
} completion:nil];
}
Then the constraints between the tableview and the bottom of the view is somehow broken and this is what I get when I show and then hide the tableview
Does anyone know why, the constraint is broken but only when I try to animate the changes?
Update: UIButton Constraints
Both buttons have width and height constraints as well a a constraint to the bottom of the view. The button on the left has a leading constraint to the view. The one of the right has a trailing constraint to the view. The also both have a horizontal spacing constraint to the table view as mentioned above.
I think your problem is in this method call:
[UIView transitionWithView:self.categoryTableView duration:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
self.categoriesHeightConstraint.constant = 44.0f;
[self.categoryTableView layoutIfNeeded];
} completion:nil];
This should do what you're looking for:
self.categoriesHeightConstraint.constant = 44.0f;
[UIView animateWithDuration:0.3f animations:^{
[self.view layoutIfNeeded];
//This is assuming the method is called from a view controller.
//You need to call layoutIfNeeded on the superview of what you're animating
}];
Also, Apple documentation says it's good form to make an extra [self.view layoutIfNeeded] call before you change the constraint so that any incomplete constraint changes and be updated. I'll leave that up to you though.
So I think I got the functionality you're looking for:
I gave the table view a height constraint that matched the top of the buttons, but it could be anything.
Then I get the calculated height the table should be and set it:
tableArray = [[NSMutableArray alloc]init];
[tableArray addObject:#"Row 1"];
[tableArray addObject:#"Row 2"];
etc.......
double newHeight = ([tableArray count] * 44);
CGRect newFrame = CGRectMake(basicTable.frame.origin.x, basicTable.frame.origin.y, basicTable.frame.size.width, newHeight);
[basicTable setFrame:newFrame];
And then for your animation I used [UIView animateWithDuration] as shown:
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
tableHgtConst.constant = newHeight;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
}];
I just left the bottom constraint alone, then changing the height constraint to match the height forces the table up instead of down.
Screens:
Simulator: http://i.stack.imgur.com/T7MBr.png
Code File: http://i.stack.imgur.com/GGOkq.png

animating UITableViewHeader to grow/shrink

Im very new to iOS and am trying to figure out how I can animate the tableView header to appear like its sliding down from the top of the view, stay there for 3 seconds, then slide back up and disappear.
I havent done any animation of any kind before so I could really use some help. I dont know where to start. Ive looked at other answers on here but cant seem to figure out how to do it.
ive got my table view appearing just fine with this code in my viewDidLoad of my UITableViewController class
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320,15)];
self.headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 15)];
self.headerLabel.textAlignment = NSTextAlignmentCenter;
self.headerLabel.text = #"some text";
[self.headerView addSubview:self.headerLabel];
self.tableView.tableHeaderView = self.headerView;
self.tableView.tableHeaderView.frame = CGRectMake(0, 0, 320, 15);
I assume I need to animate the height of my frame.
edit: I found this code which seems like it should work, however it crashes my app when the animation runs
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[UIView animateWithDuration:3.0f
delay:0.0f
options: UIViewAnimationOptionBeginFromCurrentState
animations:^{
self.headerView.frame = CGRectMake(0.0f, 0.0f, 320.f, 0.0f);
}
completion:^(BOOL finished) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}];
}
I don't know if you can animate a table view's header view like you want to. With a complex UI object like a Table view Apple tends to treat the views that make up the object as private, and bad things tend to happen when you try to manipulate the view hierarch.
If you're not using auto-layout and you want to animate another view that is part of YOUR view hierarchy then your code might be as simple as something like this:
CGPoint center = myView.center;
CGFloat myViewBottom = myView.frame.origin.y + myView.frame.origin.size.height;
//Move the view above the top of it's superview before starting the animation.
center.y -= myViewBottom;
//Animate the view down into position
[UIView animateWithDuration: .2
animations:
^{
myView.center += myViewBottom;
};
completion:
^{
//As the completion block for the first animation, trigger another
//animation to animate the view away again. This time delay
//for 3 second before the 2nd animation.
[UIView animateWithDuration: .2
delay: 3.0
options: 0
animations:
^{
myView.center -= myViewBottom;
};
completion: nil
}
];
Try to use beginUpdates and endUpdates, here is an example:
in your header
#property NSInteger newOffset;
implementation:
- (void) colapseHeader
{
_newOffset = 50;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 50 + _newOffset;
}
- (void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
_newOffset = 100;
[self.tableView beginUpdates];
[self.tableView endUpdates];
[self performSelector:#selector(colapseHeader) withObject:nil afterDelay:3];
}

Resources