I have an animation to add a subview to make it appear as if it comes from inside where the user touches and then fills the entire screen.
Sameway, when an iOS app opens up from the place the user touches it..
- (void) showView : (UIView *) theview : (CGPoint) thepoint {
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) {
}];
}
The above code did, what we needed till iOS8.. Built under XCode5.1 (iOS7 SDK)
But the behaviour was entirely different starting with the iOS8 SDK, XCode6
The ZOOM now is behaving strangely. I was finally able to find that CGAffineTransformIdentity is behaving wrongly(or I use it wrongly?) in iOS8..
I see many have this issue, but they mentioned about AutoLayout. All my views are created programatically. We don't use a nib file.(IB)
How can I make this work with XCode 6?
After couple of hours effort, I came up with a solution. I just post it for future users.
- (void) showView : (UIView *) theview : (CGPoint) thepoint {
CGPoint c = thepoint;
CGFloat tx = c.x - (floorf(theview.center.x)) ;
CGFloat ty = c.y - (floorf(theview.center.y));
/* The transformation now is before the animation block */
CGAffineTransform t = CGAffineTransformMakeTranslation(tx, ty);
t = CGAffineTransformScale(t, 0.1, 0.1);
theview.transform = t;
theview.layer.masksToBounds = YES;
/* Animate only the CGAffineTransformIdentity */
[UIView animateWithDuration:0.8
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
theview.layer.masksToBounds = YES;
[theview setTransform:CGAffineTransformIdentity];
}
completion:^(BOOL finished) {
}];
}
But I have no clue, why it worked with iOS7 SDK previously and not with the new iOS8 SDK.!
Related
I need two animations on a UIView:
Make the view move down and slightly grow.
Make the view grow even bigger about its new center.
When I attempt to do that, the second animation starts in a weird location but ends up in the right location and size. How would I make the second animation start at the same position that the first animation ended in?
#import "ViewController.h"
static const CGFloat kStartX = 100.0;
static const CGFloat kStartY = 20.0;
static const CGFloat kStartSize = 30.0;
static const CGFloat kEndCenterY = 200.0;
#interface ViewController ()
#property (nonatomic, strong) UIView *box;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.box = [[UIView alloc] initWithFrame:CGRectMake(kStartX, kStartY, kStartSize, kStartSize)];
self.box.backgroundColor = [UIColor brownColor];
[self.view addSubview:self.box];
[UIView animateWithDuration:2.0
delay:1.0
usingSpringWithDamping:1.0
initialSpringVelocity:0.0
options:0
animations:^{
self.box.transform = [self _transformForSize:50.0 centerY:kEndCenterY];
}
completion:^(BOOL finished) {
[UIView animateWithDuration:2.0
delay:1.0
usingSpringWithDamping:1.0
initialSpringVelocity:0.0
options:0
animations:^{
self.box.transform = [self _transformForSize:100.0 centerY:kEndCenterY];
}
completion:^(BOOL finished) {
}];
}];
}
- (CGAffineTransform)_transformForSize:(CGFloat)newSize centerY:(CGFloat)newCenterY
{
CGFloat newScale = newSize / kStartSize;
CGFloat startCenterY = kStartY + kStartSize / 2.0;
CGFloat deltaY = newCenterY - startCenterY;
CGAffineTransform translation = CGAffineTransformMakeTranslation(0.0, deltaY);
CGAffineTransform scaling = CGAffineTransformMakeScale(newScale, newScale);
return CGAffineTransformConcat(scaling, translation);
}
#end
There's one caveat: I'm forced to use setTransform rather than setFrame. I'm not using a brown box in my real code. My real code is using a complex UIView subclass that doesn't scale smoothly when I use setFrame.
This looks like it might be a UIKit bug with how UIViews resolve their layout when you apply a transform on top of an existing one. I was able to at least get the starting coordinates for the second animation correct by doing the following, at the very beginning of the second completion block:
[UIView animateWithDuration:2.0
delay:1.0
usingSpringWithDamping:1.0
initialSpringVelocity:0.0
options:0
animations:^{
self.box.transform = [self _transformForSize:50.0 centerY:kEndCenterY];
}
completion:^(BOOL finished) {
// <new code here>
CGRect newFrame = self.box.frame;
self.box.transform = CGAffineTransformIdentity;
self.box.frame = newFrame;
// </new code>
[UIView animateWithDuration:2.0
delay:1.0
usingSpringWithDamping:1.0
initialSpringVelocity:0.0
options:0
animations:^{
self.box.transform = [self _transformForSize:100.0 centerY:kEndCenterY];
}
completion:^(BOOL finished) {
}];
}];
Using the same call to -_transformForSize:centerY: results in the same Y translation being performed in the second animation, though, so the box ends up further down in the view than you want when all is said and done.
To fix this, you need to calculate deltaY based on the box's starting Y coordinate at the end of the first animation rather than the original, constant Y coordinate:
- (CGAffineTransform)_transformForSize:(CGFloat)newSize centerY:(CGFloat)newCenterY
{
CGFloat newScale = newSize / kStartSize;
// Replace this line:
CGFloat startCenterY = kStartY + kStartSize / 2.0;
// With this one:
CGFloat startCenterY = self.box.frame.origin.y + self.box.frame.size.height / 2.0;
// </replace>
CGFloat deltaY = newCenterY - startCenterY;
CGAffineTransform translation = CGAffineTransformMakeTranslation(0.0, deltaY);
CGAffineTransform scaling = CGAffineTransformMakeScale(newScale, newScale);
return CGAffineTransformConcat(scaling, translation);
}
and it should do the trick.
UPDATE
I should say, I consider this "trick" more of a work-around than an actual solution, but the box's frame and transform look correct at each stage of the animation pipeline, so if there's a true "solution" it's eluding me at the moment. This trick at least solves the translation problem for you, so you can experiment with your more complex view hierarchy and see how far you get.
I'm having a little problem about animations on iOS with Objective-c.
I'm trying to create a card game, and as the game start, the deck distributes the cards on the table, with an animation. The problem is that at the beginning the cards are all in row in the bottom side of the screen, then with the animation they distribute all over the table. But when I tap on anyone of them, they return in their initial position, although the game keeps on working because they flip and unflip over, as well as the score works properly. I'm attaching some snippets of the code and of the view.
Here are links to screenshots:
1)cards as the game starts: prntscr.com/68r8yj
2)cards at the end of initial animation: prntscr.com/68r92u
3)cards as I tap on any of them: prntscr.com/68r2hr
Here's my code snippet
-(void)viewDidAppear:(BOOL)animated {
CGPoint startPoint = {21, 34};
for(int i = 0; i < [self.cardsView count]; i++) {
FrenchPlayingCardView *view = [self.cardsView objectAtIndex:i];
int width = view.bounds.size.width;
int height = view.bounds.size.height;
CGRect newFrame = CGRectMake(startPoint.x, startPoint.y, width, height);
/*[UIView animateWithDuration:1.0
delay:1.0
options:UIViewAnimationOptionTransitionNone
animations:^(){
view.frame = newFrame;;
}
completion:nil];*/
[UIView beginAnimations:#"move" context:nil];
[UIView setAnimationDuration:3.0];
view.frame = newFrame;
[UIView commitAnimations];
NSLog(#"index:%d %f %f", i, view.frame.origin.x, view.frame.origin.y);
startPoint.x += 76;
if(i % 4 == 3) {
startPoint.y += 100;
startPoint.x = 21;
}
}
[super viewDidAppear:animated];
}
call [super viewDidAppear] before your animation and close autolayout. I think autolayout can cause this
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);
}
I have an UIImageView with an rotation animation that loops, I need to move the UIImageView by x and y in the screen.
The rotation animation code is:
-(void)rotate{
if (leftRotate){
leftRotate = NO;
}else{
leftRotate = YES;
}
CGRect initFrame = self.frame;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse
animations:^{
if (leftRotate){
self.transform = CGAffineTransformRotate(self.transform, -1 * (M_PI/8));
}else{
self.transform = CGAffineTransformRotate(self.transform, M_PI / 8);
}
}
completion:^(BOOL completed) {
if (completed){
if (!stopRotate){
[self rotate];
}
}
}];
}
I try differents approaches to move the image, like setting the center but I cant move it.
I found the way, I have to set the center out side the animation:
CGPoint facePosition = CGPointMake(self.face1.center.x+1,
self.face1.center.y);
self.face1.center = facePosition;
I use the following block of code to slide a UIView down and when finished rotate another UIView.
The second part of the animation, the completion block is only performed once which means the 1st animation is not completed else it would reach the completion block.
On the iphone simulator it looks as if the 1st animation did finish...
can anyone help me figure this out?
my NSLog says:
finished 1st
started 2nd
finished 1st
finished 1st
finished 1st
.
.
.
- (IBAction) move
{
[UIView animateWithDuration:0.7 animations:^{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:NO];
CGPoint pos = movingtTable.center;
float moveDistance = 220.0;
if(!isViewVisible){
//expose the view
pos.y = pos.y+moveDistance;
//disable selection for xy table
xTable.userInteractionEnabled = NO;
yTable.userInteractionEnabled = NO;
//angle = M_PI;
}
else
{
pos.y = pos.y-moveDistance;
xTable.userInteractionEnabled = YES;
yTable.userInteractionEnabled = YES;
//angle = -M_PI;
}
isViewVisible = !isViewVisible;
movingtTable.center = pos;
NSLog(#"finished 1st");
}completion:^(BOOL finished){
NSLog(#"started 2nd");
[UIView animateWithDuration:0.4 animations:^{
//[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.4];
//[UIView setAnimationRepeatCount:1];
//[UIView setAnimationRepeatAutoreverses:NO];
arrows.transform = CGAffineTransformMakeRotation(angle);
}completion:^(BOOL finished){
angle = -angle;
}];
}];
Why are you trying to initialize another UIView animation inside the animateWithDuration block code? Update your code to the following and make sure you're not performing multiple animations of a single view at a time.
- (IBAction) move
{
[UIView animateWithDuration:0.7 animations:^{
CGPoint pos = movingtTable.center;
float moveDistance = 220.0;
if(!isViewVisible){
//expose the view
pos.y = pos.y+moveDistance;
//disable selection for xy table
xTable.userInteractionEnabled = NO;
yTable.userInteractionEnabled = NO;
//angle = M_PI;
}
else
{
pos.y = pos.y-moveDistance;
xTable.userInteractionEnabled = YES;
yTable.userInteractionEnabled = YES;
//angle = -M_PI;
}
isViewVisible = !isViewVisible;
movingtTable.center = pos;
NSLog(#"finished 1st");
}
completion:^(BOOL finished){
NSLog(#"started 2nd");
[UIView animateWithDuration:0.4 animations:^{
arrows.transform = CGAffineTransformMakeRotation(angle);
}completion:^(BOOL finished){
angle = -angle;
}];
}];
BTW: The block code requires some serious refactoring, if you ask me :)
You are mixing and matching paradigms and I believe that is causing the issue you are seeing. You are creating an animation block, but inside of that block you are creating a new animation routine with the 'old' paradigm for running UIView animations. Apple is leading people away from the old paradigm and I would encourage you to ONLY use blocks as well.
This is why the completion block only runs once, the UIView animateWith block code only runs once. However, your internal animation code runs multiple times.
Take out:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.7];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:NO];
If you want your animation block to run several times, then use the full method:
animateWithDuration:delay:options:animations:completion:
Make the delay = 0, and set your options to UIViewAnimationOptionRepeat, or whatever you need to accomplish the number of cycles you want the block to complete.
Here is my suggestion assuming you want it to repeat:
- (IBAction) move
{
[UIView animateWithDuration:0.7
delay:0
options:UIViewAnimationOptionRepeat
animations:^{
CGPoint pos = movingtTable.center;
float moveDistance = 220.0;
if(!isViewVisible) {
//expose the view
pos.y = pos.y+moveDistance;
//disable selection for xy table
xTable.userInteractionEnabled = NO;
yTable.userInteractionEnabled = NO;
//angle = M_PI;
}
else {
pos.y = pos.y-moveDistance;
xTable.userInteractionEnabled = YES;
yTable.userInteractionEnabled = YES;
//angle = -M_PI;
}
isViewVisible = !isViewVisible;
movingtTable.center = pos;
NSLog(#"finished 1st");
}
completion:^(BOOL finished){
NSLog(#"started 2nd");
[UIView animateWithDuration:0.4
animations:^{
arrows.transform = CGAffineTransformMakeRotation(angle);
}
completion:^(BOOL finished){
angle = -angle;
}];
}];
}