目标:实现一个可以拖动选取矩形区域的控件
原理:拖动手势及贝塞尔曲线
代码如下:
import UIKit
/// 可拖动选择一个长方形区域的视图
public class HZG_AreaSelectView: UIView {
public var leftTopX: CGFloat = 10
public var leftTopY: CGFloat = 10
public var areaWidth: CGFloat = 100
public var areaHeight: CGFloat = 100
public var lineColor: UIColor = .blue
// 粗线宽度
public var thickLineWidth: CGFloat = 5.0
// 粗线长度
public var thickLineLength: CGFloat = 20.0
// 细线宽度
public var fineLineWidth: CGFloat = 2.0
// 端头长度
public var endLength: CGFloat = 5.0
public var passPositionCallback: ((_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> Void)?
public override init(frame: CGRect) {
super.init(frame: frame)
self.initSettings()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.initSettings()
}
func initSettings() -> Void {
let panGes = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture))
self.addGestureRecognizer(panGes)
}
@objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
// 获取手势在视图上的拖动距离
let translation = recognizer.translation(in: self)
// 将视图的中心点根据手势的拖动距离进行调整
if recognizer.view != nil {
// 根据拖动点的位置重绘区域视图
let location = recognizer.location(in: self)
if (location.x > self.leftTopX - 20.0) &&
(location.x < self.leftTopX + 20.0) &&
(location.y > self.leftTopY - 20.0) &&
(location.y < self.leftTopY + 20.0) {
// 左上角
self.leftTopX = self.leftTopX + translation.x
self.leftTopY = self.leftTopY + translation.y
self.areaWidth = self.areaWidth - translation.x
self.areaHeight = self.areaHeight - translation.y
} else if (location.x > self.leftTopX + self.areaWidth - 20.0) &&
(location.x < self.leftTopX + self.areaWidth + 20.0) &&
(location.y > self.leftTopY - 20.0) &&
(location.y < self.leftTopY + 20.0) {
// 右上角
self.leftTopY = self.leftTopY + translation.y
self.areaWidth = self.areaWidth + translation.x
self.areaHeight = self.areaHeight - translation.y
} else if (location.x > self.leftTopX + self.areaWidth - 20.0) &&
(location.x < self.leftTopX + self.areaWidth + 20.0) &&
(location.y > self.leftTopY + self.areaHeight - 20.0) &&
(location.y < self.leftTopY + self.areaHeight + 20.0){
// 右下角
self.areaWidth = self.areaWidth + translation.x
self.areaHeight = self.areaHeight + translation.y
} else if (location.x > self.leftTopX - 20.0) &&
(location.x < self.leftTopX + 20.0) &&
(location.y > self.leftTopY + self.areaHeight - 20.0) &&
(location.y < self.leftTopY + self.areaHeight + 20.0) {
// 左下角
self.leftTopX = self.leftTopX + translation.x
self.areaWidth = self.areaWidth - translation.x
self.areaHeight = self.areaHeight + translation.y
} else if (location.x > self.leftTopX + 20.0) &&
(location.x < self.leftTopX + self.areaWidth - 20.0) &&
(location.y > self.leftTopY + 20.0) &&
(location.y < self.leftTopY + self.areaHeight - 20.0) {
// 中心区域,移动
self.leftTopX = self.leftTopX + translation.x
self.leftTopY = self.leftTopY + translation.y
}
}
self.setNeedsDisplay()
self.passPositionCallback?(self.leftTopX, self.leftTopY, self.areaWidth, self.areaHeight)
// 重置手势的拖动距离
recognizer.setTranslation(CGPoint.zero, in: self)
}
public override func draw(_ rect: CGRect) {
// 根据坐标和宽高,绘制区域
let context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(self.lineColor.cgColor)
// 绘制顶端横线
self.drawHorizontalLine(context: context, y: self.leftTopY)
// 绘制右端竖线
let rightX = self.leftTopX + self.areaWidth
self.drawVerticalLine(context: context, x: rightX)
// 绘制底端横线
let bottomY = self.leftTopY + self.areaHeight
self.drawHorizontalLine(context: context, y: bottomY)
// 绘制左端竖线
let leftX = self.leftTopX
self.drawVerticalLine(context: context, x: leftX)
}
func drawHorizontalLine(context: CGContext?, y: CGFloat) -> Void {
// 左端粗线端
context?.setLineWidth(self.thickLineWidth)
context?.move(to: CGPoint(x: self.leftTopX - self.endLength, y: y))
context?.addLine(to: CGPoint(x: self.leftTopX + self.thickLineLength - self.endLength, y: y))
context?.closePath()
context?.strokePath()
// 中间细线
context?.setLineWidth(self.fineLineWidth)
context?.move(to: CGPoint(x: self.leftTopX - self.endLength + self.thickLineLength, y: y))
context?.addLine(to: CGPoint(x: self.leftTopX + self.areaWidth - self.thickLineLength + self.endLength, y: y))
context?.closePath()
context?.strokePath()
// 右端粗线
context?.setLineWidth(self.thickLineWidth)
context?.move(to: CGPoint(x: self.leftTopX + self.areaWidth - self.thickLineLength + self.endLength, y: y))
context?.addLine(to: CGPoint(x: self.leftTopX + self.areaWidth + self.endLength, y: y))
context?.closePath()
context?.strokePath()
}
func drawVerticalLine(context: CGContext?, x: CGFloat) -> Void {
context?.setLineWidth(self.thickLineWidth)
context?.move(to: CGPoint(x: x, y: self.leftTopY - self.endLength))
context?.addLine(to: CGPoint(x: x, y: self.leftTopY + self.thickLineLength - self.endLength))
context?.closePath()
context?.strokePath()
// 中间细线
context?.setLineWidth(self.fineLineWidth)
context?.move(to: CGPoint(x: x, y: self.leftTopY + self.thickLineLength - self.endLength))
context?.addLine(to: CGPoint(x: x, y: self.leftTopY + self.areaHeight - self.thickLineLength + self.endLength))
context?.closePath()
context?.strokePath()
// 底端粗线
context?.setLineWidth(self.thickLineWidth)
context?.move(to: CGPoint(x: x, y: self.leftTopY + self.areaHeight - self.thickLineLength + self.endLength))
context?.addLine(to: CGPoint(x: x, y: self.leftTopY + self.areaHeight + self.endLength))
context?.closePath()
context?.strokePath()
}
}