Apply barrel distortion to UIView - iphone

I've got a UIScrollview which fills a landscape screen, which contains a small tree of UIViews around inside it which I can scroll horizontally. I'd like to apply a barrel distortion to the scrollview, so that as the subviews move from the outside to the centre of the scrollview, they change shape.
What is a good what to go about applying distortions like this?
Is there a way to override drawRect for the scroll view, draw onto a bitmap, distort it, then draw that to the ScrollView's context instead? Or are there built in APIs to do this sort of distortion?
(source: arielnet.com)

You could get close to this effect by properly transforming the layers for each of your UIViews. Using a CATransform3D, you can rotate and translate each of the layers in 3-D, as well as apply a perspective effect. For your case, you can translate each of your UIView layers so that they are like a carousel, with the middle view forward and straight-on to the screen, and the side layers tilted and recessed slightly from the screen. This would not cause a curvature of the layers themselves (they will still be drawn as rectangles), but if you apply a transform on the main view's layer to create a perspective effect you should be able to get very close.
For an example of this kind of 3-D positioning of layers, I recommend this example where a full 3-D maze is constructed out of CALayers.
As far as the scroll view goes, you probably will have to do custom touch handling for this. You might be able to get by with respinding to the UIScrollViewDelegate methods like scrollViewDidScroll:, but I've not tried this myself.

Related

Stretchable UIImage and CGAffineTransform

I've got a UIScrollView whose zoom behavior I want to confine to the horizontal axis. I've accomplished that through using a custom UIView as the viewForZoomingInScrollView: and overriding setTransform:. So far so good – the view only zooms horizontally.
One catch: The container view includes some stretchable UIImage instances in UIImageViews. Obviously, with the transform in effect, the images distort.
What's the best bet for either redrawing the view so that the images aren't distorted, or, zooming the view in such a way as to not require transforms in the first place?
Thanks for any help you can offer.
You sure way of achieving this is to make your UIImageView into a custom UIView, and have its drawRect: code do the right thing: draw both stretched and unstretched elements.

How to auto rotate your own custom views?

I've learned that the best way to get graceful rotation is to set the auto rotation mask on the view that you want resize or move. This works fine if you're using SDK views like UILabel, but if you have your own custom view that uses the drawRect method it doesn't rotate as gracefully. In fact the only thing that happens is that it stretches whatever you drew in drawRect.
I've tried redrawing both before and after the rotation, but it doesn't give me that smooth rotation.
I looked at a UITextField auto rotating (flexible width) in slow motion and it follows the edge perfectly during the rotation. That is what I want my view to do, so how do I do that? My views jump to the right position either before or after the rotation.
The following line will make your UIView stretch the middle pixel only. If this is not your desired behavior I suggest you read the documentation for contentStretch to learn how to manipulate the values of the CGRect.
[self setContentStretch:CGRectMake(0.5, 0.5, 0.0, 0.0)];
I would guess that the UITextField you're looking at has at least three subviews, one displaying the left cap of the field's border, one displaying the right cap, and one displaying the middle, with autoresizing masks of "flexible right margin", "flexible left margin", and "flexible width", respectively. If you set up your custom view something like that, and make sure its autoresizesSubviews property is set to YES, then you should get the same smooth resize that the text field does.

What is actually changing when we zoom out a subView inside a UIScrollView?

I have a subview inside a uiscrollview. Then I zoom it out. So it becomes bigger and allows me to scroll through it.
So what is actually changing here? ContentSize of UIScrollView?
If you are not manually responding to changes in the zoom scale (like I describe in this answer), the view that you return from the -viewForZoomingInScrollView: delegate method is simply having a scaling transform applied to it by the UIScrollView. The frame size of the view is not changing, it is just being graphically transformed (which is why you see blurriness at higher scale factors).
The content size of the scrollview remains logically the same. If you check the frame of the scroll view it remains the same.
I think all that is changing is the scaling of the CGLayers. When you zoom in, it shrinks the clipping region frame smaller but then scales the CGLayer transform upwards. In other words, all the logical elements are still present it is simply choosing to draw and display a different part of it.
In the iPhone Application Programming Guide they have a good explanation about the relationship between frames, clipping regions and various transforms on views.

Flipping UIViews From Top / Bottom

I'm well aware that there are only two available UIView transitions, UIViewAnimationTransitionFlipFromLeft and UIViewAnimationTransitionFlipFromRight. I'm wondering if there is anyway that I can implement / emulate a UIViewAnimationTransitionFlipFromTop or UIViewAnimationTransitionFlipFromBottom.
The only way I can think to do this is by flipping the x axis with the y axis, but I've not seen any information about how to do this. Just setting the coordinates of each axis won't fix the issue as the x-axis till remains the x-axis.
Does anyone have any ideas how this can be accomplished?
You can do this by applying a CATransform3D to your main view's CALayer, with your secondary view's CALayer behind it. In this response, I point to a Mac implementation of this animation by Mike Lee. You should be able to reuse a significant portion of that code to recreate this effect on the iPhone.
Guess you'll have to use a UIView animation block with a 3D rotation transform of 90 degrees, have the Core Animation delegate call you when that's done, swap the view with the new one (3D rotated on the other side) and chain with the last 90 degrees for the new view...
Although its not exactly what you want, there are two built-in animations that you didn't mention: UIViewAnimationTransitionCurlDown and UIViewAnimationTransitionCurlUp.
CurlUp looks like someone is peeling the view from the bottom up to the top, and CurlDown looks like the view is being unrolled down on top of the screen from top to bottom. You should at least give them a look, as they are easy to use.

My Quartz drawing on iPHone is clipped after rotation!

I'm drawing three rectangles, one of which falls off the end of the view, (i.e. the drawing is bigger than the current view bounds) so you don't see the right edge. This is good, but when the view is rotated, the right edge is still clipped, even though there's plenty of room to draw it. How can I get the view to redraw the full rectangle?
I've tried:
1) changing the frame and bounds rectangles to bigger
2) calling setNeedsLayout
3) calling setContentMode:UIViewContentModeRedraw;
4) calling [self.view setClipsToBounds:NO]
Trying a CGLayer is next, unless somebody suggests something else.
-Owen
Make sure your autoresizing mask on this layer is set properly in IB. If its not stretching the right way on rotation then the view will not take the new shape of the screen.