CGContextClipToMask不剪切

时间:2012-11-27 23:45:32

标签: iphone objective-c ios uikit core-graphics

我试图通过使用剪贴蒙版剪切路径的笔划在圆角矩形内绘制插入阴影,只留下阴影。最终目标是近似通知中心内容的插图外观。

我失败了。 CGContextClipToMask根本不起作用,这特别令人抓狂,因为我在项目的其他地方使用了相同的技术,并且在那里工作正常。这是我的绘图代码:

const CGFloat cornerRadius = 5.0f;
CGContextRef context = UIGraphicsGetCurrentContext();

// Draw background color
[[UIColor colorWithWhite:0.2 alpha:0.5] setFill];
UIBezierPath* background = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:cornerRadius];
[background fill];

// Draw the inner shadow
UIImage* mask;
UIGraphicsBeginImageContext(self.bounds.size);
{{
    CGContextRef igc = UIGraphicsGetCurrentContext();
    [[UIColor blackColor] setFill];
    CGContextFillRect(igc, self.bounds);

    // Inset the mask by a ridiculous degree just to be sure that it's working
    UIBezierPath* insetMask = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, 20, 20) cornerRadius:cornerRadius];
    [[UIColor whiteColor] setFill];
    [insetMask fill];

    mask = UIGraphicsGetImageFromCurrentImageContext();
}}
UIGraphicsEndImageContext();

CGContextSaveGState(context);
{{
    CGContextClipToMask(context, self.bounds, mask.CGImage);

    [[UIColor whiteColor] setStroke];
    CGContextSetShadow(context, CGSizeMake(0, 1), 1);
    [background stroke];
}}
CGContextRestoreGState(context);

如果我猜错了什么,我会说我在某种程度上违反了文档中规定的先决条件:

  

如果mask是图像,则它必须位于DeviceGray颜色空间中,可能没有alpha分量,并且可能不会被图像蒙版或蒙版颜色遮罩。

但是,我发现没有办法验证是否属于这种情况,更不用说纠正它了。

TIA可以提供任何帮助!

(我在运行iOS 6的iPhone 4S上运行该应用程序。)

1 个答案:

答案 0 :(得分:4)

您无法使用UIGraphicsBeginImageContext创建单通道图像。您必须使用CGBitmapContextCreate。您也没有使用UIGraphicsBeginImageContext代替UIGraphicsBeginImageContextWithOptions正确处理Retina。

让我向您展示一种更简单的方法来绘制内部阴影,不需要弄乱位图上下文和图像。当你有一个非自相交路径时,你可以用一个大矩形围绕它并使用偶数奇数填充规则来反转它。

这是一个例子。我做了InnerShadowView。这是drawRect:

- (void)drawRect:(CGRect)rect {
    [self fillIfNeeded];
    [self drawShadowIfNeeded];
}

fillIfNeeded方法只绘制实心圆角矩形:

- (void)fillIfNeeded {
    UIColor *color = self.fillColor;
    if (!color)
        return;

    [color setFill];
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds
        cornerRadius:self.cornerRadius];
    [path fill];
}

drawShadowIfNeeded方法绘制内部阴影。首先,我剪切到圆角矩形:

- (void)drawShadowIfNeeded {
    UIColor *color = self.shadowColor;
    if (!color)
        return;

    CGContextRef gc = UIGraphicsGetCurrentContext();
    CGContextSaveGState(gc); {
        [[UIBezierPath bezierPathWithRoundedRect:self.bounds
            cornerRadius:self.cornerRadius] addClip];

接下来,我构造一个圆角矩形的反转:一个包含除了圆角矩形之外的所有路径。我从一个非常大的矩形开始:

        UIBezierPath *invertedPath = [UIBezierPath bezierPathWithRect:CGRectInfinite];

然后我将圆角矩形附加到相同的路径:

        [invertedPath appendPath:[UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, -1, -1) cornerRadius:self.cornerRadius]];

请注意,我正在略微展开圆角矩形。当角半径非零时,这是必要的,以避免曲线周围出现一些瑕疵。

接下来,我将复合路径设置为使用偶数/奇数填充规则:

        invertedPath.usesEvenOddFillRule = YES;

通过使用偶数/奇数填充规则,我确保无限矩形和圆角矩形内的任何点都从填充区域排除,而圆角矩形外的任何点都是包括

现在,当我填充此反转路径时,它将尝试填充圆角矩形外的每个像素。它可以投射阴影的唯一地方是圆角矩形。因为我已经剪切到圆角矩形,它只会绘制阴影。

所以我设置了填充参数并填充了路径:

        CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(),
            self.shadowOffset, self.shadowBlur, color.CGColor);
        [[UIColor blackColor] setFill];
        [invertedPath fill];

清理:

    } CGContextRestoreGState(gc);
}

使用红色阴影颜色,它看起来像这样:

InnerShadowView screen shot

您可以找到所有代码in this gist以便于下载。