Swift 5.7开发macOS13应用:实现无光标阻塞的定点重复点击
解决macOS下不移动光标模拟定点点击的问题
你的核心问题在于原方案通过移动真实光标完成点击后再移回,导致系统光标同步跳动并干扰用户操作。以下是三种可行的解决方案,按推荐优先级排序:
方案1:针对目标窗口发送点击事件(公开API,无额外权限)
通过将点击事件直接发送到目标窗口的本地坐标,避免全局光标移动。适合能明确目标窗口的场景。
实现步骤:
- 获取目标窗口的
CGWindowID(可通过进程名、窗口标题等定位) - 将全局屏幕坐标转换为目标窗口的本地坐标
- 构造鼠标事件并发送到目标窗口
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
相关产品推荐
相关产品推荐

