How to access the layer of a view? - iphone

I would like to apply a transformation (rotation) to a UIImageView. I could just set the transform property of the view, but wouldn't setting the layer's transform make it faster? If so, how can I achieve this?
Assuming I have a UIImageView initialized with an image, can anyone help? Wouldn't it be better to have a UIVIew with a UIImageView in it?

If you're doing a simple rotation, the speed difference is probably insignificant, but you can get access to a view's layer by doing:
view.layer
So, you can just use
view.layer.affineTransform = newTranform;

You can do a constant rotation using a CAAnimation that auto repeats (I think; I haven't actually tried to do this exact thing, but similar stuff).
If one animation won't do it, certainly two will (have one rotate 180°, then when it ends, have another one fire to do the second 180°. This can be done on either the view or its layer. You'll definitely want to test this on a device for performance.
No need to use NSTimer; this is what CoreAnimation is built for.

Related

How to get the correct frame of a rotated UIView (including correct position)

I have an app where I have a rectangle that the user can rotate and pan using their fingers. I'd simply like to know what the frame is of this rotated view so I can find out if it intersects another rectangular UIView (can't use the frame property because it gets invalidated when the UIView gets transformed). What's the easiest way to accomplish this?
Every UIView has a property frame which is of type CGRect.
You can access it using view.frame.
After the transform is applied you can use the bounds and center property on the view to get the orientation. It may take a little bit of calculation but i hope you can get to it easily.
Refer to this image from an answer to this question.

UIKit: CGAffineTransforms and Composited Animations

I've noticed that when animating things in UIKit, certain types of animations can be composited using standard block-based animations while others cannot. For instance, view.transform interferes with view.frame, but not with view.center. Are these things documented anywhere?
On a related note, because of these compositing issues, I've often resorted to animating mainly using CGAffineTransforms, since they can be composited very easily. Is this a good idea? It seems that applying a transform is different under the hood than simply changing the frame, so I'm not sure if I should be using them to permanently move a view or change its size. Do CGAffineTransforms and view.frame-related changes overlap at all?
Thanks!
For what it's worth, here's Apple's stance on this:
You typically modify the transform property of a view when you want to
implement animations. For example, you could use this property to
create an animation of your view rotating around its center point. You
would not use this property to make permanent changes to your view,
such as modifying its position or size a view within its superview’s
coordinate space. For that type of change, you should modify the frame
rectangle of your view instead.
Source: View Programming Guide for iOS, View and Window Architecture
(I suppose one exception would be permanently rotated views, which would be impossible to accomplish with frame modifications.)
I've also determined that CGAffineTransforms appear to modify the underlying rendered image of a view, not its content, so (for example) applying a CGAffineTransformScale is fundamentally different from expanding the frame. I'm not sure if this is necessarily true, or if it depends on contentMode/other factors.
I'm still not entirely clear on how the frame, bounds, and transform of a view interact. You can, for example, set the frame of a view after applying a rotation, and it'll be relative to the rotated view, whereas modifying the bounds will apply the transformation to the view pre-rotation (IIRC).

iphone - making the CGAffineTransform permanent

I am banging my head on the wall here due to this problem:
When I create a UIImageView this view has a certain orientation and size. Lets call this state "A".
This view responds to taps. It can be dragged around the screen.
At some point in the code I apply a CGAffineTransform to the view. Does not matter if the affine is a scale, a rotation, a translation or a combination of all. Does not matter also if the transform is absolute or relative. Not to mention the device can change its orientation and the view is autorotated to the correct orientation (that we can cay is a kind of rotation or transformation applied to the view).
The problem is: the moment I touch that object or try to animate its transparency or any other parameter, it "remembers" the state "A" and does all animations from that state, not from current state. If I simply touch the view, it returns instantly to state "A". The code is not doing it by itself. It is pretty annoying. How to I make a view assume its current state of transformations as the reset or initial state? In other words, how do I make a view forget its past transformations or states?
The only way I know is recreating the view, but this is a ridiculous way of doing this.
Is there any way to make this work as I described?
thanks
Afaik, all of the SDK animations automatically create a copy, perform the animation on the copy while hiding the original. In your code you'll have a getState line that starts this and creates the pointer to the animation object. To make it permanent at the end of your animation routine set the original objects view to the animation view.
Iirc it something like this, but I don't have my code samples in front of me:
myOriginalObject.view = myMnimationObject.view
Obviously do this before you release your animations but after you're done with the transforms.

iPhone Flip Transition - Can I get notified at the half way point

I have a single UIView for drawing any one of a set of items. That is, my one UIView subclass can be told to draw a square, circle, or triangle for example.
I'd like to have a transition to flip that view so that the UIView draws a different shape at the half way point, so it looks like I'm transitioning to a different view, but really I'm just redrawing the single view at the half way point of the transition.
Is there a way to do this, without resorting to using two different UIViews and swapping them out in the animation block? It would be a bit clumsy for me to rejig what I have to use the swap mechanism.
Ideally all I need is some callback or notification that the animation is at the half way point, and then I can redraw the view with the new shape then.
Thanks for any help!
Even if you did know the midpoint of your flip animation, I don't believe redrawing the content of the view mid-animation would do anything. I think that the contents of the view's layer are cached at the beginning of the animation and don't change as the animation proceeds. Also, your view would end up inverted, left to right, at the end of the animation if you didn't have another view behind it.
If you wish to do this as a custom animation, you could break this into two halves using something like Mike Lee's Lemur Flip implementation that I describe in this answer. After the first half of the animation, you could redraw the view's content and complete the animation, ending up back where you started.
However, to me it seems more clumsy to not switch views in response to the transition. It certainly will take a lot more code to do.
you can try to do smth like this. For example, your animationDuration property is set to 2.0f. so, the half way point is 1 second, so you can use
[self performSelector:#selector(yourCallbackMethod) withObject:nil afterDelay:1.0f];
The only way I know of that you could do this is to re-create the animation using Core Animation and then monitor the animation in the presentationLayer using a timer. There is no KVO available in Core Animation Layers so you have to monitor it explicitly.
In other words, it's probably not worth it. I suggest you think of a different way to solve your problem.

How do I suppress animation during AutoRotation

I'm making an app where one of the objects needs to appear not to move or rotate during AutoRotation, but everything else needs to rotate and be repositioned. The way I did this is by manually rotating the object, and moving it into the same position, relative to the device, that it had prior to rotation. The problem is that it looks funny, appearing to rotate into the same position it had before.
If I suppress the animation effects during rotation, it would appear that the object never moved, and everything else just snapped into place, which is what I want. I haven't been able to find anything in the documentation that tells me how to do this though. How do I do this?
When it comes to UIViewController rotation there are two ways you can set up so you get a chance to rotate and move your own views.
In the one-step process, in your UIViewController-derived class provide an implementation of willAnimateRotationToInterfaceOrientation. It gets called and you can kick off your object rotation so while the system is rotating everything else your object is getting counter-rotated so it looks like it's staying put. You'll want to calculate how much to counter-rotate and in which direction based on current interface orientation vs. new orientation.
The other way is to get notified to do custom rotation in two steps. For example, in the first half you can shrink the object down, move it, and rotate it part way then in the second half finish the rotation as you scale back up to normal size. It's a pretty clever way to make the rotation animation look smoother to the eye.
For the two-step process, you need to define two methods. willAnimateFirstHalfOfRotationToInterfaceOrientation gets called for the first half of the rotation (i.e. up to 45 degrees for a 90 degree rotation and at 90 degrees for an upside down flip). Once past that point the second half is called via willAnimateSecondHalfOfRotationFromInterfaceOrientation.
If your object has a 1:1 aspect ratio (i.e square or round) and in the middle of the view then the one-step process will probably work fine. But if it's a non-square object and has to move position (for example if it's at position 40, 60 in portrait but moves to 20, 100 in landscape) and maybe even needs a bit of scaling to look better then you may want to try the two-step process and see if it looks smoother.
If your object is inside its own individual UIView then it's pretty easy to schedule the rotations through UIView animations. Just create a transform through CGAffineTransformMakeRotation, then inside a pair of UIView beginAnimations/commitAnimations blocks set the transform property of the view to this value. You can tweak the timing through setAnimationDuration.
EDIT: Based on the comments, I'm adding some code to show how you could attach the view to the top-level window instead of to the view controller. Your object would then reside in this view instead of the one managed by controller (which is getting rotated). You still need to over-ride the UIViewController rotate methods, but instead of rotating an object under control of the view controller you would trigger a counter-rotation in the object on the top-level.
To add a view to the top-level window:
YourAppDelegate* windowDelegate = ((YourAppDelegate*) [UIApplication sharedApplication].delegate);
[windowDelegate.window addSubview:yourView];
Keep a reference to yourView somewhere you can get to, then in the UIViewController's willAnimateRotationToInterfaceOrientation counter-rotate yourView, i.e. calculate how much to rotate the view in reverse to where you're going--if the phone is turning 90-degrees clockwise, you'll want to rotate the view back 90 degrees counter-clockwise, etc. Then use UIView animations on yourView.
If you don't want rotation animations, overload -shouldAutorotateToInterfaceOrientation: in your UIViewController and return NO. You can then observe UIDeviceOrientationDidChangeNotification to receive manual orientation change notifications.
This may work:
Listen for willRotateToInterfaceOrientation
[UIView setAnimationsEnabled:NO];
Listen for UIDeviceOrientationDidChangeNotification
[UIView setAnimationsEnabled:YES];
Untested, but I know that does work for some cases.