Preface:

I wrote two related papers before,

Low imitation scan all-powerful king selection area function

The selection area function of imitation scan almighty king: take pictures, rotate

The idea is to draw directly from touch related gestures, recognition points

Inconvenient, simply add animation

This article introduces another idea through controls

Any reference RZMN/CropView

The specific thinking

1, get the point, same three methods,

touchesBegan, touchesMoved, touchesEnded

2. View hierarchy
  • The functional viewcropView, added to the image View,

In the function view cropView, recognize gestures

  • Draw a line, four sides,

var areaQuadrangle = SEAreaView()

Add to the function view cropView

2.1, easy to do animation, four corners are controls, added to the functional view cropView

class CornerView: UIView

Drag a corner to make the circle in that corner bigger.

Let go of that corner, and the circle in that corner retracts

3. Coordinates

ImageView is sizeToFit,

By default, the size of the imageView is greater than the size of the image seen above

(Of course, image.size is very large)

Function view cropView frame = frame of image seen above

So draw lines, four sides, var areaQuadrangle = Frame of SEAreaView() = function view’s bounds of cropView

Var cornerLocations: [CGPoint]?

This is the first time that the external is passed in, or the external needs to modify the incoming,

He was also identified by the gesture ‘touchesMoved’

implementation

configuration

Func configure(corners imageView: UIImageView) {self.imageView = imageView self.imageView? . IsUserInteractionEnabled = true imageView. AddSubview (self) / / initialization for subview in subviews {if subview is SECornerView {subview. RemoveFromSuperview ()}} / / configure four angles for _ in 0.. <Setting.std.cornerCount { let corner = SECornerView(frame: CGRect(x: 0, y: 0, width: Setting.std.cornerSize, height: Setting. The STD. CornerSize) addSubview (corner) cornerViews. Append (corner)} / / configure four sides areaQuadrangle backgroundColor = .clear addSubview(areaQuadrangle) }Copy the code

The functional viewcropViewFrame = frame of the image seen above

The first time you come in, do that

Because it’s usually configured in viewDidLoad, and at this point, the frame of the imageView isn’t set yet

The appropriate time to implement the control internally is when func layoutSubviews() needs to go once

public override func layoutSubviews() { super.layoutSubviews() guard first else { return } if let imgsize = imageView? .image? .size, let imageBounds = imageView? .bounds { let f = AVMakeRect(aspectRatio: imgsize, insideRect: ImageBounds) frame = f} first = false Let f = bounds let first = f bounds in let rhsTop = CGPoint(x: first.x + f bounds, y: first.y) let lhsHip = CGPoint(x: first.x, y: first.y + f.height) let end = CGPoint(x: rhsTop.x, y: lhsHip.y) let dots = [first, rhsTop, end, lhsHip] self.cornerLocations = dots areaQuadrangle.frame = bounds update(scale: nil) cornerOnTouch = nil }Copy the code

So AVMakeRect is a good way to do that, because you just figure out the size of the image sizeToFit inside the image view

Gesture recognition

Of course, there are at least two other gestures

override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) guard let touchIdx = cornerOnTouch, touches.count == 1, let first = touches.first, let cornerLocations = cornerLocations, let img = imageView? // select cornerLocations[touchIdx] from cornerLocations[touchIdx]; // select cornerLocations[touchIdx] from cornerLocations[touchIdx]; Let from = first.previousLocation(in: self) let to = first.location(in: self) let Derivative = CGPoint(x: self) to.x - from.x, y: to.y - from.y) let rawPt = CGPoint(x: cornerLocations[touchIdx].x + derivative.x, y: CornerLocations [touchIdx].y + derivative. Y) let newCenterOnImage = rawPt. Normalized (size: Img. Size) self.cornerlocations? [touchIdx] = newCenterOnImage update(scale: nil)}Copy the code

The effect

func update(scale isBigger: Bool?) {guard let touchIdx = cornerOnTouch else {return} pairPositionsAndViews() if let bigger = isBigger{guard let touchIdx = cornerOnTouch else {return} Switch bigger {case true: // corner circle, animation zoom cornerViews[touchIdx]. ScaleUp () case false: CornerViews [touchIdx].scaleDown()}} for corner in cornerViews {corner.layer.borderColor = (isPathValid? Setting.std.goodAreaColor : Setting.std.badAreaColor).cgColor } }Copy the code

Update location

Func pairPositionsAndViews() {// control update position, is to change the center of the position if let cornerPositions = self.cornerLocations {for I in 0.. < Setting.std.cornerCount { self.cornerViews[i].center = CGPoint(x: cornerPositions[i].x, y: CornerPositions [I].y)}} self.areaquadrangle.fill (path: path)}Copy the code
animation

Control animation, simple

Class CornerView: UIView {// zoom animation func scaleUp() {uiView.animate (withDuration: 0.15, animations: {self. Layer. BorderWidth = 0.5 self. Transform = CGAffineTransform. Identity. ScaledBy (x: 2, y: 2)})} / / shrink animation similar}Copy the code

Cross reconnection

If a point drags over the opposite side, it causes a cross

If it’s a convex quadrilateral, reconnect it

The trigger for this logic is’ touchesEnded.

override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) guard cornerOnTouch ! SortPointClockwise () // Initialize update(scale: false) cornerOnTouch = nil}Copy the code

Cross-linking, as explained in the first blog, is omitted here

github repo