问题
I'm trying to animate a UITextField
but I've been caught by a annoying and strange problem. My animation has the following sequence:

In first state the application has the UITextField
, the camera UIButton
and the cancel UIButton
after the camera button that is not been showed because it is been positioned out of the limits of the application. Actually my application has 320 of width and the cancel button origin is (350,7)

In second state, when user touches on UITextField
, the camera button alpha channel is set to 0 and the origin of cancel button is set to (247,7).

The final state is the same as first state after user touched on Return button of keyboard or in cancel button.
During process the UITextField width and the cancel button origin in x axis are animated.
My code is:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
barcodeSearchButton.userInteractionEnabled = NO;
cancelButton.userInteractionEnabled = NO;
CGFloat cancelButtonXOffset = CGRectGetMaxX(barcodeSearchButton.frame) - cancelButton.frame.size.width;
CGFloat searchFieldWidth = searchField.frame.size.width - cancelButton.frame.size.width + barcodeSearchButton.frame.size.width;
[UIView animateWithDuration:0.3
animations:^{
searchField.frame = CGRectMake(searchField.frame.origin.x, searchField.frame.origin.y, searchFieldWidth, searchField.frame.size.height);
cancelButton.alpha = 1.0;
cancelButton.frame = CGRectMake(cancelButtonXOffset, cancelButton.frame.origin.y, cancelButton.frame.size.width,cancelButton.frame.size.height);
barcodeSearchButton.alpha = 0.0;
}
completion:^(BOOL finished) {
barcodeSearchButton.userInteractionEnabled = NO;
cancelButton.userInteractionEnabled = YES;
}];
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
barcodeSearchButton.userInteractionEnabled = NO;
cancelButton.userInteractionEnabled = NO;
[UIView animateWithDuration:0.3
animations:^{
searchField.frame = CGRectMake(searchField.frame.origin.x, searchField.frame.origin.y, searchFieldStartWidth, searchField.frame.size.height);
cancelButton.alpha = 0.0;
cancelButton.frame = CGRectMake(cancelButtonStartX, cancelButton.frame.origin.y, cancelButton.frame.size.width,cancelButton.frame.size.height);
barcodeSearchButton.alpha = 1.0;
}
completion:^(BOOL finished) {
barcodeSearchButton.userInteractionEnabled = YES;
cancelButton.userInteractionEnabled = NO;
}];
}
When the animation is executed until the final state ONLY for the first time, which means, user touched on UITextField, WROTE DOWN (this part is important) some text on field and after that the user touched on keyboard return button the problem appears. The problem is that the typed text in UITextField is animated when the width of UITextField is animated too.
The animation behaves like bellow:
- The
UITextField
stretches back to its initial value. - Cancel button backs to its initial origin
- Camera button is set to 1.0
- The typed text flies from the top-left corner of
UITextField
and it lands intoUITextfield
to the position the it should not have gone.
The problem is that the step 4 should not happen and the problem is it happens only one time. If a start the process again (touch on uitextfield, type some text, touch on return key) the step 4 does not happen.
I spend a day long trying to solve this problem but I was unsuccessful.
回答1:
Thanks for answers of everyone but I've found the solution some time ago. The correct way to start an animation in a UITextField
, depending on the keyboard state, is overriding the methods textFieldShouldBeginEditing
and textFieldShouldEndEditing
.
My first attempt I was overriding textFieldDidBeginEditing
and textFieldDidEndEditing
to create the animations. I don't know the reason to the animations work in textFieldShouldBeginEditing
and textFieldShouldEndEditing
but not in textFieldDidBeginEditing
and textFieldDidEndEditing
. But it just works.
My revised code version:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat cancelButtonXOffset = CGRectGetMaxX(barcodeSearchButton.frame) - cancelButton.frame.size.width;
CGFloat searchFieldWidth = searchField.frame.size.width - cancelButton.frame.size.width + barcodeSearchButton.frame.size.width;
[UIView animateWithDuration:0.3
animations:^{
searchField.frame = CGRectMake(searchField.frame.origin.x, searchField.frame.origin.y, searchFieldWidth, searchField.frame.size.height);
cancelButton.alpha = 1.0;
cancelButton.frame = CGRectMake(cancelButtonXOffset, cancelButton.frame.origin.y, cancelButton.frame.size.width,cancelButton.frame.size.height);
barcodeSearchButton.alpha = 0.0;
} completion:^(BOOL finished) {
searchField.clearButtonMode = UITextFieldViewModeAlways;
}];
return YES;
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
[UIView animateWithDuration:0.3
animations:^{
searchField.frame = CGRectMake(searchField.frame.origin.x, searchField.frame.origin.y, searchFieldStartWidth, searchField.frame.size.height);
cancelButton.alpha = 0.0;
cancelButton.frame = CGRectMake(cancelButtonStartX, cancelButton.frame.origin.y, cancelButton.frame.size.width,cancelButton.frame.size.height);
barcodeSearchButton.alpha = 1.0;
} completion:^(BOOL finished) {
searchField.clearButtonMode = UITextFieldViewModeNever;
}];
return YES;
}
回答2:
Set some placeholder text on the search field, _searchField.text = @"placeholder"
and also set _searchField.clearsOnBeginEditing = YES;
Or you can just clear out the value if it's equal to your placeholder text on textDidBeginEditing
回答3:
{edited to improve the fix} I have this exact same problem, and some further details as well as a small hack that is indeed fixing it for me.
So first the further details:
- The problem only occurs the first time the frame animation expands
the
UITextField
and there is entered text in the field. If theUITextField
is edited again, and the same animations happen again, the problem does not happen. searchField.clearsOnBeginEditing = YES;
does not clear the problem.- I've been using placeholder text the entire time the problem has been observed. It has no effect on the results.
- Changing the text property for 1 frame does have the effect of clearing the problem.
Here is what I am doing to fix it:
In my viewDidLoad
I am setting the text property to a single space, then dispatching once an assignment to an empty string.
-(void)viewDidLoad {
searchField.text = @" ";
dispatch_async(dispatch_get_main_queue(), ^{
searchField.text = @"";
});
}
Then in my methods that shrink and expand the UITextField
, as well as move a cancel button on-screen, I check if this is an instantaneous animation and set the duration to 0.0f.
- (void)showCancelButton {
NSTimeInterval duration = 0.3f;
[UIView animateWithDuration:duration animations:^{
CGPoint buttonOrigin = cancelButton.frame.origin;
CGSize buttonSize = cancelButton.frame.size;
CGFloat buttonTravelDist = buttonSize.width + 10.0f;
CGPoint onscreenPos = CGPointMake(buttonOrigin.x - buttonTravelDist, buttonOrigin.y);
cancelButton.frame = CGRectMake(onscreenPos.x, onscreenPos.y, buttonSize.width, buttonSize.height);
CGPoint fieldOrigin = searchField.frame.origin;
CGSize fieldSize = searchField.frame.size;
searchField.frame = CGRectMake(fieldOrigin.x, fieldOrigin.y, fieldSize.width - buttonTravelDist, fieldSize.height);
}];
}
- (void)hideCancelButton {
NSTimeInterval duration = 0.3f;
[UIView animateWithDuration:duration animations:^{
CGPoint buttonOrigin = cancelButton.frame.origin;
CGSize buttonSize = cancelButton.frame.size;
CGFloat buttonTravelDist = buttonSize.width + 10.0f;
CGPoint onscreenPos = CGPointMake(buttonOrigin.x + buttonTravelDist, buttonOrigin.y);
cancelButton.frame = CGRectMake(onscreenPos.x, onscreenPos.y, buttonSize.width, buttonSize.height);
CGPoint fieldOrigin = searchField.frame.origin;
CGSize fieldSize = searchField.frame.size;
searchField.frame = CGRectMake(fieldOrigin.x, fieldOrigin.y, fieldSize.width + buttonTravelDist, fieldSize.height);
}];
}
This has completely fixed the situation for me.
来源:https://stackoverflow.com/questions/8087413/strange-behavior-when-animating-uitextfield