Animated rotation of a view

Hello,

In my app I want a compass to give directions. I have an image view for the compass dial, and another image view superposed to it, showing the needle. Both have the same center.

I use mapCompassNeedle’s setFrameCenterRotation_(-angle) to set the right direction, and everything worked fine, but I thought it would be more natural to make the needle changing its direction smoothly, like a real compass.

So I checked the option “Core Animation Layer” in IB for the needle’s image view, used

        mapCompassNeedle's animator()'s setFrameCenterRotation_(-angle)

. and saw my needle jump erratically before reaching its correct position :frowning:

What mistake did I make? (sigh).

Well, it seems to be another of these “you should stop asking and read the docs” problems.

Hopefully it’s just a detail, and has nothing to do with creating an instance of NSAnimatablePropertyContainer.

I tried to animate the rotation (which is correct without animation)

  • by playing with the Core Animation layers in IB;
  • by rotating the bounds instead of the frame;
  • by telling the animator where was the center of the view to rotate.

and I don’t know where to search now.

Anybody out there? :expressionless:

I found some of the same problems when I tried various things suggested by the docs. I found a way to do it with the code below. I just used a long narrow box to act as the needle. In IB, I checked the box for “Wants Core Animation Layer” for the “needle’s” superview (which gives the needle one too). It seems like there should be a simpler way to do this, but at least it works. I put the positioning and anchor point setting of the needle in awakeFromNib so that you wouldn’t see it move around before the rotation animation starts.

property parent : class "NSObject"
	property arrow : missing value -- IBOutlet for a long narrow NSBox
	
	on awakeFromNib()
		arrow's layer()'s setAnchorPoint_({0.5, 0.5})
		set xValue to (arrow's layer()'s superlayer()'s |bounds|()'s |size|()'s |width|) / 2
		set yValue to (arrow's layer()'s superlayer()'s |bounds|()'s |size|()'s height) / 2
		arrow's layer()'s setPosition_({xValue, yValue})
	end awakeFromNib
	
	on applicationDidFinishLaunching_(aNotification)
		set ctx to current application's NSAnimationContext's currentContext()
		ctx's setDuration_(1.5)
		arrow's layer()'s setValue_forKeyPath_(1.57, "transform.rotation") -- Rotation by 90 degrees counterclockwise
	end applicationDidFinishLaunching_

You can also use the arrow’s superview to get the xValue and yValue, instead of using the layer’s bounds, since the layers are the same size as the views.

set xValue to (arrow's superview()'s |bounds|()'s |size|()'s |width|) / 2
		set yValue to (arrow's superview()'s |bounds|()'s |size|()'s height) / 2

Ric

Ric,

Oh, yes, according to the docs there is simply nothing on what we have to do before rotating the view! The anchor point is supposed to be set when setFrameCenterRotation is called, but it is at (0,0).

I don’t know if it works in Objective-C – after some search on the Web, I guess that it doesn’t. If it’s not a bug, it’s at least badly documented!

I suppose there is another possibility: a thread for a loop containing setFrameRotation_(angle++) / display().

Last question: what is the 1.57 value (instead of 90)?

Anyway, thank you. I understand now why there was no answers for this post.

Regards,

Ric,

Sorry for the silly question, it was pi/2. :confused:

Your method works for an image in an enclosing view – in my case it was two superposed images, so my needle aligns in the center of the window. I’ll have to calculate the center of the background image, then probably convert the coordinates, so.

. it’s too much effort for just a nice effect. My needle will simply jump in position, and so much for the smooth effect. I’ll re-implement it the day when Apple will correct this bug (because it is a bug).

Meanwhile, if I have time, I’ll have a look to these “threads”, maybe I can get better control this way.

I keep your method anyway, I’m sure I can use it elsewhere.

Thank you!

I think the easy way to do this would be to enclose your 2 image views in a box (of the custom type) and make it transparent – that way the centering will be at the box’s center rather than the window’s. Also, that way, if you need to move your image views in code, you can just move the box. I tried this with 2 image views, and it worked fine.

Ric

Ric,

It’s working here too. Thank you for the solution – here we’re not fighting the framework, but we’re surely tricking it.

Best regards,

Ric,

Maybe it’s not a good thing to defy the framework… or maybe it’s something wrong with custom NSBox, but I get strange artifacts with this class – drawing of a non-existent border, loss of the box (cannot be clicked in IB [XCode 4]). Note that all these problems do appear when I’m trying to animate, not with setFrameCenterRotation: wich works perfecty.

Has anyone else experienced problems with Core Animation?

Best regards,

I don’t know why you think we’re defying the framework with what we’re doing here – we’re not. Nesting views inside of other views (a box in this case) is a perfectly acceptable thing to do. I haven’t seen any problems yet with core animation, but I’m using XCode 3.

It does? I thought it wouldn’t animate. It didn’t for me.

Ric

Sorry, I didn’t say animator()'s setFrameCenterRotation, just setFrameCenterRotation. Without animation, it works. It just shows the final state of the rotated image, which is correct. But the animated part was erratic.

Didn’t you get artifacts at runtime (independently of XCode version)? I opened my .xib file using IB 3.2.6 and I got the same behaviour. By running the app using XCode 3, same thing: a ghost frame around the custom box, as if the frame was drawn with transparent pixels, erasing the underlying ones.

No, I didn’t get that. Did you click the wants core animation layer (or whatever it says in IB) box? I think I clicked the one for the content view, which gives you a layer for all the subviews.

Ric

Ric,

I’ll reconsider the whole design and keep you informed.

Regards,