The whole Transform thing

The reason for this article is that once when I was moving bricks, I saw a piece of code written by my friend which I thought was very magical (it may be my lack of knowledge), so I started the text without much wordy.

Before we start, how can we implement this very simple UI

I’ll keep you in suspense

In iOS, the implementation of transform is mainly the CGAffineTransform structure under the Framework of CoreGraphics, which can realize the rotation, scaling, translation and combined transformation of view or layer.

#### Implementation Principle

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

As explained in apple’s documentation, the values in the above structure are assembled into a 3×3 matrix as follows:? \begin{bmatrix} a&b&0\ c&d&0\ tx&ty&1\ \end{bmatrix}?

View coordinates (x, y) are transformed to (x ‘, y ‘) :

After expanding the matrix, the following equation can be obtained, and the affine transformation is carried out in accordance with the following relations:

x’ = ax+cy+tx

y’ = bx+dy+ty

Now let’s look at how several transformations corresponding to transform work

1. The translation

The initialization method is as follows:

/* Return a transform which translates by `(tx, ty)': t' = [ 1 0 0 1 tx ty ] */

CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,
  CGFloat ty) CG_AVAILABLE_STARTING(10.0.2.0);
Copy the code

The corresponding matrix is:? \begin{bmatrix} 1&0&0\ 0&1&0\ tx&ty&1\ \end{bmatrix}? And then we get x prime is equal to x plus tx, y prime is equal to y plus ty

The shift looks pretty simple. If tx is positive, it moves forward in the x axis, and vice versa, and the ty value is the same (positive for x to the right, positive for y down)

2. Zoom

The initialization method is as follows:

/* Return a transform which scales by `(sx, sy)': t' = [ sx 0 0 sy 0 0 ] */

CG_EXTERN CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
  CG_AVAILABLE_STARTING(10.0.2.0);
Copy the code

The corresponding matrix is:? \begin{bmatrix} sx&0&0\ 0&sy&0\ 0&0&1\ \end{bmatrix}? So by coordinate transformation, we get x prime is equal to x times sx and y prime is equal to y times sy

Scale according to incoming Sx, SY, incoming negative values can be mirrored horizontally or vertically

3. The rotation

The initialization method is as follows:

/* Return a transform which rotates by `angle' radians: t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */

CG_EXTERN CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
  CG_AVAILABLE_STARTING(10.0.2.0);
Copy the code

Angle = x*Double. PI /180 Rotation is done around the center of the view. If Angle is positive, the view rotates clockwise around the center of the view

Assuming the coordinates of v are (x,y), the coordinates of v’ (x ‘,y ‘) can be derived (assuming the distance from the origin to V is R, and the Angle between the vector from the origin to v and the x axis is ϕ).

X = rcos ϕ, y = rsin ϕ

‘x = rcos (theta + ϕ), y’ = rsin (theta + ϕ)

I get it by trig expansion

‘x = rcos theta cos ϕ – rsin theta sin ϕ

Y ‘= rsin theta cos ϕ + rcos theta sin ϕ

Plug in the x and y expressions

‘x = xcos theta – ysin theta

Y ‘= xsin theta + ycos theta

Written in matrix form:

The transformation matrix is t prime in initialization

Here’s the question: why is the value of rotate passed in clockwise? The official documentation gives the answer: developer.apple.com/library/arc…

The Quartz 2D drawing model has two Spaces, user space and Device space. User space represents the document page to be drawn currently, and device space represents the device with the original resolution. Quartz 2D maps user space to device space using a current Transformation matrix (CTM). CTM is stored in the Graphics Context with an initial value of Identity matrix. This can be modified during drawing. The API for modifying the current CTM is CGContextRotateCTM CGContextScaleCTM CGContextTranslateCTM for rotation, scaling, translation. Rotate rotates with the origin as the center of the circle. The graph context created by Quartz rotates with the lower left corner as the center of the circle. Positive values Rotate counterclockwise and negative values Rotate clockwise. UIKit creates an image context with a rotation center at the top left corner and an Angle value that is clockwise for a positive value and counterclockwise for a negative value.

The concept of device space and user space can be understood as two pieces of paper. Device space is a piece of paper, fixed and motionless, representing the screen. User space is also a piece of paper, the actual drawing is drawn on the user space paper, but eventually needs to be pasted on the device space paper. How to paste is the problem described by CTM. I may shift the user space paper to some distance and paste it again, or I may enlarge and shrink the user space paper and paste it again, or I may rotate the Angle to paste it again. The user-space paper corresponds to each page in the drawing process. Different pages may use different user-space, that is, the CTM may be different each time when drawing.

2 d arbitrary rotation / 3 D rotation

Interested can look at the blog, write more detailed blog.csdn.net/csxiaoshui/…

Problem solving

Back to the original question, except for these answers:

  1. Use UIView(UILabel + UIImage)
  2. Use UIControl(UILabel + UIImage)
  3. Using UIButton, manually change the edgeinSet
  4. Using UIButton, manually change the frame
  5. .

Transform can also be used:

self.transform = CGAffineTransformMakeScale(1.0.1.0);
self.titleLabel.transform = CGAffineTransformMakeScale(1.0.1.0);
self.imageView.transform = CGAffineTransformMakeScale(1.0.1.0);
Copy the code

Isn’t it amazing