Scrollview's position jumps during animation when contentOffset has value - iphone

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

Related

Convert UIKeyboardFrameEndUserInfoKey to View or Window Coordinates

For the constant UIKeyboardFrameEndUserInfoKey, in the Apple docs it says:
These coordinates do not take into account any rotation factors
applied to the window’s contents as a result of interface orientation
changes. Thus, you may need to convert the rectangle to window
coordinates (using the convertRect:fromWindow: method) or to view
coordinates (using the convertRect:fromView: method) before using it.
So if I use [view1 convertRect:rect fromView:view2]
What would I insert for the above parameters to get it to convert the rotation values correctly? ie:
view1 = ?
rect = ? (the keyboard frame I'm assuming)
view2 = ?
Been trying some things and getting some funny stuff.
The first view should be your view. The second view should be nil, meaning window/screen coordinates. Thus:
NSDictionary* d = [notification userInfo];
CGRect r = [d[UIKeyboardFrameEndUserInfoKey] CGRectValue];
r = [myView convertRect:r fromView:nil];
Now you have the rect that the keyboard will occupy, in terms of your view. If your view is the current view controller's view (or a subview thereof), rotation and so forth are now accounted for.
I tried the accepted answer and found that it does not actually provide the CGRect of the keyboard within the view. I found that I have to convert the CGRect from the UIScreen object to the UIWindow object, and from the UIWindow object to the UIView object:
NSValue * keyboardEndFrame;
CGRect screenRect;
CGRect windowRect;
CGRect viewRect;
// determine's keyboard height
screenRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
windowRect = [self.view.window convertRect:screenRect fromWindow:nil];
viewRect = [self.view convertRect:windowRect fromView:nil];
I use the above to resize the root view to not be hidden by the keyboard:
NSTimeInterval duration;
CGRect frame;
// determine length of animation
duration = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// resize the view
frame = self.view.frame;
frame.size.height -= viewRect.size.height;
// animate view resize with the keyboard movement
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:duration];
self.view.frame = frame;
[UIView commitAnimations];
+ (void)parseKeyboardNotification:(NSNotification *)notification
inRelationToView:(UIView *)view
info:(void(^)(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions))callback
{
NSParameterAssert(notification != nil);
NSParameterAssert(view != nil);
NSDictionary *userInfo = [notification userInfo];
UIViewAnimationCurve animationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
UIViewAnimationOptions animationOption = animationCurve << 16; // https://devforums.apple.com/message/878410#878410
NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
// http://stackoverflow.com/a/16615391/202451
CGRect screenRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect windowRect = [view.window convertRect:screenRect fromWindow:nil];
CGRect viewRect = [view convertRect:windowRect fromView:nil];
callback(animationDuration, viewRect, animationOption);
}
Can be used like this
- (void)keyboardWillShowOrHide:(NSNotification *)notification
{
[AGKeyboardInfo parseKeyboardNotification:notification inRelationToView:self.view info:^(NSTimeInterval keyboardAnimationDuration, CGRect keyboardFrameInView, UIViewAnimationOptions keyboardAnimationOptions) {
[UIView animateWithDuration:keyboardAnimationDuration delay:0 options:keyboardAnimationOptions animations:^{
// do any modifications to your views
} completion:nil];
}];
}

UIScrollView scrolling issue

I have a scrollview which works perfectly until I dismiss the keyboard. After that it scrolls but not all the way down anymore... I am a bit puzzled.
in viewDidLoad I save the frame so I can reset it later
frame = self.scrollView.frame;
NSLog(#"Height beginning: %f",self.scrollView.frame.size.height);
offset = self.scrollView.contentOffset;
For debugging I checked the scrollview's height which is 629
in my keyboardDidHide method I set the old frame back
self.scrollView.frame = frame;
NSLog(#"Height end: %f",self.scrollView.frame.size.height);
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
The debugging output is 629 as well which means that the scrollview's height has been set to the old value. It does scroll but when I let go it bounces all the way back to the beginning...
EDIT:
When using 480 as height it is not filling the whole screen because of iphone 4
Some code
-(void) keyboardDidShow: (NSNotification *)notif
{
NSLog(#"Keyboard is visible");
// If keyboard is visible, return
if (keyboardVisible) {
NSLog(#"Keyboard is already visible. Ignore notification.");
return;
}
// Get the size of the keyboard.
CGRect keyboardEndFrame;
[[notif.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Resize the scroll view to make room for the keyboard
CGRect viewFrame = frame;
viewFrame.size.height = 200;
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = viewFrame;
offset = self.scrollView.contentOffset;
CGRect textFieldRect;
if(!activeField)
textFieldRect = comment.frame;
else
textFieldRect = activeField.frame;
textFieldRect.origin.y += 10;
[self.scrollView scrollRectToVisible:textFieldRect animated:YES];
// Keyboard is now visible
keyboardVisible = YES;
}
-(void) keyboardDidHide: (NSNotification *)notif
{
// Is the keyboard already shown
if (!keyboardVisible) {
NSLog(#"Keyboard is already hidden. Ignore notification.");
return;
}
//viewFrame.size.height -= keyboardEndFrame.size.height;
self.scrollView.frame = frame;
[self.scrollView setContentSize:CGSizeMake(320, 700)];
// Reset the scrollview to previous location
self.scrollView.contentOffset = offset;
// Keyboard is no longer visible
keyboardVisible = NO;
}

Date Picker controller need to set the selected value iphone

am using scrollview for text field when i press that text field the view scrolling down can anyone pls help me why its happening ?
This code am using for scroll
- (void)keyboardWasShown:(NSNotification*)aNotification {
if (keyboardShown)
return;
//static const float deviceHeight = 480;
static const float keyboardHeight = 216;
static const float gap = 5; //gap between the top of keyboard and the control
//Find the controls absolute position in the 320*480 window - it could be nested in other views
CGPoint absolute = [activeField.superview convertPoint:activeField.frame.origin toView:nil];
//NSLog(#"Keyborad Shown %f : %f",absolute.y,(keyboardHeight + gap);
if (absolute.y > (keyboardHeight + gap)) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.3f]; //this is speed of keyboard
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = activeField.superview.frame;
bkgndRect.size.height += kbSize.height;
[activeField.superview setFrame:bkgndRect];
[scrollview setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-92) animated:YES];
[UIView commitAnimations];
}
keyboardShown = YES;
}
// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification {
//[UIView beginAnimations:nil context:nil];
//[UIView setAnimationDuration:0.003f]; //this is speed of keyboard
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollview frame];
viewFrame.size.height += keyboardSize.height;
scrollview.frame = viewFrame;
//[UIView commitAnimations];
keyboardShown = NO;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
activeField = textField;
}
Try This....
This code my be helpful for you to scroll your scrollview up.
(BOOL)textViewShouldBeginEditing:(UITextView *)textView {
[scrView setContentOffset:CGPointMake(0, textView.frame.origin.y - 20)];
return YES;
}

(iphone) uiScrollView sensitivity?

I have 2 questions on uiscrollview.
First question:
I have imageviews on uiscrollview(at bottom of screen) and let users to drag the image view out of uiscroll view.
I place fingers on top of the imageView to drag it out but uiscrollview takes the touch and scrolls.
I guess I need to let the imageView gets the touch event first (if the touch event occurred on imageView)
If finger direction is almost horizontal, give the touch event to scrollview so that it can scroll, otherwise(finger direction is not horizontal) let the user drag the imageView.
(I have scrollView.directionalLockEnabled = YES)
I wonder how this can be done?
Second Question:
User can send the imageView back to uiScrollView by double tapping the imageView.
When I do that, the scrollview is not scrollable until I drag out any imageViews in the scrollView.
Don't know what's causing this problem..
Below is my code to send the imageView back to uiscrollview.
aDragView.userInteractionEnabled = NO;
CGPoint contentOffset = self.puzzlePieceScrollView.contentOffset;
float x = contentOffset.x;
int addIndex = x / widthWithPadding;
aDragView.scrollContainerIndex = addIndex;
CGRect newFrame;
int puzzlePieceCount = 0;
[UIView beginAnimations:#"addDragView" context: [aDragView retain]];
for(UIView* subview in [self.puzzlePieceScrollView subviews])
{
if([subview isKindOfClass: [DragView class]])
{
DragView* dragView = (DragView*)subview;
if(dragView.scrollContainerIndex >= addIndex)
{
dragView.scrollContainerIndex += 1;
newFrame = dragView.frame;
newFrame.origin.x += widthWithPadding;
dragView.frame = newFrame;
}
++puzzlePieceCount;
}
}
newFrame = aDragView.frame;
newFrame.origin.x = [self getOriginXForScrollIndex: addIndex];
newFrame.origin.y = self.frame.origin.y + upperPadding;
aDragView.frame = newFrame;
[UIView setAnimationDuration:0.3];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationFinished:finished:context:)];
[UIView commitAnimations];
float newWidth = std::max((puzzlePieceCount+1) * widthWithPadding, [[UIScreen mainScreen] bounds].size.width );
CGSize newContentSize = self.puzzlePieceScrollView.contentSize;
newContentSize.width = newWidth;
self.puzzlePieceScrollView.contentSize = newContentSize;
- (void) animationFinished:(NSString*)animationId finished:(BOOL)finished context:(void*)context
{
if ([animationId isEqualToString:#"addDragView"])
{
DragView* aDragView = (DragView*)context;
// add to puzzlePieceScrollView
CGPoint p;
p = CGPointMake(aDragView.bounds.size.width * 0.5, aDragView.bounds.size.height * 0.5);
CGPoint newCenter = [aDragView convertPoint: p toView: self.puzzlePieceScrollView];
aDragView.center = newCenter;
[self.puzzlePieceScrollView addSubview: aDragView];
aDragView.userInteractionEnabled = YES;
[aDragView release];
}
self.puzzlePieceScrollView.userInteractionEnabled = YES;
}

iPhone: TableView inside UIScrollview, show vaccillating scrollbars around

I have added some table and other vies in a scrollview. scrolling in tables are working fine. But in the parent scrollview, when scrolling, a vacillating vertical scrollbars are shown, sometimes it even come to the middle of the screen. sometime show at the left side of the screen. and not limited to the vertical scrollbar region. When I set the showsVerticalScrollIndicator = NO, it is not shown. But do you know why the scrollbar is moving around.
DashboardView is a subclass of UIScrollView.
dashboard=[[DashboardView alloc] initWithFrame:fullScreenRect];
dashboard.contentSize = CGSizeMake(320,700); // must do!
dashboard.showsVerticalScrollIndicator = YES;
dashboard.bounces = YES;
self.view = dashboard;
#implementation DashboardView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
}
- (void) layoutSubviews{
NSArray *views = self.subviews;
[UIView beginAnimations:#"CollapseExpand" context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
UIView *view = [views objectAtIndex: 0];
CGRect rect = view.frame;
for (int i = 1; i < [views count]; i++ ) {
view = [views objectAtIndex:i];
view.frame = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height, view.frame.size.width, view.frame.size.height);
rect = view.frame;
}
[UIView commitAnimations];
}
Remember that scrollbars is subviews of scroll too. Exclude it from views array, before doing something with views.
Yap. that solved my problem. It seems Scrollbars are a kind of UIImageView. If anybody need it, this is my new code which works fine.
(void) layoutSubviews{
NSArray *views = self.subviews;
[UIView beginAnimations:#"CollapseExpand" context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
//CGRect fullScreenRect=[[UIScreen mainScreen] applicationFrame];
// [self.view insertSubview:balanceView belowSubview:profileView];
UIView *view = [views objectAtIndex: 0];
CGRect rect = view.frame;
for (int i = 1; i < [views count]; i++ ) {
view = [views objectAtIndex:i];
if ([view isMemberOfClass: [UIView class] ]) {
//CFShow(view);
view.frame = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height, view.frame.size.width, view.frame.size.height);
rect = view.frame;
}
}
[UIView commitAnimations];
}