如何在Swift开发的macOS应用中检测窗口拖动并调整大小
macOS窗口拖动调整应用实现指导
需求概述
开发一款macOS应用,能够检测用户鼠标拖动操作,定位目标窗口后调整其大小。以下是现有初步实现代码:
现有代码实现
ActiveWindow 结构体(存储窗口信息)
struct ActiveWindow { let frame: CGRect let applicationName: String let windowId: Int init(frame: CGRect, applicationName: String, windowId: Int) { self.frame = frame.flippedScreenBounds self.applicationName = applicationName self.windowId = windowId } var isFocus: Bool { return applicationName.caseInsensitiveCompare("ReSize") == .orderedSame } }
WindowList 结构体(获取系统窗口列表)
struct WindowList { static func allWindows() -> [ActiveWindow] { let options = CGWindowListOption(arrayLiteral: .excludeDesktopElements, .optionOnScreenOnly) let windowsListInfo = CGWindowListCopyWindowInfo(options, CGMainDisplayID()) let infoList = windowsListInfo as! [[String: Any]] return infoList.filter { $0["kCGWindowLayer"] as! Int == 0 } .map { ActiveWindow( frame: CGRect(x: ($0["kCGWindowBounds"] as! [String: Any])["X"] as! CGFloat, y: ($0["kCGWindowBounds"] as! [String: Any])["Y"] as! CGFloat, width: ($0["kCGWindowBounds"] as! [String: Any])["Width"] as! CGFloat, height: ($0["kCGWindowBounds"] as! [String: Any])["Height"] as! CGFloat), applicationName: $0["kCGWindowOwnerName"] as! String, windowId: $0["kCGWindowNumber"] as! Int) } } static func window(at point: CGPoint) -> ActiveWindow? { let list = allWindows() return list.filter{ $0.frame.contains(point) }.first } }
ViewController 实现
class ViewController: NSViewController { private lazy var windowMain: NSWindow = self.view.window! private var highlightWindowController: HighlighWindowController? private var currentView: ActiveWindow? private var mouseLocation: NSPoint = NSEvent.mouseLocation { didSet { if let window = WindowList.window(at: mouseLocation), !window.isFocus { self.currentView = window highlight(window: window) } else { removeHighlight() } } } override func viewDidLoad() { super.viewDidLoad() registerMouseEvents() } private func registerMouseEvents() { NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) { self.mouseLocation = NSEvent.mouseLocation return $0 } NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved]) { _ in self.mouseLocation = NSEvent.mouseLocation } } // 假设你已实现以下高亮相关方法 private func highlight(window: ActiveWindow) { // 高亮窗口逻辑 } private func removeHighlight() { // 移除高亮逻辑 } }
拖动检测与窗口调整实现指导
1. 扩展鼠标事件监听,添加拖动相关事件
修改registerMouseEvents方法,补充鼠标按下、拖动、释放事件的监听:
private func registerMouseEvents() { // 原有鼠标移动监听 NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) { self.mouseLocation = NSEvent.mouseLocation return $0 } NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved]) { _ in self.mouseLocation = NSEvent.mouseLocation } // 添加拖动相关事件监听 NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDown]) { [weak self] event in self?.handleMouseDown(event: event) } NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseDragged]) { [weak self] event in self?.handleMouseDrag(event: event) } NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseUp]) { [weak self] event in self?.handleMouseUp(event: event) } }
2. 添加拖动状态跟踪变量
在ViewController中添加变量,用于跟踪拖动过程的关键状态:
private var isDragging = false private var dragStartPoint: CGPoint = .zero private var targetWindow: ActiveWindow?
3. 实现拖动事件处理方法
// 处理鼠标按下事件,确认目标窗口与拖动起始点 private func handleMouseDown(event: NSEvent) { let mousePoint = NSEvent.mouseLocation guard let window = WindowList.window(at: mousePoint), !window.isFocus else { return } // 判断点击位置是否在窗口边缘(可自定义边缘阈值,示例为10pt) let edgeThreshold: CGFloat = 10 let isOnEdge = (mousePoint.x <= window.frame.minX + edgeThreshold) || (mousePoint.x >= window.frame.maxX - edgeThreshold) || (mousePoint.y <= window.frame.minY + edgeThreshold) || (mousePoint.y >= window.frame.maxY - edgeThreshold) if isOnEdge { isDragging = true targetWindow = window dragStartPoint = mousePoint } } // 处理鼠标拖动事件,实时调整窗口大小 private func handleMouseDrag(event: NSEvent) { guard isDragging, let window = targetWindow else { return } let currentPoint = NSEvent.mouseLocation let deltaX = currentPoint.x - dragStartPoint.x let deltaY = currentPoint.y - dragStartPoint.y var newFrame = window.frame // 根据拖动边缘调整窗口大小(示例实现右侧、下侧拖动,可扩展其他边缘) // 右侧拖动:调整宽度 if currentPoint.x >= window.frame.maxX - 10 { newFrame.size.width += deltaX } // 下侧拖动:调整高度(同步偏移窗口原点,保持拖动位置对应) if currentPoint.y <= window.frame.minY + 10 { newFrame.size.height += deltaY newFrame.origin.y -= deltaY } // 应用新窗口尺寸 if let windowRef = CGWindowID(window.windowId) { CGWindowSetFrame(windowRef, newFrame, true) } // 更新起始点,确保后续拖动计算准确 dragStartPoint = currentPoint } // 处理鼠标释放事件,结束拖动状态 private func handleMouseUp(event: NSEvent) { isDragging = false targetWindow = nil }
4. 权限注意事项
macOS限制应用对其他窗口的操作,必须开启辅助功能权限:
- 打开系统偏好设置 → 安全性与隐私 → 隐私 → 辅助功能
- 勾选你的应用,允许其控制电脑
5. 优化建议
- 扩展边缘拖动方向判断,实现左上、右上、左下、右下等全方向的窗口大小调整
- 拖动时同步更新窗口高亮效果,强化用户反馈
- 添加窗口大小边界限制,避免窗口被拖至极小尺寸或超出屏幕范围
- 适配多显示器场景,确保坐标转换逻辑正确
内容的提问来源于stack exchange,提问作者Josué H.
相关产品推荐
相关产品推荐

