View Transition stammering when KeyBoard comes up iOS - ios

Here is a transition code I have for Showing ViewB from ViewA
CGPoint c = thepoint;
CGFloat tx = c.x - floorf(theview.center.x) + 10;
CGFloat ty = c.y - floorf(theview.center.y) + 100;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
// Transforms
CGAffineTransform t = CGAffineTransformMakeTranslation(tx, ty);
t = CGAffineTransformScale(t, 0.1, 0.1);
theview.transform = t;
theview.layer.masksToBounds = YES;
[theview setTransform:CGAffineTransformIdentity];
}
completion:^(BOOL finished) {
}];
Now that the transition went very smooth without issues.
When in my ViewB, I had a text box with default focus. (In viewDidAppear)
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:NO];
[self performSelector:#selector(focusCommentText) withObject:nil afterDelay:0.5];
}
- (void) focusCommentText {
[self focusTextbox:commentText];
}
- (void) focusTextbox : (UITextView *) textView{
#try {
[ textView becomeFirstResponder];
}
#catch (NSException *exception) {
}
}
The Transition and the Keyboard coming up.. happens at same time.. And it is slightly awkward now. Can some one help me out here please?

What I would do is not call [textView becomeFirstResponder] until after the transition has completed.

EDIT: The killer was actually the background color of my view was transparent with alpha 0.8. Once I made it opaque (alpha = 1.0), the animation was smooth!!
I modified my code to make the focus of text box only after completion of the animation.
CGPoint c = thepoint;
CGFloat tx = c.x - floorf(theview.center.x) + 10;
CGFloat ty = c.y - floorf(theview.center.y) + 100;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
// Transforms
CGAffineTransform t = CGAffineTransformMakeTranslation(tx, ty);
t = CGAffineTransformScale(t, 0.1, 0.1);
theview.transform = t;
theview.layer.masksToBounds = YES;
[theview setTransform:CGAffineTransformIdentity];
}
completion:^(BOOL finished) {
if( theview.tag == 1000) {
if(myVC != nil)
[myVC focusCommentText]; /// Here is where I set focus
}
}];
When CATransition is used, it is the delegate animationDidStop I have to choose.
CATransition *transition = [CATransition animation];
transition.duration = 0.4;
transition.type = kCATransitionFromRight; //choose your animation
transition.subtype = kCATransitionFade;
transition.delegate = self; //Setting Delegate as Self
[self.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:myVC.view];
#pragma mark - CATransition Delegate
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
if(myVC != nil) {
[myVC focusCommentText];
}
}

Related

Objective-c animation with constraints instability issue for trailing constraint

I'm developing an IOS app using objective-c. I came across with an instability problem when animating a view's constraints. The animation works well regarding the final position of the frame, but randomly, the trailing(right) constraint is omitted for the first animation block(left constraint never reach to -20 constant value), however leading(left), top and bottom constraints are working as expected.
Please see the video I uploaded. In the first tap, the animation is working as expected, but in the second tap the issue occurs. Please advice.
Video showing the problem
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 1
UIView *containerView = transitionContext.containerView;
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2
[containerView addSubview:toViewController.view];
if([toViewController isKindOfClass:[ImageViewController class]]){
ImageViewController *vcImage = (ImageViewController*)toViewController;
vcImage.constraintLeft.constant = 20;
vcImage.constraintTop.constant = self.selectedCardFrame.origin.y + 60.0;
vcImage.constraintRight.constant = 20.0;
vcImage.constraintBottom.constant = 0;//self.CollectionView.bounds.size.height - (self.selectedCardFrame.origin.y + self.selectedCardFrame.size.height);
[toViewController.view layoutIfNeeded];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[toViewController.view layoutIfNeeded];
[UIView animateWithDuration:duration animations:^{
//First animation block
vcImage.constraintRight.constant = -20.0;
vcImage.constraintLeft.constant = -20.0;
vcImage.constraintTop.constant = -20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
//Second animation block
[UIView animateWithDuration:duration animations:^{
vcImage.constraintLeft.constant = 0;
vcImage.constraintTop.constant = 0.0;
vcImage.constraintRight.constant = 0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}];
}
}
Updated code after #Sh_Khan 's comment.
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 1
UIView *containerView = transitionContext.containerView;
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2
[containerView addSubview:toViewController.view];
if([toViewController isKindOfClass:[ImageViewController class]]){
ImageViewController *vcImage = (ImageViewController*)toViewController;
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:0 animations:^{
vcImage.constraintLeft.constant = 20;
vcImage.constraintTop.constant = self.selectedCardFrame.origin.y + 60.0;
vcImage.constraintRight.constant = 20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
} completion:^(BOOL finished) {
[UIView animateWithDuration:duration animations:^{
//First animation block
vcImage.constraintRight.constant = -20.0;
vcImage.constraintLeft.constant = -20.0;
vcImage.constraintTop.constant = -20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
//Second animation block
[UIView animateWithDuration:duration animations:^{
vcImage.constraintLeft.constant = 0;
vcImage.constraintTop.constant = 0.0;
vcImage.constraintRight.constant = 0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}];
}];
}
}
Before setting your imageView try reorienting the photo. Something tells me the image view is having a hard time determining the orientation of the image. I had this happen during a transition once and would explain why you are seeing the behavior only some of the time. I am a bit rusty with Objective C but give this a shot and use it to before setting your imageView image.
-(UIImage*)normalizeImage:(UIImage*)currentImage {
if (currentImage.imageOrientation == UIImageOrientationUp){
return currentImage;
}
UIGraphicsBeginImageContextWithOptions(currentImage.size, NO, currentImage.scale);
[currentImage drawInRect:CGRectMake(0, 0, currentImage.size.width, currentImage.size.height)];
UIImage *_newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return _newImage;
}

UITextField's border flashs

I have several UITextField in a scrollView,
and I custom textFields' border by setting
layer.borderColor = ...,
layer.borderRadius = 3.0,
layer.borderWidth = 0.1,
layer.masksToBounds = YES.
- (void)keyboardWillShow:(NSNotification *)notification {
CGFloat animDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
CGRect rect = [self.currentTextField superview].frame;
[UIView animateWithDuration:animDuration animations:^{
[self.scrollView scrollRectToVisible:rect animated:NO];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
CGFloat animDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
CGRect rect = [self.currentTextField superview].frame;
[UIView animateWithDuration:animDuration animations:^{
[self.scrollView scrollRectToVisible:rect animated:NO];
}];
}
Bug every time I got any textField focused or dismiss the keyboard, all the textfield's border flashes. But if I just scroll the scrollView no flashes
I finally find the resolution.
Use the CoreAnimation rasteraztion, toggle the textField's layer's shouldRasterize to true: textField.layer.shouldRasterize = true
and set the scale: textField.layer.rasterizationScale = UIScreen.mainScreen().scale
shouldRasterize instructs CoreAnimatin to cache the layer contents as an image. You then get rid off the flashing.
To avoid any unnecesary caching, you should turn off rasterization as soon as the animation is done.
The whole code is like:
- (void)keyboardWillShow:(NSNotification *)notification {
CGRect keyboardRectEnd = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat animDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
CGRect rect = [self.currentTextField superview].frame;
self.scrollViewBottomConstraint.constant = keyboardRectEnd.size.height;
[self toggleTextFieldRasterization:YES];
[UIView animateWithDuration:animDuration animations:^{
[self.scrollView scrollRectToVisible:rect animated:NO];
[self layoutIfNeeded];
} completion: ^(BOOL finished) {
[self toggleTextFieldRasterization:NO];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
CGFloat animDuration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
CGRect rect = [self.currentTextField superview].frame;
self.scrollViewBottomConstraint.constant = 0.0;
[self toggleTextFieldRasterization:YES];
[UIView animateWithDuration:animDuration animations:^{
[self.scrollView scrollRectToVisible:rect animated:NO];
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self toggleTextFieldRasterization:NO];
}];
}
- (void)toggleTextFieldRasterization:(BOOL)toggle {
CGFloat scale = UIScreen.mainScreen.scale;
self.textField1.layer.shouldRasterize = toggle;
self.textField1.layer.rasterizationScale = scale;
self.textField2.layer.shouldRasterize = toggle;
self.textField2.layer.rasterizationScale = scale;
self.textField3.layer.shouldRasterize = toggle;
self.textField3.layer.rasterizationScale = scale;
}

Image Animation. Rotate Image like a coin rotation

I have a situation where I have to rotate the image like a coin. I am doing as follows.
CATransition* transition = [CATransition animation];
transition.startProgress = 0;
transition.endProgress = 1.0;
transition.type = #"flip";
transition.subtype = #"fromRight";
transition.duration = 1.0;
transition.repeatCount = 15;
[rotateimageView.layer addAnimation:transition forKey:#"transition"];
It is rotating the image but it is not completely rotating it Please check the video.
https://www.dropbox.com/s/x4t2aqyscgecqfb/animationVideo.mov?dl=0
It looks like rotating but the right pointing arrow never points left. I want it to point left and right as a complete coin animation.
Check out the following article in which I explained how to rotate a banner. Maybe you can apply the same techniques in your application.
http://highoncoding.com/Articles/876_Creating_a_Flipping_Tweets_View.aspx
-
(void) updateFlippingView:(NSTimer *) timer
{
if(index == ([tweets count] -1))
{
index = 0;
}
UILabel *messageLabel = (UILabel *) [flippingView viewWithTag:MESSAGE_LABEL_TAG];
[UIView animateWithDuration:1.0 animations:^{
[UIView animateWithDuration:2.0 animations:^{
messageLabel.alpha = 0.0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
messageLabel.alpha = 1.0;
messageLabel.text = [tweets objectAtIndex:index];
}];
}];
flippingView.layer.transform = CATransform3DMakeRotation(rotationIndex *M_PI, 1.0, 0.0, 0.0);
for(UIView *subView in flippingView.subviews)
{
subView.layer.transform = CATransform3DMakeRotation(rotationIndex *M_PI, 1.0, 0.0, 0.0);
}
}
completion:^(BOOL finished) {
rotationIndex = rotationIndex == 1 ? 2 :1;
}];
index++;
}

FacebookPOP animation delays

I am having diffuculties about bouncies animation effect , whenever i swipe down, The two UI elements swipe down , 1st is container ( which holds UIPickerView ) and it changes coordinate(x,y) and w/h value, second UI is just for pull on/off and I am only applying it's coordinates. I am doing it with FacebookPOP Framework and i also applied bounce effect. First pulling down , 2 UI has a trailing space while keeping in bounce effect. Without change or refresh view controller, second / third's swipe down action occurs as i expected .
My code while swipe down / up looks like that :
if(sender.direction == UISwipeGestureRecognizerDirectionDown)
{
isDown = YES;
POPSpringAnimation *bounceForControlView = [POPSpringAnimation animation];
bounceForControlView.property = [POPAnimatableProperty propertyWithName:kPOPViewFrame];
bounceForControlView.toValue=[NSValue valueWithCGRect:CGRectMake(0, 241, 320, 42)];
bounceForControlView.springBounciness = 15;
bounceForControlView.springSpeed = 10;
[controlView pop_addAnimation:bounceForControlView forKey:#"bounceForControlView"];
POPSpringAnimation *bounceForPickerView = [POPSpringAnimation animation];
bounceForPickerView.property = [POPAnimatableProperty propertyWithName:kPOPViewFrame];
bounceForPickerView.toValue=[NSValue valueWithCGRect:CGRectMake(0, 64, 320, 177)];
bounceForPickerView.springBounciness = 15;
bounceForPickerView.springSpeed = 10;
[swipeView pop_addAnimation:bounceForPickerView forKey:#"bounceForPickerView"];
[UIView animateWithDuration:0.7 animations:^() {
[pullUPImage setAlpha:1.0];
[self.pullImage setAlpha:0.0];
}];
[UIView animateWithDuration:0.7 animations:^() {
[myPicker setHidden:NO];
}];
}
if(sender.direction == UISwipeGestureRecognizerDirectionUp)
{
isDown = NO;
[myPicker setHidden:YES];
popAnimation = [POPSpringAnimation animation];
popAnimation.property = [POPAnimatableProperty propertyWithName:kPOPViewFrame];
popAnimation.toValue=[NSValue valueWithCGRect:CGRectMake(0, 64, 320, 0)];
popAnimation.springBounciness = 5;
popAnimation.springSpeed = 5;
[swipeView pop_addAnimation:popAnimation forKey:#"slide"];
popAnimation = [POPSpringAnimation animation];
popAnimation.property = [POPAnimatableProperty propertyWithName:kPOPViewFrame];
popAnimation.toValue=[NSValue valueWithCGRect:CGRectMake(0, 64, 320, 42)];
popAnimation.springBounciness = 5;
popAnimation.springSpeed = 5;
[controlView pop_addAnimation:popAnimation forKey:#"slide"];
[UIView animateWithDuration:0.5 animations:^() {
[pullUPImage setAlpha:0.0];
[self.pullImage setAlpha:1.0];
}];
}
This is how animation works in realtime :

How to repeat UIView animation specific times

I'd like to repeat this animation three times.
I confirmed this code worked without repeating.
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}];
Would anyone tell me how to do?
Thanks!
int animationLoopCount = 0;
- (void)doAnimation
{
for (animationLoopCount; animationLoopCount < 3; animationLoopCount++) {
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
[self doAnimation];
if (animationLoopCount == 3) animationLoopCount = 0;
}];
}
}
There are a couple ways you could accomplish this, I think cleanest approach would probably be to use recursion.
Here is a general-purpose method animate things with animation blocks recursively.
- (void) animateWithDuration:(NSTimeInterval) duration
delay:(NSTimeInterval) delay
options:(UIViewAnimationOptions) options
animations:(void(^)( )) animations
completion:(void(^)( BOOL finished )) completion
repeatCount:(NSInteger) repeatCount {
// Only do the animation if we have an animations block, and our count is bigger than 0.
if ( repeatCount <= 0 || !animations )
return;
// Do the animation
[UIView animateWithDuration: duration
delay: delay
options: options
animations:^{
// Invoke the animations block
animations();
}
completion:^(BOOL finished) {
// Invoke the animation completion if it exists
if ( completion )
completion( finished );
// Invoke ourself again with a decremented repeat count.
[self animateWithDuration: duration
delay: delay
options: options
animations: animations
completion: completion
repeatCount: repeatCount - 1];
}];
}
Here is how you could use it in your code using the example you provided.
[self animateWithDuration: 1.0f
delay: 0.1f
options: UIViewAnimationOptionCurveLinear
animations: ^{
imageOne.center = CGPointMake(100, 150);
}
completion: ^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}
repeatCount: 3];
The proper way to do this is like so:
[UIView animateWithDuration:1.0f
delay:0.1f
options:UIViewAnimationOptionCurveLinear
animations:^{
[UIView setAnimationRepeatCount:3.0];// <===
imageOne.center = CGPointMake(100, 150);
} completion:^(BOOL finished) {
imageOne.center = CGPointMake(100,200);
}];
use this simple code
CABasicAnimation* animate = [CABasicAnimation animationWithKeyPath:#"position"];
[animate setDuration:1.0];
[animate setRepeatCount:3];
[animate setAutoreverses:YES];
[animate setFromValue:[NSValue valueWithCGPoint:
CGPointMake(imageOne.center.x, imageOne.center.y)]];
[animate setToValue:[NSValue valueWithCGPoint:
CGPointMake(imageOne.center.x, imageOne.center.y+100)]];
[line.layer addAnimation:animate forKey:#"position"];
I'd rather make something like this:
const CGFloat AnimationDuration = 1.0;
const CGFloat AnimationDelay = 0.1;
const NSUInteger AnimationCount = 3;
- (void) someAction
{
[self showAnimationTimes:AnimationCount];
}
- (void) showAnimationTimes:(NSUInteger)count
{
__block NSUInteger blockCount = count;
[self animation:^
{
imageOne.center = CGPointMake(100, 150);
}
completion:^(BOOL finished)
{
imageOne.center = CGPointMake(100, 200);
if (count > 1)
{
[self showAnimationTimes:--blockCount];
}
}];
}
- (void) animation:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:AnimationDuration
delay:AnimationDelay
options:UIViewAnimationOptionCurveLinear
animations:animations
completion:completion];
}
it's easier to understand for me.

Resources