Rotating a UIView; How to prevent clipping during rotation? - ios

So let's say I have a UIView that in its standard configuration spans the full width and height of its container. For the purposes of this question, let's say that its dimensions are 320x400. Suppose that this view contains content that may be (and typically is) larger than its standard dimensions (so it scrolls through content).
Now if this UIView has a UIRotationGestureRecognizer associated with it that is used to rotate the view using its transform property, how do I ensure that its frame size/drawable area is always sufficiently large for its current orientation (by "sufficiently large" I mean that the rendered content should always extend out to the original bounds, and not be clipped prior to reaching the edge of the original bounds)? For instance, if I rotate it 90 degrees the view needs to understand that its "width" may now consume up to 400 pixels, while its height is constrained to 320 pixels.
Note that I don't want to scale the view as part of the rotation operation. Any "additional" space that becomes available due to the current rotation should be used to display additional content, if available, and not to simply display the same content at a higher zoom level.

You don't want any empty spaces to appear when rotating. The solution is to have a subview containing a background which is sufficiently large enough to cover the entire viewport during rotation. I'd say a 400 by 400 view should do. This view can either be the parent of your contentview, but that'll give you a bit of a positioning hassle.
Easier would be to add a subview on your currentview. Put the (large) background on this subview, make sure this view is at the bottom of the view stack and on your currentview put clipsToBounds=NO.
Beware though that disabling clipping can have a performance impact especially if your view hierarchy becomes complex.

Related

Is it possible to have a variable height UIView mask layer NOT stretch, and perhaps just sit at the top? (For example, a "torn paper" style edge?)

I can't for the life of me figure out how to create such an effect on a UIView that has dynamic height. I want to have the top have a "torn paper" style edge, but the view can be of dynamic height, so when I set the torn paper mask at the top, it gets stretched the full length of the view.
Can I say, "stick to the top", or perhaps designate a portion that is supposed to stretch? Preferably with layers but iOS 8's maskView property works too.
You can set the frame of the layer's mask to anything you want. So that's one way to position it.
Or you could (probably, I haven't tried yet) use a stretchable image view as the mask (- resizableImageWithCapInsets:resizingMode:). That should keep the torn paper edge from stretching.

What is the relationship between a UIView's frame origin and its center?

Say I'm creating an animation, and I want to move the center of a UIView to a certain CGPoint I have. I know what I want the center to be, just the aforementioned CGPoint, and I know the width and height that I want, but when I go to create the frame for my UIView I have no idea what to set for the x and y for the origin.
Should I just be setting 0, 0 or anything really? Does it matter?
Should I just be thinking as setting center as a different method of setting the origin? Makes me wish CGRect had an instantiator with center as an option.
There has been a previous question addressing a somewhat similar question, but it addresses them as three separate entities, rather than how to deal with center when creating the view.
Yes, go ahead and set the initial frame's origin to anything, since subsequently setting the center will effectively move this origin anyway. It is not uncommon to initialise views with a frame of CGRectZero when it is going to be fully configured later.
frame is actually just a calculated property based on the view's center and bounds, and so all three are intrinsically linked when it comes to view layout. The UIView Class Reference has this to say about it:
The geometry of a view is defined by its frame, bounds, and center
properties. The frame defines the origin and dimensions of the view in
the coordinate system of its superview and is commonly used during
layout to adjust the size or position of the view. The center property
can be used to adjust the position of the view without changing its
size. The bounds defines the internal dimensions of the view as it
sees them and is used almost exclusively in custom drawing code. The
size portion of the frame and bounds rectangles are coupled together
so that changing the size of either rectangle updates the size of
both.

Correcting blurry text after a CGAffineTransformMakeScale

I have multiple views with many UILabels on the views. (all constructed in Interface Builder).
I am then trying to create a "smaller" replica of my view when you pinch the screen.
To do this I apply:
view.transform = CGAffineTransformMakeScale(.5, .5);
and then I also adjust the frame of view.
The problem is that after the transformation, the text in all of my UILabels becomes "blurry". It doesn't stay pixel perfect as it is in full-scale view.
Is there a way to increase the pixelation of the labels after the transformation?
Applying a transform to a UIView or CALayer merely scales the rasterized bitmap of that layer or view. This can lead to blurriness of the resulting UI element, because they aren't re-rendered at that new scale.
If you really want your text or images to be crisp at the new scale factor, you're going to need to manually resize them and cause them to redraw instead of applying a transform. I described one way that I did this with a UIView hosted in a UIScrollView in this answer.
You might be able to create a single method that traverses your view hierarchy for your one main view, recursively reads each subview's frame, scales that down, and then forces a redraw of its contents. Transforms are still great to use for interactive manipulation or animation, but you can then trigger a full manual scaling and redraw at the end of the manipulation or animation.

UIInterfaceOrientation, CGAffineTransform, Frame, Bounds and Center

Can somebody point me to a good primer on the above, and what happens to one when you mess with the others? It seems as though no matter what I do, once I start messing with either the status bar orientation or the view transform (even if all I'm doing is 90-degree rotations), I can count on my views ending up sideways, upside down and backwards, and on a frustrating afternoon of trial and error trying to get them straightened out. I'm sure it all makes sense once you know the logic and what order everything's applied in, but so far, empirically, I haven't been able to figure it out.
I don't know of a good single document primer on the subject, but the following is what I've learned from experience and reading the docs.
center, bounds, and frame
If you set frame then center and bounds will be updated. If you set center or bounds then frame will be updated. Frame is a convenience method for manipulating center and bounds using the superview's coordinate system.
From UIView Class Reference:
The geometry of a view is defined by its frame, bounds, and center properties. The frame defines the origin and dimensions of the view in the coordinate system of its superview and is commonly used during layout to adjust the size or position of the view. The center property can be used to adjust the position of the view without changing its size. The bounds defines the internal dimensions of the view as it sees them and is used almost exclusively in custom drawing code. The size portion of the frame and bounds rectangles are coupled together so that changing the size of either rectangle updates the size of both.
See The Relationship of the Frame, Bounds, and Center Properties for more details.
transform
If you set the transform property to something besides the identity transform, frame is undefined. If you set the transform to something else, you should only manipulate the view geometry using center (to position the view in it's superview) and bounds (to adjust the size of the view). Here's the relevant info from UIView Class Reference:
The origin of the transform is the value of the center property, or the layer’s anchorPoint property if it was changed. (Use the layer property to get the underlying Core Animation layer object.) The default value is CGAffineTransformIdentity.
...
Warning If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
See Coordinate System Transforms for more details.
UIInterfaceOrientation
UIInterfaceOrientation doesn't affect the transform, bounds, center, or frame properties directly. However, when the device orientation changes, the view controller will automatically resize its subview (which will in-turn resize it's subviews and so on).
See Responding to Device Orientation Changes and View Controller View Resizing for more details.

Why is there an frame rectangle and an bounds rectangle in an UIView?

Well although it's late in the dark night, I don't get it why there are two different rectangles: frame and bounds.
Like I understand it, one single rectangle would have been just enough to do everything. Positioning the View itself relative to another coordinate system, and then clipping it's content to a specified size. What else would you do with two rectangles? And how do they interact with each other?
Does anyone have a good explanation? The one from the Apple docs with the kid holding the fruit is not very good for understanding.
Here's the cheatsheet:
frame is where the view is (with respect to the superview)
bounds is where the view is allowed to draw (with respect to itself)
Some more clarification:
If you are positioning the view in its superview, you almost always change the frame origin.
If you are clipping where the UIView is drawing, you almost always modify its bounds.
Note that you are allowed to have bounds that is bigger than the frame. That is, you can draw "outside the lines" of where you are.
Frame is in the superview's coordinate system, bounds is in the view's coordinate system. From my perspective, it is a convenience to have both. Frame seems to be the more useful of the two, unless there is some case I am unaware of where a subview can have a completely different coordinate system (e.g. pixels scaled differently) than the superview.
I've been having troubles with bounds lately and have done some experimentation. The bounds property does limit where a UIView can draw, but does not limit its subviews. The other thing bounds controls is touch event dispatching. A view will not, as far a I can tell, receive touch events that are outside its bounds. Furthermore, any subview that outside of the parent view's bounds will also not receive touch events. In these situations, you have to pretty meticulously update the bounds of the container view as the size and position of its subviews change. Everything will always draw fine (because subviews aren't clipped by the bounds of their parent) but touches won't be received.
(This really should be a reply to an earlier post, but since I can't reply yet, it's stuck here...)

Resources