What is the difference between bounds.size.width and bounds.width in swift? Will they return the same thing?
Thanks!
bounds is a CGRect struct property for a UIView that contains 2 nested structs within it; CGPoint and CGSize. These represent the points of origin for the view (x and y), and the size of the view in height and width specified in points respectively.
If you have a UIView that's 100 x 100, then: bounds.width will return 100, and bounds.size.width will also return 100. Basically they will return the same CGFloat values even if your CGRect has negative width and height values.
Related
I have a UIView that needs to be placed over a UIImage inside of a UIImageView at specific coordinates. The coordinates for the frame are referenced from the top left corner and have a specified width and height refrenced from the original image.
So, to make the frame, I am first getting the CGRect of the image using a category from the following post: UIImage size in UIImageView
I then get a scale factor to shrink the size of the frame by taking the original height, dividing it by the scaled height, and then dividing all of my values by that.
Lastly, I take the image CGRect and add the scaled position values of the frame to get my final CGRect for the view. However, the frame is always up and to the right of the desired location. Can anyone see what I'm doing wrong?
Here's the code (new is just a custom object with the correct frame parameters):
CGRect imageBounds = [self.imageView displayedImageBounds];
float scaleFactor = AppDelegate.usedImage.size.height / imageBounds.size.height;
new.height /= scaleFactor;
new.width /= scaleFactor;
new.positionX /= scaleFactor;
new.positionY /= scaleFactor;
UIView *faceRectView = [[UIView alloc] init];
faceRectView.tag = idx;
faceRectView.backgroundColor = [UIColor whiteColor];
faceRectView.frame = CGRectMake((imageBounds.origin.x + new.positionX), (imageBounds.origin.y + new.positionY), new.width, new.height);
[self.view addSubview:faceRectView];
CGPoint is a C structure that defines a point in a coordinate system. The origin of this coordinate system is at the top left on iOS and at the bottom left on OS X. In other words, the orientation of its vertical axis differs on iOS and OS X.
CGSize is another simple C structure that defines a width and a height value, and CGRect has an origin field, a CGPoint, and a size field, a CGSize. Together the origin and size fields define the position and size of a rectangle.
On iOS and OS X, an application has multiple coordinate systems. On iOS, for example, the application's window is positioned in the screen's coordinate system and every subview of the window is positioned in the window's coordinate system. In other words, the subviews of a view are always positioned in the view's coordinate system.
Take this example of a frame
and notice how it differs from the concept of bounds
CGGeometry Reference is a collection of structures, constants, and functions that make it easier to work with coordinates and rectangles. You may have run into code snippets similar to this:
CGPoint point = CGPointMake(self.view.frame.origin.x + self.view.frame.size.width, self.view.frame.origin.y + self.view.frame.size.height);
Not only is this snippet hard to read, it's also quite verbose. We can rewrite this code snippet using two convenient functions defined in the CGGeometry Reference.
CGRect frame = self.view.frame;
CGPoint point = CGPointMake(CGRectGetMaxX(frame), CGRectGetMaxY(frame));
To simplify the above code snippet, we store the view's frame in a variable named frame and use CGRectGetMaxX and CGRectGetMaxY. The names of the functions are self-explanatory.
The CGGeometry Reference defines functions to return the smallest and largest values for the x- and y-coordinates of a rectangle as well as the x- and y-coordinates that lie at the rectangle's center. Two other convenient getter functions are CGRectGetWidth and CGRectGetHeight.
Finally to conclude, check out the implementation of CGRectMake.
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
{
CGRect rect;
rect.origin.x = x; rect.origin.y = y;
rect.size.width = width; rect.size.height = height;
return rect;
}
Can you add Like that
faceRectView.frame = CGRectMake((0.0), (0.0), new.width, new.height);
I have 2 UIImageView. Both UIImageView has the same size proportion. The first UIImageView's size is always smaller or equal to the second UIImageView's size.
On the first UIImageView I have a UIView. This UIView the user can place it anywhere on the first UIImageView.
I want to find the x,y of the second UIView that will be on the second UIImageView with same proportion as the first UIView.
Please look at this image to be clear. Please note that Rectangle 1 is first UIImageView and Rectangle 2 is second UIImageView.
What I've tried:
CGFloat xMultiplier = imageView2.frame.size.width / imageView1.frame.size.width;
CGFloat yMultiplier = imageView2.frame.size.height / imageView1.frame.size.height
CGFloat view2x = view1.frame.origin.x * xMultiplier;
CGFloat view2y = view1.frame.origin.y * yMultiplier;
My application is an application that users can choose stickers (UIView) and place it anywhere on their photo. The stickerView on the second image is not in the same place as stickerView in the second image
The resulting x,y for my code is close to where it should be but its not exactly where it should be.
What am I missing?
Thanks!
First you should get the factor ( which you are already doing correctly ) , but then you have to calculate it proportionally to Point1 ( to some point in View1 ) , for example
CGFloat yFactor = view1.frame.size.height / view2.frame.size.height;
CGFloat xFactor = view1.frame.size.width / view2.frame.size.width;
CGPoint pointInView2 = CGPointMake(pointInView1.x / xFactor,pointInView1.y / yFactor);
pointInView1 is the origin of Sticker in View1
if sticker2 is subview of view2 , then origin will be pointInView2
if sticker2 is not subview of view2 , and they have the same superview , the origin will
CGPointMake(pointInView2.x + view2.frame.size.width,pointInView2.y + view2.frame.size.height);
if there is any other hierarchy , you can use UIVIews method convertPoint ...
[view2 convertPoint:pointInView2 toView:sticker2.superView]
assuming that the rectangle 2 is merely a rescaled version of rectangle 1. the position of view 2 is :
xpos_view2 = xpos_view1/xwidth_Rect1 * xwidth_Rect2
ypos_view2 = ypos_view1/ywidth_Rect1 * ywidth_Rect2
View2.position = CGPoint(x:xpos_view2, y:ypos_view2)
Calculate Ratio from smallest iPhone device
func ratioW(_ ofSize : CGFloat) -> CGFloat {
let bounds = UIScreen.main.bounds
let windowWidth = bounds.size.width
let temp = 320/ofSize
return windowWidth / temp
}
func ratioH(_ ofSize : CGFloat) -> CGFloat {
let bounds = UIScreen.main.bounds
let windowHeight = bounds.size.height
let temp = 480/ofSize
return windowHeight / temp
}
What is the difference between CGSize and CGRect?
As a beginner reading the CGGeometry Reference it wasn't immediately obvious, especially because there were references to size being a vector. I was kind of surprised that I couldn't find this basic question on StackOverflow, so I decided to research it more and post my answer below.
CGSize
CGSize is a width and a height. It is not technically considered a rectangle (which is one reason I confused it with CGRect in the beginning), but rather just a size. However, for the purpose of illustration, I will represent it as a rectangle below:
This combination of width and heigh is known as a struct, which is short for structure, and originally comes from the C language (but I will be using the Swift rather than Objective C syntax here). A structure is just a group of logically related variables. Here we can see the CGSize structure:
struct CGSize {
var width: CGFloat
var height: CGFloat
}
where CGFloat is either a float (32 bit) or a double (64 bit). (See What's the difference between using CGFloat and float?)
You can make a CGSize by doing the following:
var size = CGSize(width: 50, height: 30)
CGRect
CGRect is a rectangle. What is not immediately obvious from the name, though, is that in addition to a width and a height, it also has has an origin.
CGsize, by comparison, does not have an origin.
CGRect is also a structure. If fact, it's a structure of structures: a CGPoint (the origin) and CGSize (the width and height). Here it is:
struct CGRect {
var origin: CGPoint
var size: CGSize
}
where CGPoint is
struct CGPoint {
var x: CGFloat
var y: CGFloat
}
You can make a CGRect by doing the following:
var rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 50, height: 30))
Negative width and height
The width and height values can be negative. We can see what this looks like with CGRect. Notice how the origin appears on different corners:
Vectors
The documentation for CGSize says
A CGSize structure is sometimes used to represent a distance vector, rather than a physical size. As a vector, its values can be negative. To normalize a CGRect structure so that its size is represented by positive values, call the CGRectStandardize function.
Vectors in math have a magnitude (or length) and a direction. Although CGSize doesn't have an origin, you can see from the following diagram how the width and height along with their associated positive or negative values give both the length and direction.
Further Reading
CGGeometry Reference
Structures in Swift
Enums and Structs in Swift
Big Nerd Ranch - Rectangles, Part 1
CGRect, CGSize and CGPoint
C Structures
Difference between frame.size.width and frame.width
Short version: Their names are confusing, because they both can be used to represent rectangles.
CGSize has width and height
CGRect has x, y, width and height
You can think of CGRect as a CGSize with coordinates.
CGRect is commonly used to specify a UIView frame, which (top, left) coordinates are relative to the (top, left) coordinates of the parent view.
I have a unique requirement to set the coordinate origin of a UIView to be the center of the view. To clarify, the origin needs to be centered vertically and horizontally so that moving to the right is a positive X value, moving left is negative. For Y, moving above the center mark is positive, below is negative. This is essentially the same as geographic coordinates, using the prime meridian's intersection with the equator as the origin.
I am not even sure where to start with this. Can anyone offer up a hint? Thanks, V
Set the origin of the views bounds property to be the middle of the view, something like:
CGRect bounds = view.frame;
bounds.origin.x = bounds.size.width / -2.;
bounds.origin.y = bounds.size.height / -2.;
view.bounds = bounds;
You need functions to transform normal coordinates to strange and vice versa.
For example method in your UIView subclass
- (CGPoint)normalToStrange:(CGPoint)normal
{
return CGPointMake(normal.x - self.width / 2, -normal.y + self.height / 2);
}
I am creating a UIImageView and adding it in a loop to my view, I set the initial frame to 0,0,1,47 and each passage of the loop I change the center of the image view to space them out.
I am always using 0 as the origin.y
The problem is the origin reference is in the centre of the image view, assuming we was in interface builder, this is equivalent to the image below.
How can I change the reference point in code ?
After reading these answers and your comments I'm not really sure what is your point.
With UIView you can set position by 2 ways:
center – It definitely says it is the center.
frame.origin – Top left corner, can't be set directly.
If you want the bottom left corner to be at x=300, y=300 you can just do this:
UIView *view = ...
CGRect frame = view.frame;
frame.origin.x = 300 - frame.size.width;
frame.origin.y = 300 - frame.size.height;
view.frame = frame;
But if you go one level deeper to magical world of CALayers (don' forget to import QuartzCore), you are more powerful.
CALayer has these:
position – You see, it don't explicitely says 'center', so it may not be center!
anchorPoint – CGPoint with values in range 0..1 (including) that specifies point inside the view. Default is x=0.5, y=0.5 which means 'center' (and -[UIView center] assumes this value). You may set it to any other value and the position property will be applied to that point.
Example time:
You have a view with size 100x100
view.layer.anchorPoint = CGPointMake(1, 1);
view.layer.position = CGPointMake(300, 300);
Top left corner of the view is at x=200, y=200 and its bottom right corner is at x=300, y=300.
Note: When you rotate the layer/view it will be rotated around the anchorPoint, that is the center by default.
Bu since you just ask HOW to do specific thing and not WHAT you want to achieve, I can't help you any further now.
The object's frame includes its position in its superview. You can change it with something like:
CGRect frame = self.imageView.frame;
frame.origin.y = 0.0f;
self.imageView.frame = frame;
If I am understanding you correctly, you need to set the frame of the image view you are interested in moving. This can be done in the simple case like this:
_theImageView.frame = CGRectMake(x, y, width, height);
Obviously you need to set x, y, width, and height yourself. Please also be aware that a view's frame is in reference to its parent view. So, if you have a view that is in the top left corner (x = 0, y = 0), and is 320 points wide and 400 points tall, and you set the frame of the image view to be (10, 50, 100, 50) and then add it as a subview of the previous view, it will sit at x = 10, y = 50 of the parent view's coordinate space, even though the bounds of the image view are x = 0, y = 0. Bounds are in reference to the view itself, frame is in reference to the parent.
So, in your scenario, your code might look something like the following:
CGRect currentFrame = _theImageView.frame;
currentFrame.origin.x = 0;
currentFrame.origin.y = 0;
_theImageView.frame = currentFrame;
[_parentView addSubview:_theImageView];
Alternatively, you can say:
CGRect currentFrame = _theImageView.frame;
_theImageView.frame = CGRectMake(0, 0, currentFrame.size.width, currentFrame.size.height);
[_parentView addSubview:_theImageView];
Either approach will set the image view to the top left of the parent you add it to.
I thought I would take a cut at this in Swift.
If one would like to set a views position on the screen by specifying the coordinates to an origin point in X and Y for that view, with a little math, we can figure out where the center of the view needs to be in order for the origin of the frame to be located as desired.
This extension uses the frame of the view to get the width and height.
The equation to calculate the new center is almost trivial. See the below extension :
extension CGRect {
// Created 12/16/2020 by Michael Kucinski for anyone to reuse as desired
func getCenterWhichPlacesFrameOriginAtSpecified_X_and_Y_Coordinates(x_Position: CGFloat, y_Position: CGFloat) -> CGPoint
{
// self is the CGRect
let widthDividedBy2 = self.width / 2
let heightDividedBy2 = self.height / 2
// Calculate where the center needs to be to place the origin at the specified x and y position
let desiredCenter_X = x_Position + widthDividedBy2
let desiredCenter_Y = y_Position + heightDividedBy2
let calculatedCenter : CGPoint = CGPoint(x: desiredCenter_X, y: desiredCenter_Y)
return calculatedCenter // Using this point as the center will place the origin at the specified X and Y coordinates
}
}
Usage as shown below to place the origin in the upper left corner area, 25 pixels in :
// Set the origin for this object at the values specified
maskChoosingSlider.center = maskChoosingSlider.frame.getCenterWhichPlacesFrameOriginAtSpecified_X_and_Y_Coordinates(x_Position: 25, y_Position: 25)
If you want to pass a CGPoint into the extension instead of X and Y coordinates, that's an easy change you can make on your own.