Strange behavior when animating UITextField - ios

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 into UITextfield 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.

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;
}

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

{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 the
UITextField 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.

Related

animateWithDuration can't animating UIButton's bounds after UIButton has set an image

After I had set an image to an UIButton called bigIcon, I put it inside animateWithDuration with the change of its frame, then I ran the code, the button size changed instantly (has no animation), but it move from origin to destination slowly (has animation), how can I solve this problem? I found out that if I did set an image to the button this problem will disappear.
here is the code:
- (void)bigImage:(MCGodCell *)godCell withImage:(UIImage *)image {
self.bigIcon = [UIButton new];
self.bigIcon.adjustsImageWhenHighlighted = NO;
[self.bigIcon setBackgroundImage:image forState:UIControlStateNormal];
CGFloat iconX = self.tableView.frame.size.width / 2.0;
CGFloat iconY = self.tableView.frame.size.height / 2.0;
self.bigIcon.frame = CGRectMake(iconX, iconY, 1, 1);
[self.tableView addSubview:self.bigIcon];
CGFloat iconW = self.tableView.frame.size.width;
CGFloat iconH = iconW;
iconY = (self.view.frame.size.height - iconH) / 2.0;
[UIView animateWithDuration:0.3 animations:^{
self.bigIcon.frame = CGRectMake(0, iconY, iconW, iconH);
}];
}
You're not seeing the frame change because you're trying to animate it immediately after adding it to the view hierarchy.
UIKit needs to add the button as a subview, and then run the animation on the next UI update pass.
Wrap your animation block inside a dispatch_async block like this:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.bigIcon.frame = CGRectMake(0, iconY, iconW, iconH);
}];
});

UITextView large font text disappear when set setContentOffset with animation

I read all exists solution on stackoverflow for similar trouble but without lack.
Trouble in UITextView when i try scroll text to position with animation.
Top part of text disappear when scroll with animation, but if font size 15pt all is ok but if around 50pt then something goes wrong.
You can see it on video https://www.youtube.com/watch?v=EvIur672Q5k
I also try create my own method to animate scroll with loop and each loop move offset on 0.5pt it works but this utilised processor too much, and i cannot control animation time, because processor overloaded.
https://www.youtube.com/watch?v=Kw5hx3YAdMw
I also try create UITextView programmatically with same result.
I try split animation on parts with linear curve but it so ugly animation with shake.
- (IBAction) start{
_textView.scrollEnabled = NO;
_textView.scrollEnabled = YES;
UITextPosition *Pos2 = [_textView positionFromPosition: _textView.beginningOfDocument offset: 501];
UITextPosition *Pos1 = [_textView positionFromPosition: _textView.beginningOfDocument offset: 500];
UITextRange *range = [_textView textRangeFromPosition:Pos1 toPosition:Pos2];
CGRect result1 = [_textView firstRectForRange:(UITextRange *)range];
result1.origin.x=0;
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
_textView.contentOffset = result1.origin;
} completion:^(BOOL finished){
}];
}
I believe you're trying to do this
- (IBAction)start:(id)sender {
_textView.scrollEnabled = NO;
_textView.scrollEnabled = YES;
NSRange bottom = NSMakeRange(_textView.text.length, 0);
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
[_textView scrollRangeToVisible:bottom];
} completion:nil];
}
This should make a range of the text length and scroll to the bottom of it for you. If I have misunderstood your problem please let me know and I'll do my best to help

UIView Animation breaks TableView

I have a UITableView embedded in a UIView. When a cell is selected, the TableView slides left using UIView Animation so that details underneath can be displayed. When I slide the tableview back to its original frame, the table view can be scrolled over the top of the other elements in the Warpper View. Before the animation, you could only scroll the tableview cells up and down within the boundaries of the tableview. Why is this happening?
- (void)slideTableView:(BOOL)reset
{
//Current pos
CGRect newTableViewFrame = self.tableView.frame;
//Determine direction based on current pos
int newX;
if (reset || newTableViewFrame.origin.x < 0) {
newX = newTableViewFrame.origin.x=0;
}
else {
newX = newTableViewFrame.origin.x - 280;
}
newTableViewFrame.origin.x =newX;
CGFloat animationDuration = 0.2;
CGFloat animationDelay = 0.0;
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.tableView.frame=newTableViewFrame;
}
completion:^(BOOL finished){
}];
}
I had added a shadow to the view earlier and used:
self.clipsToBounds = NO;
self.layer.masksToBounds = NO;
Simply setting back fixes the above:
self.clipsToBounds = YES;
self.layer.masksToBounds = YES;

Second animation

I have several buttons located at different sites in the view (storyboard), and I want that when I click on each of them to go to a given point,
To do this, I keep their location and initial size by:
CGPoint originalCenter1;
CGRect originalsize1;
CGPoint originalCenter2;
CGRect originalsize2;
in viewDidLoad
originalCenter1 = self.boton1.center;
originalsize1 = self.boton1.frame;
originalCenter2 = self.boton2.center;
originalsize2 = self.boton2.frame;
and the IBAction associated with each button, the animation ...
-(IBAction)move:(id)sender{
UIButton * pressedbutton = (UIButton*)sender;
[UIView animateWithDuration:3.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
CGAffineTransform scale = CGAffineTransformMakeScale(3.0, 3.0);
pressedbutton.transform = scale;
switch(pressedbutton.tag)
{
case 0:
pressedbutton.center = CGPointMake(784, 340);
break;
case 1:
pressedbutton.center = CGPointMake(784, 340);
break;
When already all have moved, I have a button Refresh that puts me to the initial position.
-(IBAction)refresh:(id)sender{
self.boton1.frame = originalsize1;
self.boton1.center = originalCenter1;
self.boton1.alpha=1;
self.boton2.frame = originalsize2;
self.boton2.center = originalCenter2;
self.boton2.alpha=1;
The problem is that the next time that pulse buttons, move to the position shown in the animation but the "scale" effect, doesn't work !!
Any help ??
Thanks.
I think you should use block try this...
- (IBAction)tap:(UITapGestureRecognizer *)gesture
{
CGPoint tapLocation = [gesture locationInView:self.view1];
for (UIView *view in self.view1.subviews) {
if (CGRectContainsPoint(view.frame, tapLocation)) {
[UIView animateWithDuration:5.0 animations:^{
[self setRandomLocationForView:view];
}];
}
}
}
- (void)setRandomLocationForView:(UIView *)view
{
[view sizeToFit];
CGRect sinkBounds = CGRectInset(self.view1.bounds, view.frame.size.width/2, view.frame.size.height/2);
CGFloat x = your location.x;
CGFloat y = yout location.y;
view.center = CGPointMake(x, y);
}

UIView animations canceling each other

I have a problem when playing multiple UIView animation blocks.
What I have is a game with a lot of buttons, when you press a button it spawns an UILabel which animates to the top of the screen where is gets "added" to the total point amount, this is done by using 3 nested blocks.
This works well an lets me spawn as many point labels as possible with animations on them.
//setup the label
UILabel *pointIndicator = [[UILabel alloc]initWithFrame:CGRectMake(button.button.frame.origin.x, button.button.frame.origin.y, 60, 30)];
pointIndicator.backgroundColor = [UIColor clearColor];
pointIndicator.font = [UIFont boldSystemFontOfSize:25];
pointIndicator.alpha = 0;
pointIndicator.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
pointIndicator.layer.shadowOpacity = 0.6;
pointIndicator.layer.shadowRadius = 1;
[pointIndicators addObject:pointIndicator];
[self.view addSubview:pointIndicator];
CGPoint scoreLabelPosition = [self.view convertPoint:self.totalScoreLabel.frame.origin fromView:self.totalScoreLabel.superview];
CGSize scoreLabelSize = [self.totalScoreLabel.text sizeWithFont:self.totalScoreLabel.font];
[UILabel animateWithDuration:0.3 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
//Make label appear and move it above button
CGRect frame = pointIndicator.frame;
frame.origin.y -= 30;
pointIndicator.frame = frame;
pointIndicator.alpha = 1;
}completion:^(BOOL finished){
[UILabel animateWithDuration:0.4 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
//Move the label next to the score label
NSInteger YPosition = 0;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad){
YPosition = 15;
}else{
YPosition = 2;
}
CGRect frame = pointIndicator.frame;
frame.origin.x = scoreLabelPosition.x + self.totalScoreLabel.frame.size.width/2 + scoreLabelSize.width/2 + 5;
frame.origin.y = scoreLabelPosition.y - self.totalScoreLabel.frame.size.height/2 + YPosition;
pointIndicator.frame = frame;
}completion:^(BOOL finished){
[UILabel animateWithDuration:0.5 animations:^{
pointIndicator.alpha = 0;
}completion:^(BOOL finished){
//Animate point label to increase a bit in size
CABasicAnimation *pointAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
pointAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pointAnim.duration = 0.1;
pointAnim.repeatCount = 1;
pointAnim.autoreverses = YES;
pointAnim.removedOnCompletion = YES;
pointAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1.0)];
[self.totalScoreLabel.layer addAnimation:pointAnim forKey:nil];
[pointIndicator removeFromSuperview];
[pointIndicators removeObject:pointIndicator];
}];
}];
}];
However, when the game is ended all the buttons animate out of the screen and an image view grows in size out of the middle, each are using 1 animation block.
The problem is that if an UILabel is animating across the screen at the same time as the game ends it cancels the button animation AND the growing image animation. If no UILabels are spawned everything plays as it should. I tried to cancel and remove all the point labels before playing the other animations, but with no luck. It only seems to work if all the previous animations have been completed normally in advance. This is how I remove them:
for(UIView *pointView in pointIndicators){
[pointView.layer removeAllAnimations];
[pointView removeFromSuperview];
}
[pointIndicators removeAllObjects];
[self.totalScoreLabel.layer removeAllAnimations];
all the views which are animated are subviews of the same UIView. I noticed that if I make the point labels subviews of the buttons all the animations can play at the same time.
I cant seem to figure out this behavior, is there some sort of conflict between the animations?

Resources