What's the difference between bounds and frame? In fact, why does 'bounds' even exist? The size of 'bounds' is equal to the frame's size, and the bound's origin should always be 0,0.
From the View and Window Architecture Programming Guide for iOS:
A view object tracks its size and location using its frame, bounds,
and center properties:
The frame property contains the frame rectangle, which specifies the
size and location of the view in its superview’s coordinate system.
The bounds property contains the bounds rectangle, which specifies the
size of the view (and its content origin) in the view’s own local
coordinate system.
The center property contains the known center point of the view in the
superview’s coordinate system.
Here is a good visualization of that explanation:
The bound's origin is not always 0,0. It's easy to understand the difference between frame and bounds, if you watch how change bounds property of UIScrollView during scrolling.
For example, you have UIScrollView with frame (0, 0, 320, 460), bounds (0, 0, 320, 460) and ContentSize (640, 460). Its frame always will be (0, 0, 320, 460), but the X coordinate of bounds will change depending on distance of scrolling.
It can be useful if you want to change something in you UIScrollView (create and remove pages diynamically for example), so you want to know distance of scrolling.
The apple documents in the first answer don't cover what happens to the frame and the bounds after rotating to landscape orientation. So to be more complete, you should know that the frame of the window and the root view does not change after rotation, but the bounds do. See this article for a a little more detail and be careful using frame as a reference for anything other than portrait orientation.
From the article:
If your view controller has the top-level non-window view (i.e., it’s
the bottom-most view controller), then
self.frame
is always in portrait orientation. Wha? Yes, always in portrait –
what changes is the transform of your view. So your
self.bounds
is always accurate (keeping in mind the last point), but
self.frame
may or may not give the aspect ratio that the user is really seeing,
since each view’s frame is reported in terms of the superview’s
coordinates, and takes into account any transforms applied to the
view.
A views frame is the size of a rectangle it can completely fit into. It always seems as if the bounds and the frame are same but that's not the case.
Consider a square which is just rotated about 45 degrees!
Here the frame of the this rotated square will be the rectangle to completely fill it in and so it will differ from the bounds of this object.
P.S mostly in rotated objects frames and bounds tend to differ.
frame is coordinates values in the super view's coordinate system
bounds is used by the drawing system to draw the view's content, when the drawing is done, the system will use a transform operation to assign the content to the view's frame
Related
I have found out that
The bounds of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to its own coordinate system (0,0).
The frame of an UIView is the rectangle, expressed as a location (x,y) and size (width,height) relative to the superview it is contained within.
But my doubt is in which scenario I will have the bounds to be used . Cant we use frames in
all the cases ? Is there a need to use the bound ?
From the View Programming Guide:
The frame property contains the frame rectangle, which specifies the size and location of the view in its superview’s coordinate system.
The bounds property contains the bounds rectangle, which specifies the size of the view (and its content origin) in the view’s own local coordinate system.
Frame:
You use the center and frame properties primarily for manipulating the
geometry of the current view. For example, you use these properties
when building your view hierarchy or changing the position or size of
a view at runtime. If you are changing only the position of the view
(and not its size), the center property is the preferred way to do so.
The value in the center property is always valid, even if scaling or
rotation factors have been added to the view’s transform. The same is
not true for the value in the frame property, which is considered
invalid if the view’s transform is not equal to the identity
transform.
Bounds:
You use the bounds property primarily during drawing. The bounds
rectangle is expressed in the view’s own local coordinate system. The
default origin of this rectangle is (0, 0) and its size matches the
size of the frame rectangle. Anything you draw inside this rectangle
is part of the view’s visible content. If you change the origin of the
bounds rectangle, anything you draw inside the new rectangle becomes
part of the view’s visible content.
So you do the following things with the frame:
resizing view
moving / reposition view
Situation when you should use bounds:
when you do drawing inside the view with bounds e.g. in the drawRect: method of UIView.
adding subViews to a parent views bounds
I have a simple test app on which my rootViewController's UIView contains a bunch of UIView subviews. Each one of those UIView subview is backed by a CAShapeLayer.
I want the composition created by those subviews ( the four shapes that are within the dotted area .. ) to always stay vertically and horizontally centered with respect to my
UIWindow. (the minimium size of the left/right, top/bottom margins will be subject to be changed at runtime at each orientation change )
So for example when i rotate to portrait i will have to
resize and reposition those single shapes so that the whole figure will be mantained centered and each CAShapeLayer sublayer stays sharp ( i want their path to be resized not just a raster resize )
what would be the best technique to resize/move the shapes to always have a centered composition while maintaining path crisp appearance for the shapes?
Ultimately for me it will be good to have an answer to this: how can i shrink the subviews as a whole? i mean their sizes and relative positions?
Thanks
You can use CGPathCreateMutableCopyByTransformingPath() or -[UIBezierPath applyTransform:] to recalculate all points in the path.
I can't find an answer for this one.
I would like to know how to have the image size in a calayer's to be lower than calayer's bound's size.
I've got several pawns in an iPad game, each is a CALayer and I have them resize simply with a contentsGravity=kCAGravityResizeAspect. Image is 128x128 inside of a CALayer of 30x30 so the image gets resized automatically to 30x30 and because of both being a box, aspect ratio maintains and works.
Here I set CALayer's bounds proportional relative to superview's size, so the Pawns always present the same relative size to the view. This one is inside my sprite class subclass of calayer:
-(void) setSpriteScaleToDice {
CGFloat newSize = [self superlayer].bounds.size.width * 0.066666667f;
self.bounds=CGRectMake(0.0f, 0.0f, newSize, newSize);
self.contentsGravity = kCAGravityResizeAspect;
}
Note that in my case the CALayer bounds gets a maximum of 30x30 which is small for a touch. That's the problem I'm facing, due to this small size it's difficult to "touch" them, sometimes touch fails...
One of the ideas that I'm thinking is to increase the "bounds" of the calayer, while keeping the image at its original size. The problem is that I've search a lot and tried several options with contentsGravity, contentsCenter, contentsScale, etc... without success.
In particular, as per apple docs looks like the way to go is with contentsCenter (and not using contentsGravity), however I get deformation in the bitmap...
Please, any idea is really welcome, and thanks in advance,
Luis
This is probably a silly question, but why are you using CALayers for this instead of UIViews? UIImageView has a contentMode property that lets you do this easily (not to mention being easier to use for touch event handling).
That said, CALayer has a contentsRect property that appears to let you define a sub-rectangle for contents to be drawn within, so that may let you do what you want.
Another option would be to place your image layer inside a larger layer and use that for the hit test.
CAlayer CGFloat contentsScale
/* Defines the scale factor applied to the contents of the layer. If
* the physical size of the contents is '(w, h)' then the logical size
* (i.e. for contentsGravity calculations) is defined as '(w /
* contentsScale, h / contentsScale)'. Applies to both images provided
* explicitly and content provided via -drawInContext: (i.e. if
* contentsScale is two -drawInContext: will draw into a buffer twice
* as large as the layer bounds). Defaults to one. Animatable. */
If you want your image drawn in the CALayer at a size other than the CALayer you need to create your own drawInContext: method and draw the image rather than setting the CALayer's contents property. Do not set the contents property, create your own to track the image you want to draw.
I have a problem understanding how the parameter passed to the drawInRect method is defined when a rotation transformation is performed on a UIView.
To give an example I have a UIView which I rotated with an angle of 307 degree.
In the drawInRect method I log the following:
self.frame: {{103.932, 273.254}, {64.3007, 84.3374}}
rect (the variable passed as parameter:{{0, 0.102913}, {18, 89}}
The problem is that according to the documentation I should not draw outside of rect, but considering what I should draw, there is no way my images will fit there.
Can anyone explain to me how I am supposed to use drawInRect in the case my UIView is rotated ?
To give more detail about my problem, here is what I do:
I have a scrollview with a contentView inside (subclassed). I add my UIViews in the content view.
The views in question are composed of a handler image (bottom left) and the main image (top right). Users are supposed to grab the view by pressing the handler but that's not the point.
The drawInRect method of the UIView contains the following:
[_image drawInRect:CGRectMake(handlerSize.width, 0, _image.size.width, _image.size.height)];
CGSize size = CGSizeMake(kHandPickerWidth/self.scrollViewScale, kHandPickerHeight/self.scrollViewScale);
[_handPickerImage drawInRect:(0, _image.size.height, size.width, size.height)];
The UIViews objects are added at viewWillAppear in the content view doing the following:
first instanciate,
then addSubview:
then I set the scrollViewScale parameter,
then I set the frame parameter (according to the top right image displayed (which may vary)
then I rotate the UIView.
Starting from line three, the code is executed from the
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
to make sure every variable is set properly when displayed is needed.
The line defining the size variable is to adjust the marker's size no mater the zoomScale value of the scroll view.
You basically just draw as you would normally, and the painting will be rotated by iOS for you. You can get this transformation information if you would want to.
You need to get a reference to the currect graphics context:
CGContextRef ctx = UIGraphicsGetCurrentContext();
Then query the transformation matrix directly:
CGAffineTransform tf = CGContextGetCTM(ctx);
NSLog(#"current ctm: %#",NSStringFromCGAffineTransform(tf));
or better, get the transfrom from your drawing function to the device:
CGAffineTransform tf = CGContextGetUserSpaceToDeviceSpaceTransform(ctx);
NSLog(#"user->device transform: %#",NSStringFromCGAffineTransform(tf));
And in drawRect: you should not rely too much on the passed CGRect, because it serves mostly as a hint to what piece of the view needs updating. (e.g. because you called -setNeedsDisplayInRect: on it). To get the actual bounds where your view lives in, use self.bounds.
Drawing outside of the CGRect is no real problem, but will only hurt performance a little.
edit
ps. to clarify: self.frame is the frame of your view in the parent view coordinate system. It changes if you move, rotate or otherwise transform the view. self.bounds is the frame of your view in its own coordinate system, and (therefore) remains constant under changes of position or transformations.
So I found a solution to my problem:
I was setting the frame parameter multiple times with some CGAffineTransformation defined which is not supposed to be done.
Now each time I need to reset the frame I reset the affine transform, change the frame and set the back the affine transform.
Everything works as supposed to this way.
I am attempting to reveal (through animation) a UIView. Specifically I want to show the center portion of the view and then slowly reveal the outer edges of it (sort of like pulling back a curtain).
My first attempt was to simply set the bounds rect to be smaller and animate it to be the full size of the view's frame, but this did not have the desired effect since by changing the bounds I was also changing the frame.
If what I am trying to do does not sound possible (at least not in a simple manner), at least I would like to be able to have is some way to make the subviews of the main view stationary relative to the screen, NOT their parent view, as the parent resizes (this would give a similar effect).
Any ideas?
Thank you,
-Matt
It definitely is possible. What you need to do is
For the view you're animating, setAutoresizesSubviews:NO and setClipsToBounds:YES.
Set the view's bounds (NOT the frame) to a rect with zero size and origin at the center point of the rect you want the view to occupy when it is fully revealed (in the view's own coordinate system). In other words, startBounds.origin.x should equal half of endBounds.size.width and similarly for y.
Position the view by setting its center (in the parent view's coordinate system).
In an animation block, change the view's bounds to zero origin and full size.
In the animation's completion block, you probably want to setAutoresizesSubviews:YES again.
You may also need to set the view's autoresizing mask to be fully flexible (springs but no struts), depending on what other layout gets triggered as you resize.
Sounds like you want to change its clipping. A cheap (code-wise) way to do that would be to insert the view into a parent view (with autoresizing set to center it), set the parent to clip its children and then animate the parent's frame.