I have a uiscrollview. Scrollview contains multiple uiview as subview.I want paging like animation effect to scrollview. I have set scrollview.pagingEnabled=NO.
Following is my code
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
lastContentOffset = scrollView.contentOffset.x;
}
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
ScrollDirection scrollDirection;
if (self.lastContentOffset >scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionRight; } else if (self.lastContentOffset < scrollView.contentOffset.x) {
scrollDirection = ScrollDirectionLeft;
}
if (scrollDirection==ScrollDirectionRight) {
CGFloat xOffset = scrollview.contentOffset.x;
CGFloat yOffset = scrollview.contentOffset.y;
if (scrollview.contentOffset.x != scrollview.frame.origin.x)
{
[scrollview setContentOffset:CGPointMake(xOffset- 320, yOffset) animated:YES];
}
NSLog(#" ScrollDirectionRight custom x==%f %f", scrollview.contentOffset.x, scrollview.contentSize.width);
} else if(scrollDirection==ScrollDirectionLeft) {
CGFloat xOffset = scrollview.contentOffset.x;
CGFloat yOffset = scrollview.contentOffset.y;
if ((scrollview.contentOffset.x != scrollview.frame.origin.x) )
{
[scrollview setContentOffset:CGPointMake(xOffset + 320, yOffset) animated:YES];
}
NSLog(#"ScrollDirectionLeft custom x==%f %f", scrollview.contentOffset.x, scrollview.contentSize.width);
}
}
Through this code im getting paging like effect.but the effect is not smooth.
Thanks
Check out UIScrollViewDelegate method
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
In this callback you will modify 'targetContentOffset' to where you like to see scroll offset animation end.
Using it you can make custom pagination implementation a lot smoother.
In my iPad im creating SplitView application. I have a UITableVewtable with multiple rows, each row/cell having three UITextField
When i tap on UITextField some fields are hide by the keyboard.
So im using TPKeyboardAvoidingScrollView framework but it does not works on ios 5.0+.
some times unable to scroll the scroll view on the table.
So I want to move the focussed cell to the middle/ just above the key board at every time
What should I use?
Thanks in Advance.
Try this code. Put this in your .h file :
CGFloat animatedDistance;
In your .m file :
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
static const CGFloat PORTRAIT_KEYBOARD_HEIGHT = 264;
static const CGFloat LANDSCAPE_KEYBOARD_HEIGHT = 352;
Add this static const before your #interface myclass () You can change the keyboard height values above as per your need. And then add the following code :
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect textFieldRect =
[self.view.window convertRect:textField.bounds fromView:textField];
CGRect viewRect =
[self.view.window convertRect:self.view.bounds fromView:self.view];
CGFloat midline = textFieldRect.origin.y + 0.5 * textFieldRect.size.height;
CGFloat numerator = midline - viewRect.origin.y - MINIMUM_SCROLL_FRACTION * viewRect.size. height;
CGFloat denominator =
(MAXIMUM_SCROLL_FRACTION - MINIMUM_SCROLL_FRACTION) * viewRect.size.height;
CGFloat heightFraction = numerator / denominator;
if (heightFraction < 0.0)
{
heightFraction = 0.0;
}
else if (heightFraction > 1.0)
{
heightFraction = 1.0;
}
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait ||
orientation == UIInterfaceOrientationPortraitUpsideDown)
{
animatedDistance = floor(PORTRAIT_KEYBOARD_HEIGHT * heightFraction);
}
else
{
animatedDistance = floor(LANDSCAPE_KEYBOARD_HEIGHT * heightFraction);
}
CGRect viewFrame = self.view.frame;
viewFrame.origin.y -= animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CGRect viewFrame = self.view.frame;
viewFrame.origin.y += animatedDistance;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION];
[self.view setFrame:viewFrame];
[UIView commitAnimations];
}
what you want is to animate your view such as when you touch the textField it should go up and when you end typing it should go down. You can achieve this by implementing two UITextFiled delegate methods and some little animation as follows :
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self startAnimatingTextField: textField up: NO];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self startAnimatingTextField: textField up: YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
[UIView commitAnimations];
}
-(void)startAnimatingTextField:(UITextField *) textField up: (BOOL) up
{
const int distance = 120; /* modify conform your needs */
const float duration = 0.3f;
int movement = (up ? -movementDistance : movementDistance);
[UIView beginAnimations: #"anim" context: nil];
[UIView setAnimationBeginsFromCurrentState: YES];
[UIView setAnimationDuration: movementDuration];
self.view.frame = CGRectOffset(self.view.frame, 0, movement);
[UIView commitAnimations];
}
Hope this helps !
First open the xib file and set the Content insets(In Scrollview Size) to 200
Now update following two methods as below in .m file.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cell=#"Cell";
Cell *customCell= (Cell *)[tableView dequeueReusableCellWithIdentifier:cell];
if (customCell==nil) {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:#"Cell" owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[Cell class]])
{
customCell = (Cell *)object;
break;
}
}
}
customCell.IBTxtfield.tag=indexPath.row;
return customCell;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
NSIndexPath *path=[NSIndexPath indexPathForRow:textField.tag inSection:0];
[IBtblView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
I want to center the text vertically inside a big UITextView that fills the whole screen - so that when there's little of text, say a couple of words, it is centered by height.
It's not a question about centering the text (a property that can be found in IB) but about putting the text vertically right in the middle of UITextView if the text is short, so there are no blank areas in the UITextView.
Can this be done?
First add an observer for the contentSize key value of the UITextView when the view is loaded:
- (void) viewDidLoad {
[textField addObserver:self forKeyPath:#"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
[super viewDidLoad];
}
Then add this method to adjust the contentOffset every time the contentSize value changes:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
UITextView *tv = object;
CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
Because UIKit is not KVO compliant, I decided to implement this as a subclass of UITextView which updates whenever the contentSize changes.
It's a slightly modified version of Carlos's answer which sets the contentInset instead of the contentOffset. In addition to being compatible with iOS 9, it also seems to be less buggy on iOS 8.4.
class VerticallyCenteredTextView: UITextView {
override var contentSize: CGSize {
didSet {
var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
topCorrection = max(0, topCorrection)
contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
}
}
}
If you don't want to use KVO you can also manually adjust offset with exporting this code to a function like this :
-(void)adjustContentSize:(UITextView*)tv{
CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height);
CGFloat inset = MAX(0, deadSpace/2.0);
tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right);
}
and calling it in
-(void)textViewDidChange:(UITextView *)textView{
[self adjustContentSize:textView];
}
and every time you edit the text in the code. Don't forget to set the controller as the delegate
Swift 3 version:
func adjustContentSize(tv: UITextView){
let deadSpace = tv.bounds.size.height - tv.contentSize.height
let inset = max(0, deadSpace/2.0)
tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right)
}
func textViewDidChange(_ textView: UITextView) {
self.adjustContentSize(tv: textView)
}
For iOS 9.0.2. we'll need to set the contentInset instead. If we KVO the contentOffset, iOS 9.0.2 sets it to 0 at the last moment, overriding the changes to contentOffset.
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
UITextView *tv = object;
CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
[tv setContentInset:UIEdgeInsetsMake(topCorrect,0,0,0)];
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:NO];
[questionTextView addObserver:self forKeyPath:#"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
}
I used 0,0, and 0 for the left,bottom and right edge insets respectively. Make sure to calculate those as well for your use case.
It is the simple task using NSLayoutManager to get real text size of the NSTextContainer
class VerticallyCenteredTextView: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
let rect = layoutManager.usedRect(for: textContainer)
let topInset = (bounds.size.height - rect.height) / 2.0
textContainerInset.top = max(0, topInset)
}
}
Don't use contentSize and contentInset in your final calculations.
Here's a UITextView extension that centers content vertically:
extension UITextView {
func centerVertically() {
let fittingSize = CGSize(width: bounds.width, height: CGFloat.max)
let size = sizeThatFits(fittingSize)
let topOffset = (bounds.size.height - size.height * zoomScale) / 2
let positiveTopOffset = max(0, topOffset)
contentOffset.y = -positiveTopOffset
}
}
You can set it up directly with only constraints:
There are 3 constraints i added to align text vertically and horizontally in constraints as below :
Make height 0 and add constraints greater than
Add vertically align to parent constraints
Add horizontally align to parent constraints
I just created a custom vertically centered text view in Swift 3:
class VerticallyCenteredTextView: UITextView {
override var contentSize: CGSize {
didSet {
var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
topCorrection = max(0, topCorrection)
contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
}
}
}
Ref: https://geek-is-stupid.github.io/2017-05-15-how-to-center-text-vertically-in-a-uitextview/
func alignTextVerticalInTextView(textView :UITextView) {
let size = textView.sizeThatFits(CGSizeMake(CGRectGetWidth(textView.bounds), CGFloat(MAXFLOAT)))
var topoffset = (textView.bounds.size.height - size.height * textView.zoomScale) / 2.0
topoffset = topoffset < 0.0 ? 0.0 : topoffset
textView.contentOffset = CGPointMake(0, -topoffset)
}
I have a textview that I'm using with autolayout and with setting the lineFragmentPadding and textContainerInset to zero. None of the solutions above worked in my situation. However, this works for me. Tested with iOS 9
#interface VerticallyCenteredTextView : UITextView
#end
#implementation VerticallyCenteredTextView
-(void)layoutSubviews{
[self recenter];
}
-(void)recenter{
// using self.contentSize doesn't work correctly, have to calculate content size
CGSize contentSize = [self sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)];
CGFloat topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0;
self.contentOffset = CGPointMake(0, -topCorrection);
}
#end
I have also this problem and I solved it with a UITableViewCell with UITextView. I created method in a custom UITableViewCell subclass, property statusTextView:
- (void)centerTextInTextView
{
CGFloat topCorrect = ([self.statusTextView bounds].size.height - [self.statusTextView contentSize].height * [self.statusTextView zoomScale])/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
self.statusTextView.contentOffset = (CGPoint){ .x = 0, .y = -topCorrect };
And call this method in methods:
- (void)textViewDidBeginEditing:(UITextView *)textView
- (void)textViewDidEndEditing:(UITextView *)textView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
This solution worked for me with no issues, you can try it.
Swift 3:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textField.frame = self.view.bounds
var topCorrect : CGFloat = (self.view.frame.height / 2) - (textField.contentSize.height / 2)
topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect
textField.contentInset = UIEdgeInsetsMake(topCorrect,0,0,0)
}
Add to Carlos answer, just in case you have text in tv bigger then tv size you don't need to recenter text, so change this code:
tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
to this:
if ([tv contentSize].height < [tv bounds].size.height) {
tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
Auto-layout solution:
Create a UIView that acts as a container for the UITextView.
Add the following constraints:
TextView: Align leading space to: Container
TextView: Align trailing space to: Container
TextView: Align center Y to: Container
TextView: Equal Height to: Container, Relation: ≤
You can try below code, no observer mandatorily required. observer throws error sometimes when view deallocates.
You can keep this code in viewDidLoad, viewWillAppear or in viewDidAppear anywhere.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^(void) {
UITextView *tv = txtviewDesc;
CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
});
});
I did it like this: first of all, I embedded the UITextView in an UIView (this should work for mac OS too). Then I pinned all four sides of the external UIView to the sides of its container, giving it a shape and size similar or equal to that of the UITextView. Thus I had a proper container for the UITextView. Then I pinned the left and right borders of the UITextView to the sides of the UIView, and gave the UITextView a height. Finally, I centered the UITextView vertically in the UIView. Bingo :) now the UITextView is vertically centered in the UIView, hence text inside the UITextView is vertically centered too.
UITextView+VerticalAlignment.h
// UITextView+VerticalAlignment.h
// (c) The Internet 2015
#import <UIKit/UIKit.h>
#interface UITextView (VerticalAlignment)
- (void)alignToVerticalCenter;
- (void)disableAlignment;
#end
UITextView+VerticalAlignment.m
#import "UITextView+VerticalAlignment.h"
#implementation UITextView (VerticalAlignment)
- (void)alignToVerticalCenter {
[self addObserver:self forKeyPath:#"contentSize" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
UITextView *tv = object;
CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
- (void)disableAlignment {
[self removeObserver:self forKeyPath:#"contentSize"];
}
#end
I fixed this problem by creating extension to center height vertically.
SWIFT 5:
extension UITextView {
func centerContentVertically() {
let fitSize = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
let size = sizeThatFits(fitSize)
let heightOffset = (bounds.size.height - size.height * zoomScale) / 2
let positiveTopOffset = max(0, heightOffset)
contentOffset.y = -positiveTopOffset
}
}
Solution for iOS10 in RubyMotion:
class VerticallyCenteredTextView < UITextView
def init
super
end
def layoutSubviews
self.recenter
end
def recenter
contentSize = self.sizeThatFits(CGSizeMake(self.bounds.size.width, Float::MAX))
topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0;
topCorrection = 0 if topCorrection < 0
self.contentInset = UIEdgeInsetsMake(topCorrection, 0, 0, 0)
end
end
Ha ii everybody,i am familiar with pullDownToRefresh in tableview but in want pulluptorefresh in my application like the pocket-sword bible application chapter loading pull-up for next-chapter and pulldown for previous chapter,i done the ordinary pulldown method,but i want pull-up,i tried to customize the existing pulldown code but no luck,i tried more than 4 days for this. i want to implement pull-up below the footer just like the pulldown just-above the header.
anyone have any idea about this please help me.
Kind regards Nipin.
- (void)addPullToRefreshHeader {
refreshHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0 - REFRESH_HEADER_HEIGHT, 320, REFRESH_HEADER_HEIGHT)];
refreshHeaderView.backgroundColor = [UIColor clearColor];
refreshLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, REFRESH_HEADER_HEIGHT)];
refreshLabel.backgroundColor = [UIColor lightGrayColor];
refreshLabel.font = [UIFont boldSystemFontOfSize:16.0];
refreshLabel.textColor = [UIColor whiteColor];
refreshLabel.textAlignment = UITextAlignmentCenter;
refreshArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"arrow.png"]];
refreshArrow.frame = CGRectMake((REFRESH_HEADER_HEIGHT - 27) / 2,
(REFRESH_HEADER_HEIGHT - 44) / 2,
27, 44);
refreshSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
refreshSpinner.frame = CGRectMake((REFRESH_HEADER_HEIGHT - 20) / 2, (REFRESH_HEADER_HEIGHT - 20) / 2, 20, 20);
refreshSpinner.hidesWhenStopped = YES;
[refreshHeaderView addSubview:refreshLabel];
[refreshHeaderView addSubview:refreshArrow];
[refreshHeaderView addSubview:refreshSpinner];
[self.table addSubview:refreshHeaderView];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (isLoading) return;
isDragging = YES;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (isLoading) {
// Update the content inset, good for section headers
if (scrollView.contentOffset.y > 0)
self.table.contentInset = UIEdgeInsetsZero;
else if (scrollView.contentOffset.y >= -REFRESH_HEADER_HEIGHT)
self.table.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
} else if (isDragging && scrollView.contentOffset.y < 0) {
// Update the arrow direction and label
[UIView beginAnimations:nil context:NULL];
if (scrollView.contentOffset.y < -REFRESH_HEADER_HEIGHT) {
// User is scrolling above the header
refreshLabel.text = #"Release to previous chapter...";
[refreshArrow layer].transform = CATransform3DMakeRotation(M_PI, 0, 0, 1);
} else { // User is scrolling somewhere within the header
refreshLabel.text = #"Pull down to previous chapter..";
[refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1);
}
[UIView commitAnimations];
}
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (isLoading) return;
isDragging = NO;
if (scrollView.contentOffset.y <= -REFRESH_HEADER_HEIGHT) {
// Released above the header
[self startLoading];
}
}
- (void)startLoading {
isLoading = YES;
delegate.selectedChapter = [NSString stringWithFormat:#"%d",[delegate.selectedChapter intValue] + 1];
[delegate reloadVerses];
[self resetReadViewToVerse:1];
// Show the header
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
self.table.contentInset = UIEdgeInsetsMake(REFRESH_HEADER_HEIGHT, 0, 0, 0);
refreshLabel.text = #"Loading...";
refreshArrow.hidden = YES;
[refreshSpinner startAnimating];
[UIView commitAnimations];
// Refresh action!
[self refresh];
}
- (void)stopLoading {
isLoading = NO;
// Hide the header
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDidStopSelector:#selector(stopLoadingComplete:finished:context:)];
self.table.contentInset = UIEdgeInsetsZero;
[refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1);
[UIView commitAnimations];
}
- (void)stopLoadingComplete:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
// Reset the header
refreshLabel.text =#"Pull down to previous chapter..";
refreshArrow.hidden = NO;
[refreshSpinner stopAnimating];
}
- (void)refresh {
// This is just a demo. Override this method with your custom reload action.
// Don't forget to call stopLoading at the end.
[self performSelector:#selector(stopLoading) withObject:nil afterDelay:2.0];
}
I dont know exactly about your task. But try out following code which identifies the scrollPosition of UITableView :
NSIndexPath* ipath = [NSIndexPath indexPathForRow: cells_count-1 inSection: sections_count-1];
[tableView scrollToRowAtIndexPath: ipath atScrollPosition: UITableViewScrollPositionTop animated: YES]
For more reference go to Link
I'm using the TPKeyboardAvoidingScrollView found here: https://github.com/michaeltyson/TPKeyboardAvoiding
Here are the relevant code blocks from that class:
- (void)keyboardWillShow:(NSNotification*)notification {
if ( !CGRectEqualToRect(priorFrame, CGRectZero) ) return;
UIView *firstResponder = [self findFirstResponderBeneathView:self];
if ( !firstResponder ) {
// No child view is the first responder - nothing to do here
return;
}
priorFrame = self.frame;
// Use this view's coordinate system
CGRect keyboardBounds = [self convertRect:[[[notification userInfo] objectForKey:_UIKeyboardFrameEndUserInfoKey] CGRectValue] fromView:nil];
CGRect screenBounds = [self convertRect:[UIScreen mainScreen].bounds fromView:nil];
if ( keyboardBounds.origin.y == 0 ) keyboardBounds.origin = CGPointMake(0, screenBounds.size.height - keyboardBounds.size.height);
CGFloat spaceAboveKeyboard = keyboardBounds.origin.y - self.bounds.origin.y;
CGFloat offset = -1;
CGRect newFrame = self.frame;
newFrame.size.height -= keyboardBounds.size.height -
((keyboardBounds.origin.y+keyboardBounds.size.height)
- (self.bounds.origin.y+self.bounds.size.height));
CGRect firstResponderFrame = [firstResponder convertRect:firstResponder.bounds toView:self];
if ( firstResponderFrame.origin.y + firstResponderFrame.size.height >= screenBounds.origin.y + screenBounds.size.height - keyboardBounds.size.height ) {
// Prepare to scroll to make sure the view is above the keyboard
offset = firstResponderFrame.origin.y + self.contentOffset.y;
if ( self.contentSize.height - offset < newFrame.size.height ) {
// Scroll to the bottom
offset = self.contentSize.height - newFrame.size.height;
} else {
if ( firstResponder.bounds.size.height < spaceAboveKeyboard ) {
// Center vertically if there's room
offset -= floor((spaceAboveKeyboard-firstResponder.bounds.size.height)/2.0);
}
if ( offset + newFrame.size.height > self.contentSize.height ) {
// Clamp to content size
offset = self.contentSize.height - newFrame.size.height;
}
}
}
// Shrink view's height by the keyboard's height, and scroll to show the text field/view being edited
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
self.frame = newFrame;
if ( offset != -1 ) {
[self setContentOffset:CGPointMake(self.contentOffset.x, offset) animated:YES];
}
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification*)notification {
if ( CGRectEqualToRect(priorFrame, CGRectZero) ) return;
// Restore dimensions to prior size
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:[[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
[UIView setAnimationDuration:[[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]];
self.frame = priorFrame;
priorFrame = CGRectZero;
[UIView commitAnimations];
}
I'm having an issue that when the keyboard starts to hide, the scrollView immediately jumps up so it's origin is above the top of the window. It then animates into its proper position.
I've found out that this only happens when the contentOffset is anything > 0 when the animation runs.
While debugging I noticed the content offset was 54 when the keyboard dismiss animation begins.
I found that if I added this code to the beginning of keyboardWillHide:
priorFrame.size.height -= self.contentOffset;
...then no jump would occur and the animation would run properly, but the scrollView would be too short in the end.
Any ideas?
Also note that the normal frame for my scrollView is (0, 44, 320, 416) because it is positioned below a title bar.
It seems like this might be a bug in UIKit, unsure. I found a fix by animating contentInset instead of the frame.size.height. You can see it in my fork of the project http://github.com/wordofchristian/TPKeyboardAvoiding