2

首先,从 SO 的其他地方查看这个非常方便的CALayer扩展。它可以帮助您确定图层内容分配的CGImageRef中的点是否中的某个点是否透明。

注意:不能保证图层contents可以表示或响应,就好像它是CGImageRef 一样。(这可能对更广泛地使用上面提到的扩展产生影响,授予。)但是,在我的情况下,我知道我正在测试的层contents被分配了一个CGImageRef。(希望这不会在我分配后改变!另外我注意到它contents被保留了。)

好的,回到手头的问题。这是我使用扩展的方式。对于初学者,我已将选择器从更改containsPoint:containsNonTransparentPoint:(我需要保留原始方法。)

现在,我有一个使用七个CALayer对象的UIImageView子类。这些用于基于不透明度的动画(脉冲/发光效果和开/关状态)。这七层中的每一层都有一个已知的CGImageRef,它有效地“覆盖”(空中引号)整个视图的一部分,并带有自己的颜色。其各自图层中的每个图像的其余部分都是透明的。contents

在子类中,我注册了单击手势。当一个到达时,我遍历我的层以查看哪个被有效地点击(也就是说,哪个有一个我点击的不透明点,第一个找到获胜)然后我可以做任何需要做的事情。

这是我处理手势的方式:

- (IBAction)handleSingleTap:(UIGestureRecognizer *)sender {
    CGPoint tapPoint = [sender locationInView:sender.view];

    // Flip y so 0,0 is at lower left. (Required by layer method below.)
    tapPoint.y = sender.view.bounds.size.height - tapPoint.y;

    // Figure out which layer was effectively tapped. First match wins.
    for (CALayer *layer in myLayers) {
        if ([layer containsNonTransparentPoint:tapPoint]) {
            NSLog(@"%@ tapped at (%.0f, %.0f)", layer.name, tapPoint.x, tapPoint.y);

            // We got our layer! Do something useful with it.
            return;
        }
    }
}

好消息?所有这些都可以在 iOS 4.3.2 的 iPhone 模拟器上完美运行。(FWIW,我在 Lion 上运行 Xcode 4.1。)

然而,在我的 iPhone 4(使用 iOS 4.3.3)上,它甚至没有接近!我的水龙头似乎都没有与我期望它们匹配的任何层相匹配。

即使我尝试使用CGContextSetBlendMode的建议在绘制 1x1 像素上下文时

我希望这是飞行员的错误,但我还没有弄清楚差异是什么。水龙头确实有图案,但不是可辨别的。

也许存在数据边界问题。也许除了将 y 坐标翻转到图像的左下角之外,我还需要做一些事情。只是还不确定。

如果有人可以请阐明可能有什么问题,我将不胜感激!

2011 年 9 月 22 日更新:获得第一个啊哈时刻!问题不在于 Simulator-vs-iPhone。这是视网膜与非视网膜!使用 Retina 版本时,模拟器中也会出现相同的症状。也许解决方案以某种方式/形状/形式围绕缩放(CTM?)。Quartz 2D Programming Guide 还建议“iOS 应用程序应该使用UIGraphicsBeginImageContextWithOptions ”。我觉得我非常接近这里的解决方案!

4

1 回答 1

1

好的!首先,问题不在于 Simulator-vs-iPhone。相反,这是视网膜与非视网膜。使用 Retina 版本时,模拟器中也会出现相同的症状。马上,人们开始认为解决方案与扩展有关。

Apple Dev Quartz 2D 论坛上的一篇非常有用的帖子(沿着类似的“注意缩放”行)引导我找到解决方案。现在,我首先要承认,这个解决方案并不漂亮,但它确实适用于 Retina 和非 Retina 案例。

有了这个,这是前面提到的CALayer 扩展的修改代码:

//
// Checks image at a point (and at a particular scale factor) for transparency.
// Point must be with origin at lower-left.
//
BOOL ImagePointIsTransparent(CGImageRef image, CGFloat scale, CGPoint point) {
    unsigned char pixel[1] = {0};

    CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 1,
        NULL, kCGImageAlphaOnly);
    CGContextSetBlendMode(context, kCGBlendModeCopy);
    CGContextDrawImage(context, CGRectMake(-point.x, -point.y,
        CGImageGetWidth(image)/scale, CGImageGetHeight(image)/scale), image);

    CGContextRelease(context);
    CGFloat alpha = pixel[0]/255.0;
    return (alpha < 0.01);
}

@implementation CALayer (Extensions)

- (BOOL)containsNonTransparentPoint:(CGPoint)point scale:(CGFloat)scale {
    if (CGRectContainsPoint(self.bounds, point)) {
        if (!ImagePointIsTransparent((CGImageRef)self.contents, scale, point))
            return YES;
    }
    return NO;
}

@end

简而言之,我们需要了解规模。如果我们将图像宽度和高度除以该比例,ta-dah,命中测试现在可以在 Retina 和非 Retina 设备上运行!

我不喜欢的是我不得不把那个糟糕的选择器弄得一团糟,现在叫做containsNonTransparentPoint:Scale:。如问题中所述,永远无法保证图层的内容将包含什么。在我的情况下,我注意只在其中有 CGImageRef 的图层上使用它,但这不会在更通用/可重用的情况下飞行。

所有这一切让我想知道 CALayer 是否毕竟不是这个特定扩展的最佳位置,至少在这个新的化身中是这样。也许 CGImage 加上一些层级的智能,会更干净。想象一下对 CGImage 进行命中测试,但返回当时具有非透明内容的第一层的名称。仍然存在不知道哪些层中有 CGImageRefs 的问题,因此可能需要一些提示。(留给你和读者的练习!)

更新:在与 Apple 的开发人员讨论后,以这种方式弄乱层实际上是不明智的。与我之前学到的相反(不正确?),封装在一个 UIView 中的多个 UIImageView 是这里的方法。(我总是记得得知您希望将自己的观点保持在最低限度。也许在这种情况下,这没什么大不了的。)不过,我暂时将这个答案保留在这里,但不会将其标记为正确。一旦我尝试并验证了其他技术,我将在这里分享!

于 2011-09-22T17:20:26.960 回答