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

Swift 5.7开发macOS13应用:实现无光标阻塞的定点重复点击

解决macOS下不移动光标模拟定点点击的问题

你的核心问题在于原方案通过移动真实光标完成点击后再移回,导致系统光标同步跳动并干扰用户操作。以下是三种可行的解决方案,按推荐优先级排序:

方案1:针对目标窗口发送点击事件(公开API,无额外权限)

通过将点击事件直接发送到目标窗口的本地坐标,避免全局光标移动。适合能明确目标窗口的场景。

实现步骤:

  1. 获取目标窗口的CGWindowID(可通过进程名、窗口标题等定位)
  2. 将全局屏幕坐标转换为目标窗口的本地坐标
  3. 构造鼠标事件并发送到目标窗口
import CoreGraphics
import AppKit

// 根据进程名获取目标窗口ID
func getWindowID(forProcessName processName: String) -> CGWindowID? {
    let options: CGWindowListOption = [.optionOnScreenOnly, .optionAllApplications]
    guard let windowList = CGWindowListCopyWindowInfo(options, CGMainDisplayID()) as? [[String: Any]] else {
        return nil
    }
    
    for window in windowList {
        guard let ownerName = window[kCGWindowOwnerName as String] as? String,
              ownerName == processName,
              let windowID = window[kCGWindowNumber as String] as? CGWindowID else {
            continue
        }
        return windowID
    }
    return nil
}

// 将全局屏幕坐标转换为窗口本地坐标
func convertGlobalToWindowLocal(_ globalPoint: CGPoint, windowID: CGWindowID) -> CGPoint? {
    guard let windowImage = CGWindowListCreateImage(
        CGRectNull,
        .optionIncludingWindow,
        windowID,
        .imageBoundsOnly
    ) else {
        return nil
    }
    let windowBounds = CGImageGetBounds(windowImage)
    return CGPoint(
        x: globalPoint.x - windowBounds.origin.x,
        y: globalPoint.y - windowBounds.origin.y
    )
}

// 发送点击事件到目标窗口
func click(on globalLocation: CGPoint, windowID: CGWindowID) {
    guard let localLocation = convertGlobalToWindowLocal(globalLocation, windowID: windowID) else {
        return
    }
    
    let source = CGEventSource(stateID: .hidSystemState)
    // 构造鼠标按下事件
    let downEvent = CGEvent(
        mouseEventSource: source,
        mouseType: .leftMouseDown,
        mouseCursorPosition: localLocation,
        mouseButton: .left
    )
    downEvent?.postToWindow(windowID)
    
    // 构造鼠标抬起事件(添加微小延迟模拟真实点击)
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
        let upEvent = CGEvent(
            mouseEventSource: source,
            mouseType: .leftMouseUp,
            mouseCursorPosition: localLocation,
            mouseButton: .left
        )
        upEvent?.postToWindow(windowID)
    }
}

// 使用示例
guard let targetWindowID = getWindowID(forProcessName: "TargetApp") else {
    fatalError("无法找到目标窗口")
}

func performAutoClick(location: CGPoint) {
    click(on: location, windowID: targetWindowID)
}

方案2:使用Accessibility辅助功能API(公开API,需用户授权)

通过系统辅助功能模拟点击,无需移动光标,但需要用户在系统偏好设置中为你的应用开启辅助权限。适合需要上架App Store的场景。

import ApplicationServices
import AppKit

func clickWithAccessibility(at globalPoint: CGPoint) {
    let systemElement = AXUIElementCreateSystemWide()
    
    // 转换坐标:Accessibility坐标原点在屏幕左上角,macOS屏幕坐标原点在左下角
    let screenHeight = NSScreen.main?.frame.height ?? 0
    let accessibilityPoint = CGPoint(
        x: globalPoint.x,
        y: screenHeight - globalPoint.y
    )
    
    var axPoint = accessibilityPoint
    guard let axValue = AXValueCreate(.cgPoint, &axPoint) else {
        print("创建Accessibility坐标失败")
        return
    }
    
    let status = AXUIElementPerformAction(systemElement, kAXPressAction, axValue)
    if status != .success {
        print("辅助功能点击失败,错误码:\(status.rawValue)")
    }
}

// 使用示例
func performAutoClick(location: CGPoint) {
    clickWithAccessibility(at: location)
}

注意事项:

  • 首次运行时,系统会提示用户开启辅助权限,需引导用户前往「系统偏好设置 > 安全性与隐私 > 隐私 > 辅助功能」添加应用
  • 若需要重复点击,可通过Timer或DispatchQueue定时调用

方案3:使用私有API(无权限要求,无法上架App Store)

通过私有_CGPostMouseEvent函数直接发送点击事件,完全不影响真实光标,但该API属于私有接口,无法通过App Store审核,仅适合个人使用的自动化应用。

import CoreGraphics
import Foundation

// 导入私有API
@_silgen_name("_CGPostMouseEvent")
private func _CGPostMouseEvent(_ point: UnsafePointer<CGPoint>, _ button: UInt32, _ isDown: Bool, _ clickCount: Int32)

func clickWithPrivateAPI(at location: CGPoint) {
    // 发送鼠标按下事件
    _CGPostMouseEvent(&location, 0, true, 1)
    // 模拟真实点击的间隔(10ms)
    usleep(10000)
    // 发送鼠标抬起事件
    _CGPostMouseEvent(&location, 0, false, 1)
}

// 使用示例
func performAutoClick(location: CGPoint) {
    clickWithPrivateAPI(at: location)
}

注意事项:

  • 需要关闭Xcode中的「App Sandbox」功能(在「Signing & Capabilities」中移除App Sandbox)
  • 该API可能在未来macOS版本中被移除,兼容性需自行测试

内容的提问来源于stack exchange,提问作者J. Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:10:39