0

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

我失败了。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 上运行该应用程序。)

4

1 回答 1

4

您不能使用UIGraphicsBeginImageContext. 你必须使用CGBitmapContextCreate. 您也没有通过使用UIGraphicsBeginImageContext而不是正确处理 Retina UIGraphicsBeginImageContextWithOptions

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

这是一个例子。我做了一个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 屏幕截图

您可以在此 gist中找到所有代码,以便于下载。

于 2012-11-28T04:25:43.633 回答