【发布时间】:2011-10-04 18:50:26
【问题描述】:
如何使用 Accessibility API 获取应用程序停靠图标的位置?
【问题讨论】:
标签: cocoa accessibility dock
如何使用 Accessibility API 获取应用程序停靠图标的位置?
【问题讨论】:
标签: cocoa accessibility dock
对于 Mac OS El Capitan,看起来您不应该使用 Accessibility API 来获取图标的位置。问题是图标不在应用程序的可访问性对象层次结构中——它可以在系统 Dock 应用程序的层次结构中找到。沙盒应用不应访问其他应用的辅助功能对象。
已批准答案中的代码不会在控制台中产生任何sandboxd 守护程序的警告,看起来它没有违反任何规则。它使用函数AXUIElementCreateApplication 创建顶级可访问性对象。文档指出,它:
Creates and returns the top-level accessibility object for the
application with the specified process ID.
不幸的是,这个顶级对象不是 Dock 图标的祖先。 我尝试运行代码,它会计算第一个应用程序主菜单项的位置(与应用程序本身具有相同的标题)。比较发生在这一行:
if ([titleValue isEqual:appName]) {
所以我的应用程序的输出总是相同的:
position: (45.000000, 0.000000)
尝试访问其他应用的辅助功能对象时在控制台中产生了警告。我想必须找到另一种计算图标位置的方法。
【讨论】:
找到了!使用this forum post 作为参考,我能够将给定的示例代码塑造成我需要的样子:
- (NSArray *)subelementsFromElement:(AXUIElementRef)element forAttribute:(NSString *)attribute
{
NSArray *subElements = nil;
CFIndex count = 0;
AXError result;
result = AXUIElementGetAttributeValueCount(element, (CFStringRef)attribute, &count);
if (result != kAXErrorSuccess) return nil;
result = AXUIElementCopyAttributeValues(element, (CFStringRef)attribute, 0, count, (CFArrayRef *)&subElements);
if (result != kAXErrorSuccess) return nil;
return [subElements autorelease];
}
- (AXUIElementRef)appDockIconByName:(NSString *)appName
{
AXUIElementRef appElement = NULL;
appElement = AXUIElementCreateApplication([[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"] lastObject] processIdentifier]);
if (appElement != NULL)
{
AXUIElementRef firstChild = (__bridge AXUIElementRef)[[self subelementsFromElement:appElement forAttribute:@"AXChildren"] objectAtIndex:0];
NSArray *children = [self subelementsFromElement:firstChild forAttribute:@"AXChildren"];
NSEnumerator *e = [children objectEnumerator];
AXUIElementRef axElement;
while (axElement = (__bridge AXUIElementRef)[e nextObject])
{
CFTypeRef value;
id titleValue;
AXError result = AXUIElementCopyAttributeValue(axElement, kAXTitleAttribute, &value);
if (result == kAXErrorSuccess)
{
if (AXValueGetType(value) != kAXValueIllegalType)
titleValue = [NSValue valueWithPointer:value];
else
titleValue = (__bridge id)value; // assume toll-free bridging
if ([titleValue isEqual:appName]) {
return axElement;
}
}
}
}
return nil;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
AXUIElementRef dockIcon = [self appDockIconByName:@"MYAPPNAME"];
if (dockIcon) {
CFTypeRef value;
CGPoint iconPosition;
AXError result = AXUIElementCopyAttributeValue(dockIcon, kAXPositionAttribute, &value);
if (result == kAXErrorSuccess)
{
if (AXValueGetValue(value, kAXValueCGPointType, &iconPosition)) {
NSLog(@"position: (%f, %f)", iconPosition.x, iconPosition.y);
}
}
}
}
【讨论】:
[[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"] lastObject] processIdentifier]