Setting a view's bounds changes the coordinates of the frame, WHY? - iphone

Why setting the bounds property of a UIView messes up it's frame's coordinates?
For example:
self.view.frame = CGRectMake(10, 10, 200, 200);
CGRect b = CGRectMake(0, 0, 399, 323);
self.view.bounds = b;
I would expect the view's frame to be (10, 10, 399, 323) but instead the coordinates get some weird values like (-89.5 -51.5; 399 323).
Thanks!

From the UIView class reference:
Changing the bounds size grows or shrinks the view relative to its center point.
So it is keeping the center point in the same place, which means the origin of the frame has to adjust.
If you want to resize the view but keep the origin in the same place, set the frame instead of the bounds.

Related

SpriteKit: SKShapeNode.calculateAccumulatedFrame returns a frame, which is bigger than its content

I create a SKShapeNode rectangle with a fixed size using the following code
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 100, 200)];
CGRect accumulatedFrame = rect.calculateAccumulatedFrame;
Debugging the code above, the accumulatedFrame holds the values below:
origin=(x=-0.5, y=-0.5) size=(width=101, height=201)
Why is the calculated, accumulated frame bigger than the intended 100x200 ?
Thanks in advance for any hint :)
Code sample:
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 200, 100)];
CGRect accumulatedFrame = rect.calculateAccumulatedFrame;
CGRect frame = rect.frame;
debugger returns origin=(x=-0.5, y=-0.5) size=(width=201, height=101) for the accumulated frame property
debugger returns origin=(x=-0.5, y=-0.5) size=(width=201, height=101) for the frame propery
Using an origin > (0,0) adds only 0,5 to width and height; seems, that sprite-kit returns an accumulated frame, which really contains the node(s) and adds 0.5, so none of the node borders intersects the border of the accumulated frame. Didn't find anything about it in the api reference...
I believe the issue you are dealing with is that if you have a strokeColor defined, that border stroke adds to size of the rectangle.
Try this code :
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 200, 100)];
rect.strokeColor = nil;
You have to look at the stroke as being additive, and adding to the dimensions of your shape.

Adding sublayer to view.layer changes the frame position?

I'm adding a CALayer as a sublayer to a UIView's layer property as follows:
_graphicLayer = [[GraphicLayer alloc] init];
self.bounds = _graphicLayer.bounds;
_graphicLayer.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[self.layer addSublayer:_graphicLayer];
As you can see, I change the position of _graphicLayer to account for the centered anchorPoint. I'm noticing after I add this sublayer, that it's changing the views frame to (-self.bounds.width / 2, -self.bounds.height / 2). Why is this happening? If I change the position of _graphicLayer, I thought that was only relative to its parent view. Why would it affect the views frame property? (and I don't want to have to adjust the anchorPoint of either the views layer property or _graphicLayer).
You're currently doing
self.bounds = _graphicLayer.bounds;
which would change the view's (self's) frame as it would change the width and height. Maybe you meant to do :
_graphicLayer.bounds = self.bounds;
See here about how bounds affect the frame:Link

Calculating new origin of insetted CGRect after its size changes

I have a CGRect A and CGRect B where B is centered inside of A (a CGRect contains the x and y origin and height and width size of a rectangle). If I increase the width and height of A by some proportion, and also increase the width and height of B by that same proportion, will multiplying the x origin of B and the y origin of B by this same proportion (for the width and height respectfully), will that keep B in the center of A as both grow by the new proportion? I've tested this out in a few different scenarios and it works, but just wanted to verify it'll work for all situations as I am not that sharp in math.
Also, was wondering if there is a method that will simply allow you to multiply all values of a CGRect by this proportion without having to do it manually (couldn't find one in the docs).
UPDATE: Actually, this will not work...trying to think of a methodology that will allow me to correctly position a view within another view after a proportional increase in size for both.
Yes, what you proposed works, but only if the origin of the outer CGRect is 0,0 or if you multiply its origin by the factor, too. If you don't do that, the inner rect will be shifted to the bottom right.
Here's what happens if you multiple both origin and size:
If you don't multiply the outer rect's origin, this happens:
From your question, it isn't entirely clear what you're trying to achieve.
If you want to enlarge a CGRect and (re)center it another one, use these functions:
// center a CGRect in another one
static inline CGRect ALRectCenterInRect(CGRect outerRect, CGRect innerRect)
{
return CGRectMake(CGRectGetMidX(outerRect)-innerRect.size.width/2, CGRectGetMidY(outerRect)-innerRect.size.height/2, innerRect.size.width, innerRect.size.height);
}
// multiply each value of a CGRect with factor
// combine with CGRectIntegral() to prevent fractions (and the resulting aliasing)
static inline CGRect ALRectMultiply(CGRect rect, CGFloat factor)
{
return CGRectMake(rect.origin.x*factor, rect.origin.y*factor, rect.size.width*factor, rect.size.height*factor);
}
How to use them:
CGRect centeredInnerRect = ALRectCenterInRect(outerRect, innerRect);
CGRect multipliedRect = ALRectMultiply(someRect, 1.5);
However, when dealing with CGRects, it's usually about UIViews. If you want to center a UIView in its superview, do this:
someSubview.center = CGPointMake(CGRectGetMidX(someSuperview.bounds), CGRectGetMidY(someSuperview.bounds));
If the inner view has the same superview as the outer view, you can simply do this to center it in the outer view:
innerView.center = outerView.center;

bounds and frames: how do I display part of an UIImage

My goal is simple; I want to create a program that displays an UIImage, and when swiped from bottom to top, displays another UIImage. The images here could be a happy face/sad face. The sad face should be the starting point, the happy face the end point. When swiping your finger the part below the finger should be showing the happy face.
So far I tried solving this with the frame and bounds properties of the UIImageview I used for the happy face image.
What this piece of code does is wrong, because the transition starts in the center of the screen and not the bottom. Notice that the origin of both frame and bounds are at 0,0...
I have read numerous pages about frames and bounds, but I don't get it. Any help is appreciated!
The loadimages is called only once.
- (void)loadImages {
sadface = [UIImage imageNamed:#"face-sad.jpg"];
happyface = [UIImage imageNamed:#"face-happy.jpg"];
UIImageView *face1view = [[UIImageView alloc]init];
face1view.image = sadface;
[self.view addSubview:face1view];
CGRect frame;
CGRect contentRect = self.view.frame;
frame = CGRectMake(0, 0, contentRect.size.width, contentRect.size.height);
face1view.frame = frame;
face2view = [[UIImageView alloc]init];
face2view.layer.masksToBounds = YES;
face2view.contentMode = UIViewContentModeScaleAspectFill;
face2view.image = happyface;
[self.view addSubview:face2view];
frame = CGRectMake(startpoint.x, 0, contentRect.size.width, contentRect.size.height);
face2view.frame = frame;
face2view.clipsToBounds = YES;
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint movepoint = [[touches anyObject] locationInView: self.view];
NSLog(#"movepoint: %f %f", movepoint.x, movepoint.y);
face2view.bounds = CGRectMake(0, 0, 320, 480 - movepoint.y);
}
The UIImages and UIImageViews are properly disposed of in the dealloc function.
Indeed, you seem to be confused about frames and bounds. In fact, they are easy. Always remember that any view has its own coordinate system. The frame, center and transform properties are expressed in superview's coordinates, while the bounds is expressed in the view's own coordinate system. If a view doesn't have a superview (not installed into a view hierarchy yet), it still has a frame. In iOS the frame property is calculated from the view's bounds, center and transform. You may ask what the hell frame and center mean when there's no superview. They are used when you add the view to another view, allowing to position the view before it's actually visible.
The most common example when a view's bounds differ from its frame is when it is not in the upper left corner of its superview: its bounds.origin may be CGPointZero, while its frame.origin is not. Another classic example is UIScrollView, which frequently modifies its bounds.origin to make subviews scroll (in fact, modifying the origin of the coordinate system automatically moves every subview without affecting their frames), while its own frame is constant.
Back to your code. First of all, when you already have images to display in image views, it makes sense to init the views with their images:
UIImageView *face1view = [[UIImageView alloc] initWithImage: sadface];
That helps the image view to immediately size itself properly. It is not recommended to init views with -init because that might skip some important code in their designated initializer, -initWithFrame:.
Since you add face1view to self.view, you should really use its bounds rather than its frame:
face1view.frame = self.view.bounds;
Same goes for the happier face. Then in -touchesMoved:… you should either change face2view's frame to move it inside self.view or (if self.view does not contain any other subviews besides faces) modify self.view's bounds to move both faces inside it together. Instead, you do something weird like vertically stretching the happy face inside face2view. If you want the happy face to slide from the bottom of self.view, you should initially set its frame like this (not visible initially):
face2view.frame = CGRectOffset(face2view.frame, 0, CGRectGetHeight(self.view.bounds));
If you choose to swap faces by changing image views' frames (contrasted with changing self.view's bounds), I guess you might want to change both the views' frame origins, so that the sad face slides up out and the happy face slides up in. Alternatively, if you want the happy face to cover the sad one:
face2view.frame = face1view.frame;
Your problem seems to have something to do with the face2view.bounds in touchesMoved.
You are setting the bounds of this view to the rect, x:0, y:0, width:320, height:480 - y
x = 0 == left on the x axis
y = 0 == top on the y axis
So you are putting this image frame at the upper left corner, and making it fill the whole view. That's not what you want. The image simply becomes centered in this imageView.

Rotate using a transform, then change frame origin, and view expands?

This is quite the iPhone quandry. I am working on a library, but have narrowed down my problem to very simple code. What this code does is create a 50x50 view, applies a rotation transform of a few degrees, then shifts the frame down a few times. The result is the 50x50 view is now much larger looking.
Here's the code:
// a simple 50x50 view
UIView *redThing = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)];
redThing.backgroundColor = [UIColor redColor];
[self.view addSubview:redThing];
// rotate a small amount (as long as it's not 90 or 180, etc.)
redThing.transform = CGAffineTransformRotate(redThing.transform, 0.1234);
// move the view down 2 pixels
CGRect newFrame = CGRectMake(redThing.frame.origin.x, redThing.frame.origin.y + 2, redThing.frame.size.width, redThing.frame.size.height);
redThing.frame = newFrame;
// move the view down another 2 pixels
newFrame = CGRectMake(redThing.frame.origin.x, redThing.frame.origin.y + 2, redThing.frame.size.width, redThing.frame.size.height);
redThing.frame = newFrame;
// move the view down another 2 pixels
newFrame = CGRectMake(redThing.frame.origin.x, redThing.frame.origin.y + 2, redThing.frame.size.width, redThing.frame.size.height);
redThing.frame = newFrame;
// move the view down another 2 pixels
newFrame = CGRectMake(redThing.frame.origin.x, redThing.frame.origin.y + 2, redThing.frame.size.width, redThing.frame.size.height);
redThing.frame = newFrame;
So, what the heck is going on? Now, if I move the view by applying a translation transform, it works just fine. But that's not what I want to do and this should work anyway.
Any ideas?
From the UIView documentation:
If the transform property is also set, use the bounds and center properties instead; otherwise, animating changes to the frame property does not correctly reflect the actual location of the view.
Warning: If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
In other words, I would be wary of the frame property when a transform is set.