masksToBounds = YESがCALayerシャドウを防ぐのはなぜですか?


83

次のスニペットを使用して、UIViewの1つにドロップシャドウ効果を追加しています。これはかなりうまく機能します。しかし、ビューのmasksToBoundsプロパティをYESに設定するとすぐに。ドロップシャドウ効果は表示されなくなりました。

self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;

これについて何かアイデアはありますか?

回答:


166

シャドウはビューの外部で実行される効果であり、masksToBoundsをYESに設定すると、UIViewにそれ自体の外部にあるものを描画しないように指示するためです。

影付きのroundedCornerビューが必要な場合は、2つのビューで実行することをお勧めします。

UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];

view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];

9
くそー!それは理にかなっている!
jchatard 2010

どうもありがとうございました...とても助かりました。あなたに感謝します.. :)
innodeasapps 2012

1
注意点として、スーパービュー[この場合はview2]の背景色がはっきりしていることを確認してください。これはとにかく私の問題でした
Adam Carter

@GangstaGrahamは、view1のサブビューとしてview2を追加していることを確認してください。
pxpgraphics 2014年

まさに私が必要としていたものです!
Rohan Sanap 2016年

17

今はiOS6ですが、状況が変わった可能性があります。TheSquadの答えは、もう1行追加するまでview2.layer.masksToBounds = NO;機能しません。そうしないと、影が表示されません。ドキュメントにはmasksToBoundsデフォルトでNOと書かれていますが、私のコードはその逆を示しています。

これが、私のアプリで最も一般的に使用されるコードスニペットの1つである、影付きの丸い角のボタンを作成する方法です。

button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;

view.layer.masksToBounds = NO;      // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath = 
    [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;      

[view addSubview:button];

編集

ビューをアニメーション化またはスクロールする必要がある場合は、masksToBounds = YESパフォーマンスに大きな負担がかかります。つまり、アニメーションが途切れる可能性があります。角を丸くして影を付け、アニメーションやスクロールをスムーズにするには、代わりに次のコードを使用します。

button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;

view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;  

[view addSubview:button];

4
最初の答えは+1。更新された回答は、角の丸い部分では機能しません。「masksToBounds」を「NO」に設定すると、角の丸い部分が消えます。これを変更する代わりに、「shouldRasterize」プロパティを使用して良好なパフォーマンスを得ることができます。
Dinesh Raja

これは現在2020年に機能します。1行追加するだけの簡単なソリューションです。
GeneCode

4

これは、@ TheSquadによって投稿された回答のSwift3およびIBDesignableバージョンです。

ストーリーボードファイルに変更を加える際に、同じ概念を使用しました。まず、targetView(コーナーの半径と影が必要なもの)を新しいcontainerView内に移動しました。次に、次のコード行を追加して(参照:https://stackoverflow.com/a/35372901/419192)、UIViewクラスのIBDesignable属性をいくつか追加しました。

@IBDesignable extension UIView {
/* The color of the shadow. Defaults to opaque black. Colors created
 * from patterns are currently NOT supported. Animatable. */
@IBInspectable var shadowColor: UIColor? {
    set {
        layer.shadowColor = newValue!.cgColor
    }
    get {
        if let color = layer.shadowColor {
            return UIColor(cgColor: color)
        }
        else {
            return nil
        }
    }
}

/* The opacity of the shadow. Defaults to 0. Specifying a value outside the
 * [0,1] range will give undefined results. Animatable. */
@IBInspectable var shadowOpacity: Float {
    set {
        layer.shadowOpacity = newValue
    }
    get {
        return layer.shadowOpacity
    }
}

/* The shadow offset. Defaults to (0, -3). Animatable. */
@IBInspectable var shadowOffset: CGPoint {
    set {
        layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
    }
    get {
        return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
    }
}

/* The blur radius used to create the shadow. Defaults to 3. Animatable. */
@IBInspectable var shadowRadius: CGFloat {
    set {
        layer.shadowRadius = newValue
    }
    get {
        return layer.shadowRadius
    }
}

/* The corner radius of the view. */
@IBInspectable var cornerRadius: CGFloat {
    set {
        layer.cornerRadius = newValue
    }
    get {
        return layer.cornerRadius
    }
}

このコードを追加した後、ストーリーボードに戻り、containerViewを選択すると、属性インスペクターで新しい属性のセットを見つけることができました。

ここに画像の説明を入力してください

選択に従ってこれらの属性の値を追加する以外に、targetViewにコーナー半径を追加し、masksToBoundsプロパティをtrueに設定しました。

これがお役に立てば幸いです:)


4

StoryBoardを使用したSwift3.0バージョン

@TheSquadと同じ考え。実際のビューの下に新しいビューを作成し、下のビューに影を追加します。

1.実際のビューの下にビューを作成します

UIViewターゲットビューと同じ制約でをStoryBoardにドラッグします。ターゲットビューにバインドするクリップを確認します。また、ターゲットビューが新しいビューをカバーするように、新しいビューがターゲットビューの前にリストされていることを確認してください。

ここに画像の説明を入力してください

2.次に、新しいビューをコードにリンクし、それにシャドウを追加します

これは単なるサンプルです。ここでは好きなようにできます

shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3

shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true

2

また、影や角の丸みなど、パフォーマンスに大きな問題がありました。shadowPath部分を使用する代わりに、パフォーマンスへの影響を完全に解決する次の行を使用しました。

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;

ただし、これで画面をアニメーション化またはリレーアウトすることはできません。
ヴァンデュトラン

1

解決策の1つは次のとおりです。

     @IBOutlet private weak var blockView: UIView! {
         didSet {
          blockView.backgroundColor = UIColor.white
          blockView.layer.shadowColor = UIColor.black.cgColor
          blockView.layer.shadowOpacity = 0.5
          blockView.layer.shadowOffset = CGSize.zero

          blockView.layer.cornerRadius = 10
        }
      }
      @IBOutlet private weak var imageView: UIImageView! {
        didSet {
          imageView.layer.cornerRadius = 10
          imageView.layer.masksToBounds = true

          imageView.layer.shouldRasterize = true
        }
      }

ここに画像の説明を入力してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.