CGAffineTransform and scaling to center of the image - iphone

I started studying objective-c using the iPhone and iPad apps for Absolute Beginners by Rory Lewis book but i got stuck on the 5th chapter.
I want to make a button that shrinks an image.
My problem is, that after I wrote all the code, the image shrinks to the point (0, 0) of the UIImageView (the top left), while in the video the image shrinks to its center. I've done some research and found out that CGAffineTransform uses the center of the object to make translations, rotations etc.
So why does it not work in my case?
I have the latest Xcode version and haven't done anything strange.
I hope I've been clear. Sorry for my English.
EDIT
Sorry for the code, but i wrote the question from my phone.
Anyway the incriminated part goes something like this
- (IBAction)shrink:(id)sender {
if(hasShrink == NO){
[shrinkButton setTitle:#"Grow" forState:UIControlStateNormal];
}
else if(hasShrink == YES){
[shrinkButton setTitle:#"Shrink" forState:UIControlStateNormal];
}
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
myIcon.transform = CGAffineTransformMakeScale(.25, .25);
hasShrink = YES;
[UIView commitAnimations];
}
All the animations are correctly written and the app does work, it just shrinks to the top left. Why is the point not set to the center of the UIImageView by default like it should be?
EDIT:
I figured out it was a problem caused by AutoLayout.
Once disabled everything went smooth ;)

If you are transforming a view mangled managed by auto-layout, you may experience some strange side-effects. See this answer for more details.
The solution I ended up employing was to encapsulate the view I wanted to transform inside another view of exactly the same size. The outer view had the appropriate layout constraints applied to it while the inner view was simply centered in its container. Applying a CGAffineTransform scale transform to the inner view now centers properly.

Old question... but just in case others come looking:
The CGAffineTransform acts (rotates, scales) around an anchorPoint.
If you are sizing to 0, 0 then your anchor point is set to 0, 0. If you want it to size to a different point, such as the center of the image, you need to change the anchor point.
The anchor is part of a layer
So if you have a UIImageView called imageview, you would make a call like this to set the anchor to the center of imageview:
[imageview.layer setAnchorPoint:CGPointMake(0.5, 0.5)];

Try something like this:
CGAffineTransform t = CGAffineTransformMakeScale(0.5, 0.5);
CGPoint center = imageView.center; // or any point you want
[UIView animateWithDuration:0.5
animations:^{
imageView.transform = t;
imageView.center = center;
}
completion:^(BOOL finished) {
/* do something next */
}];
Next time show your code. It will be easier to help you.
Check this project: https://github.com/djromero/StackOverflow/archive/Q-13706255.zip
You must study how autolayout and autoresize affect your views. In the project above both are disabled to let you see how it works.

Related

iPad "album flip" animation

I'm trying to recreate the flipping album animation in iPod.app on the iPad (Music.app in iOS 5). Getting the flipping to work is easy, but I'm having trouble with positioning and enlarging the album. Right now I'm using this code:
[UIView transitionWithView:self.containerView duration:5.0 options:UIViewAnimationOptionTransitionFlipFromLeft | UIViewAnimationOptionShowHideTransitionViews animations:^(void) {
self.firstView.hidden = YES;
self.secondView.hidden = NO;
self.containerView.frame = CGRectMake(600.0, 0.0, 168.0, 1004.0);
} completion:nil];
The flipping works, but there is something strange going on in the animation. The container view does indeed move and resize, but the subviews (firstView and secondView) do not.
Because the superview clips to its bounds (even though I set that to NO; another strange thing!), it appears like the subviews are getting "cut" when the container view moves.
I hope you guys understand the problem. Any Core Animation hero who can help me with this? Thanks.
Have you set the autoresize mask on the child views? Those are used to automatically resize or reposition a view when its superview’s bounds change.

Animate size of a UIView around center point

I have a UIView which I want to grow from it's center when a user touches it. The problem is that when animating it the view expands left and then moves to the right, whereas I want it to expand to the left and right, while keeping the center point the same.
This is the code I have at the moment:
[UIView animateWithDuration:1 animations:^(void) {
[view setFrame:CGRectMake(-10, 0, 320, view.frame.size.height)];
}];
I didn't think it was going to be difficult to do this, but it seems it is. Short of animating it manually with a timer I have no idea how to get it to expand from it's center.
I am not quite sure why it's working for you in a weird manner. Did you alter the anchorPoint property in any way? Otherwise it should grow from center.
Does doing
CGRect newFrame;
newFrame = CGRectInset(view.frame, -10, -30);
[UIView animateWithDuration:1 animations:^(void) {
view.frame = newFrame;
}];
also give you the same result? What about this?
[UIView animateWithDuration:1 animations:^(void) {
view.transform = CGAffineTransformScale(view.transform, 1.1, 1.1);
}];
Try the code given by #Deepak Danduprolu and also don't forgot to UnCheck the "Use AutoLayout" checkbox in the show file inspector window of the storyboard.
It works for me.
Just offset the X,Y position of the view. Lets say you're DECREASING both the width and height of the frame by 20 points: you should then ADD 10 points to both the X and Y position of the frame.

Alternative to CGAffineTransformConcat

In the app I'm working on the user taps on a tableview to zoom it up to full view from a "thumbnail" or a miniature view. Everything is working great except for a somewhat annoying animation glitch or whatever. The thing is I'm using the code below:
if ([subview respondsToSelector:#selector (name)] && [subview.name isEqualToString:self.labelListName.text])
{
[self.tabBarController.view addSubview:subview];
CGRect frame = CGRectMake(35, 78, self.scrollView.frame.size.width, self.scrollView.frame.size.height);
subview.frame = frame;
CGAffineTransform scale = CGAffineTransformMakeScale(1.39, 1.39);
CGAffineTransform move = CGAffineTransformMakeTranslation(0,44);
CGAffineTransform transform = CGAffineTransformConcat(scale, move);
[UIView animateWithDuration:0.15
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
subview.transform = transform;
}
completion:^(BOOL finished){ [self goToList],subview.hidden = YES; }];
}
- (void)goToList
{
self.gotoWishList = [[WishList alloc] initWithNibName:#"WishList" bundle:nil];
self.gotoWishList.hidesBottomBarWhenPushed=YES;
self.gotoWishList.name = self.labelListName.text;
[self.navigationController pushViewController:self.gotoWishList animated:NO];
self.gotoWishList.scrollLists = self;
[WishList release];
}
And when doing the animation the transfer between the zoom view and the actual view the user is going to interact with is not completely perfect. The text inside the cell jumps a little when switching between the views. The problem lies in the translation matrix. If I skip that I can get the animation to work perfectly but then of course I need to move the miniature view down in the GUI which is not an option. If I instead do the animations in another order (move, scale) then it works better. I still get a jump at the end but it looks better, as everything jumps...and not just the text.
So...basically my question is how can I make this animation fluent. I read that the CGAffineTransformConcat still does each animation separately, and I really need both animations (scaling and moving the list) to be ONE fluent animation.
Thanks for any tips!
I think you will have to nest views/graphic-context to get what you want. The animation system doesn't support simultaneous animations because the mathematics of doing so requires an exponential amount of computational power. You might be able to trick it by sliding one view while enlarging the other.
I'm not sure about that as I have never had need to try it.
You might also be getting a jerk or skip from the tableview itself. The bounce at the top and end of scrolls can produce effects if you radically resize the table on the fly. I would turn all that off and see if you still have the problem. You might also want to test on the view independent of the tableview to make sure the problem is with the animations and not the tableview moving itself.

Opening a curtain: Animation with Core Animation

I would like to animate a curtain, which gets opened. I have two images: one for the left and one for the right side of the curtain (depicted in red). I would like to smoothly slide them away with Core Animation. For what animation type should I look for? How do I achieve a realistic sliding style?
Regards,
Stefan
alt text http://img.skitch.com/20100627-8ytxrbe64ccbruj49c2pbs7kt2.png
I'm not sure why people are suggesting using a translation. If all you need to do is slide the images, simply call -setCenter on each image view inside an animation block. Like this:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[leftCurtainImageView setCenter:pointOffScreenLeft];
[rightCurtainImageView setCenter:pointOffScreenRight];
[UIView commitAnimations];
Where pointOffScreenLeft, and pointOffScreenRight are calculated something like:
CGPoint pointOffScreenLeft = CGPointMake(
-[leftCurtainImageView bounds].size.width,
[leftCurtainImageView frame].origin.y);
CGPoint pointOffScreenRight = CGPointMake(
[rightCurtainImageView frame].origin.x +
[rightCurtainImageView bounds].size.width,
[leftCurtainImageView frame].origin.y);
These calculations assume that the curtains are positioned at the far left and far right edges respectively of their containing view.
The easiest solution would be to have to imageview or CGLayers and then use CGAffineTransformTranslate in an animation block to slide them off screen.
Man
After a long search. The only way I could find is this.
https://github.com/Ciechan/BCMeshTransformView

iPhone Curl Left and Curl Right transitions

I am looking for a way to do a UIViewAnimationTransitionCurlUp or UIViewAnimationTransitionCurlDown transition on the iPhone but instead of top to bottom, do it from the left to right (or top/bottom in landscape mode). I've seen this asked aroud the internet a few times but none sems to get an answer. However I feel this is doable.
I have tried changing the View's transform and the view.layer's transform but that didn't affect the transition. Since the transition changes when the device changes orientation I presume there is a way to fool the device to use the landscape transition in portrait mode and vice versa?
It's possible to do curls in any of the four directions by using a container view. Set the container view's transformation to the angle you want and then do the curl by adding your view to the container view, not your app's main view which does not have a transformed frame:
NSView* parent = viewController.view; // the main view
NSView* containerView = [[[UIView alloc] initWithFrame:parent.bounds] autorelease];
containerView.transform = CGAffineTransformMakeRotation(<your angle here, should probably be M_PI_2 * some integer>);
[parent addSubview:containerView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:containerView cache:YES];
[containerView addSubview:view];
[UIView commitAnimations];
I actually managed to achieve this effect by changing the orientation of my UIViewController. The strange thing is, I had my controller nesten in another one when it wasn't working, but when I set him as the immediate view controller, it worked.
Code that does it:
In a UIViewController that is the main view controller in my app delegate and only allows landscape orientation (as you see in the 2nd method below) I have the following:
-(void)goToPage:(int)page flipUp:(BOOL)flipUp {
//do stuff...
// start the animated transition
[UIView beginAnimations:#"page transition" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:flipUp ? UIViewAnimationTransitionCurlUp : UIViewAnimationTransitionCurlDown forView:self.view cache:YES];
//insert your new subview
//[self.view insertSubview:currentPage.view atIndex:self.view.subviews.count];
// commit the transition animation
[UIView commitAnimations];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
I also struggled with this. To get the curl to come from the right or left you can create an intermediate view and transform it. So, let's say the view you're transitioning (myView) is a child of the main window (parentView):
-parentView
-->myView
You will insert an intermediate view in between (easily done in Interface Builder):
-parentView
-->containerView
--->myView
Then, use the following code to flip the container 90 deg left and the transitioned view 90 deg right:
containerView.transform = CGAffineTransformMakeRotation(-M_PI_2);
myView.transform = CGAffineTransformMakeRotation(M_PI_2);
myView will still appear upright to the user but the transition will think it's applied at 90 degrees from the left.
Note that depending on how auto-scaling your views are, you might have to fix the frame sizes after applying the transform, eg
containerView.frame = CGRectMake(0.0, 0.0, 768.0, 1024.0);
myWebView.frame = CGRectMake(0.0, 0.0, 768.0, 1024.0);
Hope this helps. The is the closest you can get to UIViewAnimationTransitionCurlLeft and UIViewAnimationTransitionCurlRight.
I tried the solution of fluXa on iOS5 (So I had to use [UIView trans......]) but it didn't work: the curl still went up or downwards. Apparently the transition now don't take the transform of the view into account. So in case someone else wants to do the same trick on iOS5, the solution is to add another container in between and animate the transition from there.
Here is my code, which is a bit specific since I want to curl 'up' to the left, but with the lower corner curling. As if I am tearing a page out of a note book.
UIView* parent = self.view; // the main view
CGRect r = flipRectSize(parent.bounds);
UIView* containerView = [[UIView alloc] initWithFrame:r];
CGAffineTransform t = CGAffineTransformMakeRotation(-M_PI_2);
t = CGAffineTransformTranslate(t, -80, -80);
containerView.transform = CGAffineTransformScale(t, -1, 1);
[parent addSubview:containerView];
UIView* container2 = [[UIView alloc] initWithFrame:r];
[containerView addSubview:container2];
UIImageView* v = [[UIImageView alloc] initWithFrame:r];
v.image = [UIImage imageWithCGImage:contents.CGImage scale:contents.scale orientation:UIImageOrientationLeftMirrored];
[container2 addSubview:v];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[UIView transitionWithView:container2
duration:DURATION_CURL_ANIMATION
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[v removeFromSuperview];
} completion:^(BOOL finished) {
if (completion) {
completion(finished);
}
[containerView removeFromSuperview];}];});
Notes:
I must admit that the affine transform translate (80,80) doesn't make sense in my mind, but it is necessary for iphone, probably won't work on iPad.
flipSizeRect flips the width and height of a rectangle (you already got that, right?)
the dispatch_after is necessary because I added the container and then want to remove a view from the hierarchy. If I leave out the dispatch nothing animates. My best guess is that we first need to let the system do a layout pass before we can animate a removal.
I don't think there is a way beyond writing a custom animation.
More importantly you probably shouldn't try to it. The curl up and curl down are part of the user interface grammar that tells the user that a view is being lifted up or put down over the existing view. It's supposed to be like a sticky note being put down and then removed. A left<->right curl will most likely be interpreted as the something like ripping a page out of a book. It will confuse users.
Whenever you find yourself trying to do something in the interface that the standard API doesn't do easily, you should ask yourself whether such a novel method will communicate something important to user and whether it is similar to the existing interface grammar. If not, then you shouldn't bother.
Unusual interfaces have an initial wow factor but they lead to frustration and errors in day-to-day use. They can also cause Apple to refuse your app.