Setting Up Textfield Animationd To Move TextFields Above Keyboard - iphone

I know there is alot on this topic already. I got the code below from another question, but I have no idea how to set it up to use. Can someone give me a detailed step by step on how to actually setup the process of moving a textfield above the keyboard when the keyboard comes up then moving it back when the editing is done.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
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];
}

Is the textFieldDidBeginEditing: or textFieldDidEndEditing: ever get called?
If not, you might not setting your text field's delegate correctly.
When you declare you text field (or, if you're using IB, in viewDidLoad), add this:
yourTextField.delegate = self;

I would highly recommend using a UITableViewController and put your UITextField into the table. That way the keyboard-hiding issue is solved for you by the system.

Related

UITextField Won't Change Frame on Refocus

I'm having a peculiar problem. I have a view with two UITextFields that start out 280px wide. On focus, I want them to shorten to reveal a button - I'm doing that with the following code:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
CGRect revealButton = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, 221, textField.frame.size.height);
[UIView beginAnimations:nil context:nil];
textField.frame = revealButton;
[UIView commitAnimations];
NSLog(#"%f",textField.frame.size.width);
}
Once editing has ended, they should go back to their original frame:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
CGRect hideButton = CGRectMake(textField.frame.origin.x, textField.frame.origin.y, 280, textField.frame.size.height);
[UIView beginAnimations:nil context:nil];
textField.frame = hideButton;
[UIView commitAnimations];
}
The first time I focus a text field, it works perfectly. However, if I focus the first text field after focusing something else (for example, if I focus the first text field initially, focus the second, and then refocus the first, or if I initially focus the second and then focus the first), it simply won't change its frame. Even more puzzling is the fact that it will log 221 as its width - it just won't show that on the screen. Furthermore, this problem doesn't apply to the second text field.
Any ideas? Thanks in advance...
That's strange, I ran a quick test using two text fields with the exact same code and works every time.
I'd suggest deleting the text fields and connections and rebuild them. Clean all targets and try again.
Edit according to your comments:
If you're using Auto Layout you must not modify the frame of the text fields directly. The actual frames of UI elements are calculated by the system.
For your purpose I'd suggest to set up a width constraint for every text field. Make sure that you only have a left or right spacing constraint not both in addition to the width constraint. To animate it use the following code:
- (NSLayoutConstraint *)widthConstraintForView:(UIView *)view
{
NSLayoutConstraint *widthConstraint = nil;
for (NSLayoutConstraint *constraint in textField.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeWidth)
widthConstraint = constraint;
}
return widthConstraint;
}
- (void)animateConstraint:(NSLayoutConstraint *)constraint toNewConstant:(float)newConstant withDuration:(float)duration
{
[self.view layoutIfNeeded];
[UIView animateWithDuration:duration animations:^{
constraint.constant = newConstant;
[self.view layoutIfNeeded];
}];
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
float newWidth = 221.0f;
NSLayoutConstraint *widthConstraint = [self widthConstraintForView:textField];
[self animateConstraint:widthConstraint toNewConstant:newWidth withDuration:0.5f];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
float newWidth = 280.0f;
NSLayoutConstraint *widthConstraint = [self widthConstraintForView:textField];
[self animateConstraint:widthConstraint toNewConstant:newWidth withDuration:0.5f];
}

iPad keyboard hiding textfield

This is a very common problem in which the keyboard hides the textfield . Also there is lots of solution posted on SO for this.
So currently i am referring following post which is working well in iPad portrait mode but in iPad landscape mode the the view is sliding towards left direction where as i want the view to move up.
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
[self animateTextField: textField up: YES];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self animateTextField: textField up: NO];
}
- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
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];
}
As iPad itself providing good options like "Undock" and "Split" for Keyboard, generally we dont need to arrange Text Field. Although considering you problem if you needed check device current orientation
using [[UIDevice currentDevice] orientation] based on that animate text fields frame.

iPhone Keyboard hiding a UITextView when it comes in?

I am working on an iPhone Application and am attempting to get a Landscape View for a simple, editable UITextView working.
Both interface layouts are specified in NIB files, and I have succeeded in loading them and migrating data when the devices rotates.
However, the UITextView in the Landscape version "runs away" from the iPhone Keyboard, making it impossible to see what you're editing. I have no idea why it is doing this--can anybody help?
Thanks in Advance!
VIDEO OF PROBLEM: http://www.youtube.com/watch?v=hfWBKBA_KjQ
No way to know what you are doing without some code, but you should try something like this:
- (void) animateTextField: (UITextView*) textView up: (BOOL) up
{
const int movementDistance = 80; // tweak as needed
const float movementDuration = 0.3f; // tweak as needed
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];
}
Call this from where you think appropiate (UIKeyboardWillShowNotigication, for instance) and well, it will animate your view in order to show the textview.

how to restrict scrollViewWillBeginDragging to only one sender object?

i´ve built a nested scroll view. in the view.xib there is in the view one scrollview with vertical scrolling named rootScroll. in this one there are two other scrollviews with horizontal scrolling named topScroll and bottomScroll.
my goal is to fade out bottomScroll when user drags the topScroll and fade it in again when decelerating ends.
the code workes fine so far. the only problem is that scrollViewWillBeginDragging gets messages from all three UIScrollViews. i´ve logged the sender and can see that they´re different but anyway i don´t know how to restrict the animation only to messages sent by the topScroll!
how can i distinguish different senders inside of scrollViewWillBeginDragging?
probably an objective-c absolute beginners question. i hope someone will give me a hint anyway.
thank you!
- (void)viewDidLoad {
[super viewDidLoad];
// rootScroll
[rootScroll setScrollEnabled:YES];
[rootScroll setContentSize:CGSizeMake(1024, 1980)];
// topScroll
[topScroll setScrollEnabled:YES];
[topScroll setContentSize:CGSizeMake(3072, 406)];
// bottomScroll
[bottomScroll setScrollEnabled:YES];
[bottomScroll setContentSize:CGSizeMake(3072, 188)];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)sender
{
NSLog(#"will begin dragging, %i", sender);
[UIScrollView beginAnimations:nil context:nil];
[UIScrollView setAnimationDuration:0.15f];
[self.bottomScroll setAlpha:0.0];
[UIScrollView commitAnimations];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
NSLog(#"end position");
[UIScrollView beginAnimations:nil context:nil];
[UIScrollView setAnimationDuration:1.5f];
[self.bottomScroll setAlpha:1.0];
[UIScrollView commitAnimations];
}
Set the tag propeties of the scrollviews in Interface Builder. You can then use [sender tag] in your method to tell them apart.

Change the speed of setContentOffset:animated:?

Is there a way to change the speed of the animation when scrolling a UITableView using setContentOffset:animated:? I want to scroll it to the top, but slowly. When I try the following, it causes the bottom few cells to disappear before the animation starts (specifically, the ones that won't be visible when the scroll is done):
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
[self.tableView setContentOffset:CGPointMake(0, 0)];
[UIView commitAnimations];
Any other way around this problem? There is a private method _setContentOffsetAnimationDuration that works, but I don't want to be rejected from the app store.
[UIView animateWithDuration:2.0 animations:^{
scrollView.contentOffset = CGPointMake(x, y);
}];
It works.
Setting the content offset directly did not work for me. However, wrapping setContentOffset(offset, animated: false) inside an animation block did the trick.
UIView.animate(withDuration: 0.5, animations: {
self.tableView.setContentOffset(
CGPoint(x: 0, y: yOffset), animated: false)
})
I've taken nacho4d's answer and implemented the code, so I thought it would be helpful for other people coming to this question to see working code:
I added member variables to my class:
CGPoint startOffset;
CGPoint destinationOffset;
NSDate *startTime;
NSTimer *timer;
and properties:
#property (nonatomic, retain) NSDate *startTime;
#property (nonatomic, retain) NSTimer *timer;
and a timer callback:
- (void) animateScroll:(NSTimer *)timerParam
{
const NSTimeInterval duration = 0.2;
NSTimeInterval timeRunning = -[startTime timeIntervalSinceNow];
if (timeRunning >= duration)
{
[self setContentOffset:destinationOffset animated:NO];
[timer invalidate];
timer = nil;
return;
}
CGPoint offset = [self contentOffset];
offset.x = startOffset.x +
(destinationOffset.x - startOffset.x) * timeRunning / duration;
[self setContentOffset:offset animated:NO];
}
then:
- (void) doAnimatedScrollTo:(CGPoint)offset
{
self.startTime = [NSDate date];
startOffset = self.contentOffset;
destinationOffset = offset;
if (!timer)
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(animateScroll:)
userInfo:nil
repeats:YES];
}
}
you'd also need timer cleanup in the dealloc method. Since the timer will retain a reference to the target (self) and self has a reference to the timer, some cleanup code to cancel/destroy the timer in viewWillDisappear is likely to be a good idea too.
Any comments on the above or suggestions for improvement would be most welcome, but it is working very well with me, and solves other issues I was having with setContentOffset:animated:.
There is no a direct way of doing this, nor doing the way you wrote it. The only way I can accomplish this is by making the movement/animation by my own.
For example move 1px every 1/10 second should simulate a very slow scroll animation. (Since its a linear animation maths are very easy!)
If you want to get more realistic or fancy and simulate easy-in easy-off effect then you need some maths to calculate a bezier path so you can know the exact position at every 1/10 second, for example
At least the first approach shouldn't be that difficult.
Just use or -performSelector:withObject:afterDelay or NSTimerswith
-[UIScrollView setContentOffset:(CGPoint*)];`
Hope it helps
UIView calculates final view and then animates it. That's why cells that invisible on finish of animation invisible on start too. For prevent this needed add layoutIfNeeded in animation block:
[UIView animateWithDuration:2.0 animations:^{
[self.tableView setContentOffset:CGPointMake(0, 0)];
[self.tableView layoutIfNeeded]
}];
Swift version:
UIView.animate(withDuration: 2) {
self.tableView.contentOffset.y = 10
self.tableView.layoutIfNeeded()
}
I'm curious as to whether you found a solution to your problem. My first idea was to use an animateWithDuration:animations: call with a block setting the contentOffset:
[UIView animateWithDuration:2.0 animations:^{
scrollView.contentOffset = CGPointMake(x, y);
}];
Side effects
Although this works for simple examples, it also has very unwanted side effects. Contrary to the setContentOffset:animated: everything you do in delegate methods also gets animated, like the scrollViewDidScroll: delegate method.
I'm scrolling through a tiled scrollview with reusable tiles. This gets checked in the scrollViewDidScroll:. When they do get reused, they get a new position in the scroll view, but that gets animated, so there are tiles animating all the way through the view. Looks cool, yet utterly useless. Another unwanted side effect is that possible hit testing of the tiles and my scroll view's bounds is instantly rendered useless because the contentOffset is already at a new position as soon as the animation block executes. This makes stuff appear and disappear while they're still visible, as to where they used to be toggled just outside of the scroll view's bounds.
With setContentOffset:animated: this is all not the case. Looks like UIScrollView is not using the same technique internally.
Is there anyone with another suggestion for changing the speed/duration of the UIScrollView setContentOffset:animated: execution?
You can set the duration as follows:
scrollView.setValue(5.0, forKeyPath: "contentOffsetAnimationDuration") scrollView.setContentOffset(CGPoint(x: 100, y: 0), animated: true)
This will also allow you to get all of your regular delegate callbacks.
https://github.com/dominikhofmann/PRTween
subclass UITableview
#import "PRTween.h"
#interface JPTableView : UITableView{
PRTweenOperation *activeTweenOperation;
}
- (void) doAnimatedScrollTo:(CGPoint)destinationOffset
{
CGPoint offset = [self contentOffset];
activeTweenOperation = [PRTweenCGPointLerp lerp:self property:#"contentOffset" from:offset to:destinationOffset duration:1.5];
}
IF all your trying to do is scroll your scrollview I think you should use scroll rect to visible. I just tried out this code
[UIView animateWithDuration:.7
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect scrollToFrame = CGRectMake(0, slide.frame.origin.y, slide.frame.size.width, slide.frame.size.height + kPaddingFromTop*2);
CGRect visibleFrame = CGRectMake(0, scrollView.contentOffset.y,
scrollView.frame.size.width, scrollView.frame.size.height);
if(!CGRectContainsRect(visibleFrame, slide.frame))
[self.scrollView scrollRectToVisible:scrollToFrame animated:FALSE];}];
and it scrolls the scrollview to the location i need for whatever duration i am setting it for. The key is setting animate to false. When it was set to true, the animation speed was the default value set by the method
For people who also have issues with disappearing items while scrolling a UITableView or a UICollectionView you can expand the view itself so that we hold more visible items. This solution is not recommended for situations where you need to scroll a great distance or in situations where the user can cancel the animation. In the app I'm currently working on I only needed to let the view scroll a fixed 100px.
NSInteger scrollTo = 100;
CGRect frame = self.collectionView.frame;
frame.size.height += scrollTo;
[self.collectionView setFrame:frame];
[UIView animateWithDuration:0.8 delay:0.0 options:(UIViewAnimationOptionCurveEaseIn) animations:^{
[self.collectionView setContentOffset:CGPointMake(0, scrollTo)];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.8 delay:0.0 options:(UIViewAnimationOptionCurveEaseIn) animations:^{
[self.collectionView setContentOffset:CGPointMake(0, 0)];
} completion:^(BOOL finished) {
CGRect frame = self.collectionView.frame;
frame.size.height -= scrollTo;
[self.collectionView setFrame:frame];
}];
}];
I use transitionWithView:duration:options:animations:completion:
[UIView transitionWithView:scrollView duration:3 options:(UIViewAnimationOptionCurveLinear) animations:^{
transitionWithView:scrollView.contentOffset = CGPointMake(contentOffsetWidth, 0);
} completion:nil];
UIViewAnimationOptionCurveLinear is an option make animation to occur evenly over.
While I found that in an animation duration, the delegate method scrollViewDidScroll did not called until animation finished.
You can simply use block based animation to animate the speed of scrollview.
First calculate the offset point to which you want to scroll and then simply pass that offset value as here.....
[UIView animateWithDuration:1.2
delay:0.02
options:UIViewAnimationCurveLinear
animations:^{
[colorPaletteScrollView setContentOffset: offset ];
}
completion:^(BOOL finished)
{ NSLog(#"animate");
} ];
here colorPaletteScrollView is my custom scrollview and offset is the value passed .
this code works perfectly fine for me.
Is there a reason you're using setContentOffset and not scrollRectToVisible:animated:?
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated
I would recommend doing it like this:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 320, 0) animated:NO];
[UIView commitAnimations];
Unless that doesnt work. I still think you should try it.
Actually TK189's answer is partially correct.
To achieve a custom duration animated contentOffset change, with proper cell reuse by UITableView and UICollectionView components, you just have to add a layoutIfNeeded call inside the animations block:
[UIView animateWithDuration:2.0 animations:^{
tableView.contentOffset = CGPointMake(x, y);
[tableView layoutIfNeeded];
}];
On Xcode 7.1 - Swift 2.0 :
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
}
return true
}
OR
func textFieldShouldReturn(textField: UITextField) -> Bool {
if(textField.returnKeyType == UIReturnKeyType.Next) {
password.becomeFirstResponder()
}
dispatch_async(dispatch_get_main_queue()) {
UIView.animateWithDuration(0, animations: { self.scrollView!.setContentOffset(CGPointZero,animated: true) })
}
textField.resignFirstResponder()
return true
}
Note: self.scrollView!.setContentOffset(CGPointZero,animated: true) can have different positions depending on the requirement
Example:
let scrollPoint:CGPoint = CGPointMake(0,textField.frame.origin.y/2);
scrollView!.setContentOffset(scrollPoint, animated: true);
I wanted to change the contentOffSet of tableview when textfield begins to edit.
Swift 3.0
func textFieldDidBeginEditing(_ textField: UITextField) {
DispatchQueue.main.async {
UIView.animate(withDuration: 0, animations: {
self.sampleTableView.contentOffset = CGPoint(x: 0, y: 0 - (self.sampleTableView.contentInset.top - 200 ))
})
}
}