iphone - CGContextClipToMask 不剪裁

标签 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);

如果我要猜测出了什么问题,我会说我以某种方式违反了文档中规定的先决条件:

If mask is an image, then it must be in the DeviceGray color space, may not have an alpha component, and may not be masked by an image mask or masking color.

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

TIA,感谢您提供的任何帮助!

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

最佳答案

您不能使用 UIGraphicsBeginImageContext 创建单 channel 图像。您必须使用 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便于下载。

关于iphone - CGContextClipToMask 不剪裁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13595284/

相关文章:

iphone - 使用 NSString drawInRect 绘制旋转文本

iphone - 如何在iPhone越狱中阻止传入短信+代码

ios - NavigationTitle 与 subTitle 重叠

ios - 滚动停止动画 iOS

c# - Xamarin - 单击按钮时不断收到 NSInvalidArgumentException

iphone - 在 iPhone 应用内完成应用内购买后删除广告横幅

iphone - MKMapView 中带有谷歌地图的搜索栏建议

ios - 阻止 iOS 7 MKMapView 泄漏内存

ios - 将 CALayer 添加到 MKOverlayRenderer?

ios - 使用 ios 中的自定义委托(delegate)从其他 View Controller 更改一个 View Controller NSSdictonary 的值