Frame, Center, bounds and Transform

The property in UIView that represents the position and size of the view displayed in the superview is frame. The system also provides two other properties, Center and bounds. The Center property value describes the position of the center point of the view in the superview, and the size section of the bounds property describes the size inherent in the view itself. Note that the Origin section of the bounds property describes the position of the points in the view’s internal coordinate system, which affects the position of the inside view. In addition, the system provides a transform property to implement affine transformations of the view: such as translation, zoom, rotation, and tilt effects.

Of the four properties, the other three are entity properties except the frame property, which is a computational property. The value of the frame is calculated depending on these three properties. Before introducing the calculation formula of frame, we need to understand three concepts: CALayer, Anchor Point and affine transformation.

The reference coordinates of iOS and macOS are different. By default, the origin is in the upper left corner of the view for iOS, and in the lower left corner of the view for macOS.

Location mapping between UIView and CALayer

UIView is an abstract view class that is mainly responsible for storing data and implementing the operation logic. The CALayer is an abstract class for rendering and displaying information on the screen. The position and size of a view are also part of the rendering display information. Therefore, the internal implementation of several properties in the above view is actually delegated to the corresponding properties in the CALayer, and the corresponding relationship table is as follows:

UIView CALayer
frame frame
center position
bounds bounds
transform affineTransform

Anchor Point (Anchor Point)

An anchor point is the relative value of a point in the view that determines the position of the view in the superview. A view is a rectangular area with an infinite number of points in it. The position of a view can be determined by specifying the position of a point in the superview, and the position of the point in the specified view is the anchor point. The system provides an anchorPoint property for the layer to represent the anchor points. The anchor point is a relative coordinate value, where the upper left corner is (0,0) and the lower right corner is (1,1). The anchor point value of the center point is (0.5,0.5) *(for macOS, because of different coordinate systems, (0,0) is in the lower left corner, and (1,1) is in the upper right corner)*. By default, the system uses the center point within the layer as the anchor point, which is why the view center property describes where the center point of the view is located in the superview. Anchors are concepts in CALayer, not views. From the above mapping between the view property and the layer property, you can see that the center property of the view corresponds to the position property of the layer. In fact, the latter is a better representation of the concept of anchor position, because position indicates the absolute position of the layer’s anchor in the parent layer. By default, the anchor is (0.5,0.5), which is exactly what the center property indicates, but we can change the value of the anchor. Like the following code:

// set frame to (0,0,100,100), and center to (50,50), indicating that center is exactly where the center is. UIView *A = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)]; UIView *A = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)]; A. ayer. AnchorPoint = CGPointMake (0, 0). // the frame value will change to (50,50,100,100), but the center value will remain (50,50) instead of indicating the center of the view. CGRect frame = A.frame;Copy the code

Affine transformation

Affine transformation is a linear transformation of all points in a coordinate space followed by a translation. The iOS view property transform is used to affine the view. We can easily move, scale, rotate and tilt the view through affine transformation. The transform property is data of a struct type:

struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};
Copy the code

The following formula is to use this structure to realize the affine transformation processing of coordinate points from (x0,y0) to (x1,y1) :

x1 = a*x0 + b*y0 + tx
y1 = c*x0 + d*y0 + ty
Copy the code

The system provides a number of function apis beginning with CGAffine to construct and handle a variety of common affine transformation operations. By default the view’s transform property value is a CGAffineTransformIdentity shows that won’t be any affine transformation to deal with the view.

The final position and size of a view rendered to the screen are determined by the original position and size of the view plus affine transformations. The final position and size of the view rendered to the screen can be obtained through the Frame property.

Calculation rules for frames

As you can see from the description above, the final rendering position and size of a view is achieved by setting the center, bounds, Transform, and anchorPoint properties of the view or layer. But this is too cumbersome, so you can do this with the Frame property to make things easier. The frame property is a computational property. Here is the pseudo code for getting and setting this property:

-(CGRect)frame
{
      CGRect retValue = CGRectZero;
      if(CGAffineTransformIsIdentity (self. The transform)) {/ / not set / / position under the condition of affine transformation is equal to the position of the center point minus the view size multiplied by the value of the anchor. retValue.origin.x = self.center.x - self.bounds.size.width * self.layer.anchorPoint.x; retValue.origin.y = self.center.y - self.bounds.size.height * self.layer.anchorPoint.y; Retvalue.size. Width = self.bounds.size.width; retValue.size.height = self.bounds.size.height; }else{ CGAffineTransform left = CGAffineTransformMakeTranslation(-1 * self.bounds.size.width * self.layer.anchorPoint.x, -1 * self.bounds.size.height * self.layer.anchorPoint.y); // since the following coordinate transformation application starts at (0,0), the right here specifies the position of the center point. CGAffineTransform right = CGAffineTransformMakeTranslation(self.center.x, self.center.y); // The entire composition is left multiplied by the view's TansForm property and then right multiplied by the right transformation. CGAffineTransform concat = CGAffineTransformConcat(CGAffineTransformConcat(left, self.transform), right); RetValue = CGRectApplyAffineTransform (CGRectMake (0, 0, the self. The bounds. The size, width, and the self. The bounds. The size, height), concat); }return retValue
}

-(void)setFrame:(CGRect) Frame {// set Frame without considering the affine coordinate transform property transform. self.center.x = frame.origin.x + self.bounds.size.width * self.layer.anchorPoint.x; self.center.y = frame.origin.y + self.bounds.size.height * self.layer.anchorPoint.y; self.bounds.size.width = frame.size.width; self.bounds.size.height = frame.size.height; }Copy the code

Can be seen from the above code: when a view set up the CGAffineTransformIdentity affine change value, we can no longer by setting the frame attribute values to modify the position and size of the view, or eventually display effect is not clear. Therefore, if you want to adjust the position and size of a view with affine transform properties, you need to use the center and bounds properties instead of the Frame properties. Take the following example:

// If the view size is changed from (width0, height0) to (width1, height1), the correct code should be set as follows: view.bounds.size = CGSizeMake(width1, height1); X += (width1-width0) * view.layer.anchorPoint. X; view.center.y += (height1 - height0) * view.layer.anchorPoint.y; X = x1 + view.bounds.size. Width * view.layer.anchorPoint. X; // If the view position is changed from (x0,y0) to (x1,y1), the correct code should be set as follows: view.center.x = x1 + view.bounds.size. view.center.y = y1 + view.bounds.size.height * view.layer.anchorPoint.y;Copy the code

After finishing the layout, AutoLayout calculates the values of the position and size internally modified by the center and bounds properties, so that the final display does not cause exceptions due to affine transformations. This also explains why changing the position and size by modifying the Frame property after setting the constraint with AutoLayout does not work.

In the early stage, MyLayout was calculated by modifying the frame property of the view to complete the layout, but later it was found that some programmers found the view display exception after setting the affine transform property. Later versions of MyLayout were also changed to modify the center and bounds properties of the view to solve this problem.