.corner
)则找到该点在lastRect
上中心对称的点作为定点,鼠标位置作为动点;enum DotType {
case corner // 四个角的点
case top // 上边中心对应的点
case bottom // 下边中心对应的点
case right // 右边中心对应的点
case left // 左边中心对应的点
case none // 无类型标识点,当没有标识点被选到时的默认值
}
class ClipView: NSView {
var image: NSImage?
var drawingRect: NSRect?
var showDots = false // 是否显示标识点
var paths: [(NSBezierPath, DotType)] = [] // 记录标识点的path和对应的类型
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
guard let image = self.image else {
return
}
var rect = NSIntersectionRect(self.drawingRect!, self.bounds)
rect = NSIntegralRect(rect)
image.draw(in: rect, from: rect, operation: .sourceOver, fraction: 1.0)
// 根据当前drawingRect绘制标识点
if self.showDots {
self.drawDots(rect: rect)
}
}
private func drawDots(rect: NSRect) {
let radius: CGFloat = 1.5 // 标识点半径
let dots = self.getDotsCoord(with: rect, radius: radius) // 获取八个点的坐标
self.paths = []
for (dot, type) in dots {
let path = NSBezierPath(ovalIn: NSRect(origin: dot, size: CGSize(width: radius*2, height: radius*2)))
self.paths.append((path, type))
path.lineWidth = 3
NSColor.white.set()
path.stroke()
}
}
func getDotsCoord(with rect:NSRect, radius: CGFloat) -> [(NSPoint, DotType)] {
let width = rect.width
let height = rect.height
// 为了让标识点的中心落在正确的位置,根据半径提前偏移rect
// 计算出来的位置是标识点圆圈对应矩阵的左下角(起始坐标)
let origin = rect.offsetBy(dx: -radius, dy: -radius).origin
let dots: [(NSPoint, DotType)] = [
(origin, .corner),
(origin.offsetBy(dx: width/2, dy: 0), .bottom),
(origin.offsetBy(dx: 0, dy: height/2), .left),
(origin.offsetBy(dx: width, dy: 0), .corner),
(origin.offsetBy(dx: 0, dy: height), .corner),
(origin.offsetBy(dx: width, dy: height/2), .right),
(origin.offsetBy(dx: width/2, dy: height), .top),
(origin.offsetBy(dx: width, dy: height), .corner)
].map({
(point, type) in
// 取整
return (NSPoint(x: Int(point.x), y: Int(point.y)), type)
})
return dots
}
}
在Controller中记录当前选择的标识点,并在mouseDown中添加对adjust or drag的判断
class ClipWindowController: NSWindowController {
var clipView: ClipView?
var screenImage: NSImage?
var lastRect: NSRect?
var highlightRect: NSRect?
var startPoint: NSPoint?
var lastPoint: NSPoint?
var selectDotType: DotType = .none // 当前选择标识点的类型,默认为none,可以用于判断是否有选择标识点
var selectDot: NSPoint = .zero // 当前选择的标识点,默认为zero
// other functions ....
override func mouseDown(with event: NSEvent) {
let location = event.locationInWindow
switch ClipManager.shared.status {
case .ready:
// ...
case .select:
guard let rect = self.highlightRect,
let view = self.clipView,
rect.insetBy(dx: -5, dy: -5).contains(location) // 适当放宽点击判定区域
else { return }
for (path, type) in view.paths {
// 适当放宽点击判定区域
if path.bounds.insetBy(dx: -5, dy: -5).contains(location) {
self.selectDotType = type
self.selectDot = path.bounds.center()
break
}
}
if self.selectDotType != .none {
ClipManager.shared.status = .adjust
} else {
ClipManager.shared.status = .drag
}
self.lastPoint = location
default:
return
}
}
override func mouseUp(with event: NSEvent) {
switch ClipManager.shared.status {
case .start:
// ...
case .drag, .adjust:
guard let rect = self.highlightRect else { return }
self.startPoint = rect.origin
self.selectDotType = .none
self.lastRect = rect
ClipManager.shared.status = .select
default:
return
}
}
override func mouseDragged(with event: NSEvent) {
let location = event.locationInWindow
switch ClipManager.shared.status {
case .start:
// ....
case .adjust:
guard let lastRect = self.lastRect,
self.selectDotType != .none
else { break }
let dx = location.x - self.selectDot.x
let dy = location.y - self.selectDot.y
var rect: NSRect = .zero
switch self.selectDotType {
case .corner:
let symPoint = lastRect.symmetricalPoint(point: self.selectDot)
rect = RectUtil.getRect(aPoint: symPoint, bPoint: location)
case .top:
rect = RectUtil.getRect(
aPoint: lastRect.origin,
bPoint: self.selectDot.offsetBy(dx: lastRect.width/2, dy: dy)
)
case .bottom:
rect = RectUtil.getRect(
aPoint: lastRect.origin.offsetBy(dx: 0, dy: lastRect.height),
bPoint: self.selectDot.offsetBy(dx: lastRect.width/2, dy: dy)
)
case .left:
rect = RectUtil.getRect(
aPoint: lastRect.origin.offsetBy(dx: dx, dy: 0),
bPoint: self.selectDot.offsetBy(dx: lastRect.width, dy: lastRect.height/2)
)
case .right:
rect = RectUtil.getRect(
aPoint: lastRect.origin,
bPoint: self.selectDot.offsetBy(dx: dx, dy: lastRect.height/2)
)
default:
break
}
self.highlightRect = rect
self.lastPoint = location
self.startPoint = rect.origin
self.highlight()
case .drag:
// ...
default:
break
}
}
}
其中symmetricalPoint
是获取rect中某个点关于中心对称的点:
extension NSRect {
func center() -> NSPoint{
let origin = self.origin
return NSPoint(x: origin.x+self.width/2, y: origin.y+self.height/2)
}
func symmetricalPoint(point: NSPoint) -> NSPoint {
let center = self.center()
// 转换为整数,放置抖动
return NSPoint(x: Int(2*center.x-point.x), y: Int(2*center.y-point.y))
}
}
单屏幕Pin实现 ➡️