You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:50:25