iOS Transform coordinate changes

When using CGContext, the coordinates of Quartz 2D and UIKit are inconsistent, so the context needs to be changed again to achieve the desired effect.

1. Introduction to the origin of different coordinates
In Quartz 2D, the origin of coordinates is at the lower left corner of the canvas, while in UIKit, it is consistent with the screen coordinates, and the upper left corner is the origin of coordinates.

If you draw F with the (0, 0) point as the origin, the following results will be obtained in different coordinate systems.
image.png
2. Quartz 2D and UIKit coordinate system conversion

2.1 UIImage drawing

In iOS UI development, take UIImage as an example, draw a picture, set the frame of the image to (0, 0, 320, 320), and you will get the picture on the right.

If you use the following code to read the transform of the Context, you can see that this transform is not an identity matrix.

CGRect frame = CGRectMake(0.0, 0.0, 720, 1280);
UIGraphicsBeginImageContext(frame.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform contextTransform = CGContextGetCTM(context);;

The transform setting here will make the coordinate origin of Quartz 2D and UIKit coincide, which also facilitates the drawing of controls in UIKit. For coordinate system changes, please refer to the figure below.
image.png
This is why if you call CGContextDrawImage directly on the acquired context, you will get a reversed image.

但如果使用UIImage的drawInRect方法,文档是这么写的:
Instance Method
draw(in:)
Draws the entire image in the specified rectangle, scaling it as necessary to fit.
Declaration
func draw(in rect: CGRect)
Parameters
rect
The rectangle (in the coordinate system of the graphics context) in which to draw the image.
Discussion
This method draws the entire image in the current graphics context, respecting the image’s orientation setting. In the default coordinate system, images are situated down and to the right of the origin of the specified rectangle. This method respects any transforms applied to the current graphics context, however.
This method draws the image at full opacity using the CGBlendMode.normal blend mode.

In other words, the coordinates used when UIImage is drawn are still UIKit's internal coordinates, so there is no need to make any changes to the coordinate system, and you can draw a picture with the same position as the rect. Of course, this method will also draw according to the orientation of the picture.

2.2 CGContextDrawImage drawing

When changing the transform of the context, what is actually changing is the coordinate system itself.

When calling the drawing method, the internal coordinates of the coordinate system are used. Therefore, when we want to draw a picture displayed by UIKit based on the acquired context, we also need to adjust the coordinate system before drawing.

//Y-axis flip
CGContextScaleCTM(context, 1, -1);

//The origin of the image needs to be aligned with the upper left corner, and the height of the image needs to be translated down on the Y axis
CGContextTranslateCTM(context, 0, -imageSize.height);

//Draw a picture
CGContextDrawImage(context, frame, image.CGImage);
image.png

3 Transform anchor point changes For
example, on the picture editing page, we can often encounter changes such as zooming, rotating, and shifting the picture using gestures, and then generate a new picture.

According to different gesture callbacks, we can modify view.transform to make the view on the interface change corresponding to the gesture.

Here, in order to facilitate us to modify the UI interface of UIKit, the transform of the view is anchored with the center of the view.

The description in UIView's transform is to use center to modify the position.

Use this property to scale or rotate the view’s frame rectangle within its superview’s coordinate system. (To change the position of the view, modify the center property instead.) The default value of this property is CGAffineTransformIdentity.

When many other methods are called, the transform needs to use the upper left corner as the anchor point, so there needs to be a transformation here. The anchor point affects the following figure.
image.png
In the UI interface modification, we can use the callback values ​​of zoom and rotation gestures to directly modify the view's transform, and the displacement callback to modify the center, and we can achieve the desired effect. But this transform cannot be used for context drawing, because the coordinate system is changed with the origin as the anchor point.

Therefore, for the position of the existing coordinate system of the context, the anchor point is in the upper left corner, and a transform modification is required.

According to the figure above, it can be seen that the anchor point only affects the position information, and does not change the zoom and rotation.

UIImage *image; //Initialize the image

UIView *view;//Apply the changed view, and the size of the view must be consistent with the image to ensure that the zoom ratio is correct.

CGAffineTransform transform = view.transform;
CGSize imageSize = image.size;
transform.tx = view.center.x;
transform.ty = view.center.y;
transform = CGAffineTransformTranslate(transform, -imageSize.width * 0.5, -imageSize.height * 0.5);

Where tx, ty are the position of the anchor point in the coordinate system

The current anchor point is at the center of the view. We need to change it to the upper left corner of the view so that it can coincide with the origin of the coordinate system. Where *(imageSize.width * 0.5, imageSize.height * 0.5)* is the position of the anchor point in the image, and transform is the change matrix when the anchor point is in the upper left corner of the view.

4. Combine Transform
to get the result in the middle of the above figure on CGContext, not only need to apply the change of reduction by 1/2 and rotation of 45 degrees, but also need to adjust.

I said before that the CGContext application rotation is applied to the coordinate system, which is consistent with the view application rotation in its own coordinate system. Therefore, when the optimized coordinate system of the directly obtained CGContext is also the origin at the upper left corner, we can directly apply the calculated transform on the CGContext.

Afterwards, because the coordinate system is drawn based on its own coordinate system, do a round of inversion and displacement of the coordinate system to get the final result, similar to the operation in the figure below.

image.png

It should be noted here that each new change is based on the previous change, so there is an order for both the view and the transformation of the context. This is consistent with matrix multiplication, and the order will affect the result.

Guess you like

Origin blog.csdn.net/weixin_41191739/article/details/109722369