I always had the impression the origin of a View defines its position inside a parent view. But in my application the Origin is 0,0 no matter where I move my view on the Interface.
This is the code I use:
- (void)drawRect:(CGRect)rect
{
float orginX = rect.origin.x;
float orginY = rect.origin.y;
NSLog(#"X-Origin %f.", orginX);
NSLog(#"Y-Origin %f.", orginY);
Where is my mistake?
The rect passed in to drawRect: is the view's bounds, not it's frame, so its origin will be 0,0.
I believe the drawRect method is passed the frame relative to the view's origin, i.e. the origin will always be 0,0. This is because you should draw your view without knowing or caring where it is in relation to its superview.
Related
I have a UIView inside a UIScrollView and am using a CAShapeLayer with several sublayers to draw in the view. In some cases the layer is not visible and I'd like to scroll the view so that the layer becomes visible. To scroll I'm using:
[self.scrollView setContentOffset: CGPointMake(0, offset) animated: NO];
I'm having a hard time to figure out what the offset is. I've tried to get the enclosing rect, but that always has the origin at (0,0).
How can I calculate the position of the layer to use in offset?
UPDATE:
This seems to work to get the enclosing rect for all sublayers:
- (CGRect) enclosingLayerRect
{
CGRect rect = CGRectZero;
if (self.sublayers.count)
{
CAShapeLayer *layer = self.sublayers[0]; // need to get the first one, otherwise the origin will be (0,0)
rect = CGPathGetBoundingBox(layer.path);
for (CAShapeLayer *layer in self.sublayers)
{
CGRect layerRect = CGPathGetBoundingBox(layer.path);
rect = CGRectUnion(rect, layerRect);
}
}
return rect;
}
Feel free to comment if there is a better, easier way to do this.
Use the convertRect:toView: method to convert the frame to the scroll view's coordinate system, and the resulting rect should give you the required content offset.
I am doing objective-C app and I want create a UIView with my custom values and there is a problem that i can not fix.
This code works fine:
CGRect rect;
rect.origin.x = 10; rect.origin.y = 20;
rect.size.width = 30; rect.size.height = 40;
NSLog(#" %#",NSStringFromCGRect(rect));
But this one in NSLOG returns origin point to (0,0) and width and height fine:
UIView* aux = [[UIView alloc] initWithFrame:CGRectMake(10,20,30,40)];
NSLog(#" %#",NSStringFromCGRect(aux.bounds));
Is there any problem in initialization of UIView with CGRectMake?
Thanks
You are printing view.bounds, but not view.frame.
Just change it to:
NSLog(#" %#",NSStringFromCGRect(aux.frame));
First a recap on the question: frame, bounds and center and theirs relationships.
Frame A view's frame (CGRect) is the position of its rectangle in the superview's coordinate system. By default it starts at the top left.
Bounds A view's bounds (CGRect) expresses a view rectangle in its own coordinate system.
Center A center is a CGPoint expressed in terms of the superview's coordinate system and it determines the position of the exact center point of the view.
You need to use:
NSLog(#" %#",NSStringFromCGRect(aux.frame));
The origin of the bounds of a view are always (0, 0). You have to change your code to
NSLog(#" %#",NSStringFromCGRect(aux.frame));
See Apple's documentation for an explanation here
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.
So you need to use view.frame in your log method as-
NSLog(#" %#",NSStringFromCGRect(aux.frame));
Transforming a UIView affects its frame. Transforming a UIView's layer also affects the views frame in the same way. So scaling a view's layer, scales the frame. I'm trying to understand why transforms to the layer affect the views frame (even when view.layer.masksToBounds = NO is set).
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
NSLog(#"Before: %#", NSStringFromCGRect(view.frame));
// Output: {{0, 0}, {50, 50}}
// View transform applied
view.transform = CGAffineTransformMakeScale(2, 2);
NSLog(#"%#", NSStringFromCGRect(view.frame));
// Output: {{-25, -25}, {100, 100}}
// Layer transform applied
view.transform = CGAffineTransformIdentity;
view.layer.transform = CATransform3DMakeScale(2, 2, 1);
NSLog(#"%#", NSStringFromCGRect(view.frame));
// Output: {{-25, -25}, {100, 100}}
You shouldn't look at the frame value once you have a transform, since it's undefined what it contains at that point. This is mentioned in the documentation for the frame property on UIView:
WARNING
If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
If you need that to modify the frame, you have to do so using the center and bounds properties instead.
A frame is a very specific thing.
This rectangle defines the size and position of the view in its superview’s coordinate system. You use this rectangle during layout operations to size and position the view.
Transforms applied to a view effect the origin and size of that view in the superview which is why the view's frame changes.
Transforming subviews will effect the frames of the subviews, but not their superview's frame.
It's worth noting that bounds differs from frame in this respect. The bounds of a view is the origin and size of a view within it's own coordinate system. Transforms should not change a view's bounds, because the transform changes the size and placement of the view for external coordinates, but not the view's internal coordinates.
The frame is a computing property.
Basically, it's synthesized from center and bounds.( To know more, please search for anchorPoint of CALayer).
What's more, when transform is taken into consideration. The frame will be a bounding box that will cover the original box, even rotation or scale is applied.
And the default implementation of hitTest and pointInside will use the final frame, which means you can touch the translated or rotated view normally.
I am using the following code to rotate and transform an image view:
myImageView.transform = CGAffineTransformMakeRotation(45); // rotation
CGRect frame = myImageView.frame;
frame.origin.x = x_position;
frame.origin.y = y_position;
myImageView.frame = frame; // transformation
tl;dr: The frame is bogus when you have a non-identity transform. Change the center instead.
From the documentation:
Warning If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
When you are setting the transform on the first line and then reading the frame after it is undefined what you actually get back.
// Setting a non-identity transform (1)
myImageView.transform = CGAffineTransformMakeRotation(45);
CGRect frame = myImageView.frame; // At this point the frame is undefined (2)
// You are modifying something which is undefined from now on ...
Also, not only should you not read the frame because it is undefined, you should also not set it.
if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified.
The solution to that problem comes in the next sentence of the documentation
In that case, you can reposition the view using the center property and adjust the size using the bounds property instead.
Since you are only changing the position I would suggest that you don't touch the frame and instead read the center of the image view and set it to it's new value. The center is not affected by the transform in the same way as the frame.
CGPoint center = myImageView.center;
center.x = x_center_position; // Note that the `center` is not the same as the frame origin.
center.y = y_center_position; // Note that the `center` is not the same as the frame origin.
myImageView.center = center;
Try to remove autoresizing Mask of ImageView
imageView.autoresizingMask = UIViewAutoresizingNone;
This may solve your problem
I understand that in order to get the x or y coordinates of a UI Element you can use something like button.center.y or self.view.frame.size.height to get the height of your UIView). But, if I understand all this correctly, all of this is within the bounds of a UIView.
How do you obtain the same information in terms of the iPhone screen itself?
Maybe something like:
screen.view.frame.size.height
Thanks in advance.
EDIT: My UIElements are on a UIScrollView.
Convert the point from your view's coordinate system to the enclosing window's coordinate system, and then convert that point from the window to the screen.
Use -[UIView convertPoint:toView:] and -[UIWindow convertPoint:toWindow:]
UIWindow* myWindow = myView.window;
CGPoint pointInWindow = [myView convertPoint:pointInMyView toView:myWindow];
// note: toView:nil also works
CGPoint pointInScreen = [myWindow convertPoint:pointInWindow toWindow:nil];
By the way, an important point: view properties like center and frame are relative to the view's superview's coordinate system. So, to convert a view's center to the screen:
CGPoint pointInWindow = [myView.superview convertPoint:myView.center toView:nil];
CGPoint pointInScreen = [myView.window convertPoint:pointInWindow toWindow:nil];