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 色空間にある必要があり、アルファ コンポーネントを持たない可能性があり、画像マスクまたはマスキング カラーによってマスクされない可能性があります。

ただし、これが事実であるかどうかを確認する方法はなく、そうである場合は修正することはできません。

あなたが提供できるどんな助けのためのTIA!

(iOS 6 を実行している iPhone 4S でアプリを実行しています。)

4

1 に答える 1

4

を使用して単一チャンネルの画像を作成することはできませんUIGraphicsBeginImageContext。を使用する必要がありますCGBitmapContextCreateUIGraphicsBeginImageContextまた、代わりに を使用して 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);
}

赤い影の色を使用すると、次のようになります。

インナーシャドウビューのスクリーンショット

簡単にダウンロードできるように、すべてのコードはこの要点にあります。

于 2012-11-28T04:25:43.633 に答える