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 view
cropView
, 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 viewcropView
Frame = 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