I am trying to implement a 3D Carousel on the iPad, consisting of UIViews, an effect like what is shown over here.
I have gone through many similar questions on SO, but didn’t find any staisfactory answers or no answers at all.
I am trying to achieve the effect through modifying the coverflow animation but it just doesn’t give that slick effect.
Has anyone implemented this?(open for suggestions through quartz and openGL both)
No need to dive into Quartz or OpenGL, assuming you don’t mind foregoing the blur. The page you link to gets the perspective wrong (that’s why the images in the background appear to move more quickly than those in the foreground), so the maths can be a little bit of smoke and mirrors.
There’s full example code at the bottom. What I’ve done is used sine and cosine to move some views about. The basic theory behind it is that the point at angle a on the outside of a circle of radius r positioned on the origin is at (a*sin(r), a*cos(r)). That’s a simple polar to Cartesian conversion, and should be clear from the trigonometry most countries teach to their teens; consider a right angled triangle with a hypotenuse of length a — what lengths are the other two sides?
What you can then do is reduce the radius of the y part to convert the circle into an ellipse. And an ellipse looks a bit like a circle that you’re looking at from an angle. That ignores the possibility of perspective, but go with it.
I then fake perspective by making size proportional to the y coordinate. And I’m adjusting alpha in a way like the site you link to does blur, in the hope that’s good enough for your application.
I effect position and scale by adjusting the affine transform of the UIViews I want to manipulate. I set the alpha directly on the UIView. I also adjust the zPosition on the view’s layers (which is why QuartzCore is imported). The zPosition is like the CSS z position; it doesn’t affect scale, only drawing order. So by setting it equal to the scale I’ve computed, it just says “draw larger things on top of smaller things”, giving us the correct draw order.
Finger tracking is done by following one UITouch at a time through the touchesBegan/touchesMoved/touchesEnded cycle. If no finger is being tracked and some touches begin, one of them becomes the finger being tracked. If it moves then the carousel is rotated.
To create inertia, I have a little method that attaches to a timer keeps track of the current angle versus the angle one tick before. That difference is used like a velocity and simultaneously scaled downward to produce inertia.
The timer is started on finger up, since that’s when the carousel should start spinning of its own volition. It is stopped either if the carousel comes to a standstill or a new finger is placed down.
Leaving you to fill in the blanks, my code is:
So relevant member variables are a mutable array, ‘carouselViews’, a timer, ‘animationTimer’, two floats, ‘currentAngle’ and ‘lastAngle’, and a UITouch, ‘trackingTouch’. Obviously you’d probably want to use views that aren’t just coloured squares and you might want to adjust the numbers I’ve pulled out of thin air for positioning. Otherwise, it should just work.
EDIT: I should say, I wrote and tested this code using the iPhone ‘View-based application’ template in Xcode. Create that template, dump my stuff into the created view controller and add the necessary member variables to test. However, I’ve realised that the touch tracking assumes 180 degrees is the full width of your view, but the setCarouselAngle: method forces the carousel always to be 280 points across (that’s the 100 multiplier on xPosition times two, plus the width of a view). So the finger tracking will appear to be far too slow if you run it on an iPad. The solution is obviously not to assume the view width is 180 degrees but that’s left as an exercise!