web-dev-qa-db-ja.com

UIViewにドロップシャドウを追加する最良の方法は何ですか

重ねて表示されるビューにドロップシャドウを追加しようとしています。ビューが折りたたまれて、他のビューのコンテンツが表示されます。このように、ビューが折りたたまれたときにview.clipsToBounds ONを維持しますそれらのコンテンツはクリップされます。

これにより、clipsToBoundsをオンにすると影もクリップされるため、レイヤーにドロップシャドウを追加することが困難になったようです。

フレームにドロップシャドウを追加するためにview.frameview.boundsを操作しようとしましたが、境界がそれを囲むのに十分な大きさになるようにしましたが、これには運がありませんでした。

以下は、シャドウを追加するために使用しているコードです(これは、示されているclipsToBounds OFFでのみ機能します)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;

これは、最も明るい灰色のレイヤーに適用される影のスクリーンショットです。 clipsToBoundsがOFFの場合、これが私のコンテンツがどのようにオーバーラップするかのアイデアを与えてくれることを願っています。

Shadow Application.

UIViewに影を追加して、コンテンツをクリップしたままにするにはどうすればよいですか?

編集:影付きの背景画像を使用して遊んだことを追加したかっただけですが、これはうまく機能しますが、これに最適なコード化されたソリューションを知りたいです。

103
Wez

これを試して:

UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;

まずはUIBezierPathとして使用されるshadowPathは重要です。それを使用しない場合、最初は違いに気付かないかもしれませんが、鋭い目は、デバイスの回転などのイベント中に発生する特定の遅延を観察します。これは重要なパフォーマンス調整です。

特に問題に関して:重要な行はview.layer.masksToBounds = NOです。ビューの境界よりも遠くに広がるビューのレイヤーのサブレイヤーのクリッピングを無効にします。

masksToBounds(レイヤー上)とビュー自身のclipToBoundsプロパティの違いは何なのか疑問に思う人のために:実際には何もありません。一方を切り替えると、もう一方に影響があります。異なるレベルの抽象化。


Swift 2.2:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.blackColor().CGColor
    layer.shadowOffset = CGSizeMake(0.0, 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.CGPath
}

スウィフト3:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.cgPath
}
272
pkluz

Swift 2.3のWasabiiの回答:

    let shadowPath = UIBezierPath(rect: view.bounds)
    view.layer.masksToBounds = false
    view.layer.shadowColor = UIColor.blackColor().CGColor
    view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
    view.layer.shadowOpacity = 0.2
    view.layer.shadowPath = shadowPath.CGPath

Swift 3/4で:

    let shadowPath = UIBezierPath(rect: view.bounds)
    view.layer.masksToBounds = false
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
    view.layer.shadowOpacity = 0.2
    view.layer.shadowPath = shadowPath.cgPath

AutoLayoutを使用している場合、このコードをlayoutSubviews()に配置します。

62
Bart van Kuik

トリックは、ビューのレイヤーのmasksToBoundsプロパティを適切に定義することです。

view.layer.masksToBounds = NO;

動作するはずです。

ソース

13
sergio

UIViewの拡張機能を作成して、デザインエディターでこれらの値にアクセスできます。

Shadow options in design editor

extension UIView{

    @IBInspectable var shadowOffset: CGSize{
        get{
            return self.layer.shadowOffset
        }
        set{
            self.layer.shadowOffset = newValue
        }
    }

    @IBInspectable var shadowColor: UIColor{
        get{
            return UIColor(cgColor: self.layer.shadowColor!)
        }
        set{
            self.layer.shadowColor = newValue.cgColor
        }
    }

    @IBInspectable var shadowRadius: CGFloat{
        get{
            return self.layer.shadowRadius
        }
        set{
            self.layer.shadowRadius = newValue
        }
    }

    @IBInspectable var shadowOpacity: Float{
        get{
            return self.layer.shadowOpacity
        }
        set{
            self.layer.shadowOpacity = newValue
        }
    }
}
13
Chris Stillwell

ストーリーボードからビューに影を設定することもできます

enter image description here

6
pallavi

ViewWillLayoutSubviewsの場合:

override func viewWillLayoutSubviews() {
    sampleView.layer.masksToBounds =  false
    sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
    sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
    sampleView.layer.shadowOpacity = 1.0
}

UIViewの拡張機能を使用:

extension UIView {

    func addDropShadowToView(targetView:UIView? ){
        targetView!.layer.masksToBounds =  false
        targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
        targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
        targetView!.layer.shadowOpacity = 1.0
    }
}

使用法:

sampleView.addDropShadowToView(sampleView)
1
A.G

そのため、パフォーマンスのためにshadowPathプロパティを優先する必要がありますが、以下も必要です:CALayer.shadowPathのヘッダーファイルから

このプロパティを使用してパスを明示的に指定すると、通常、同じパスを共有する*複数のレイヤーで参照を行うため、レンダリングのパフォーマンスが向上します。

あまり知られていないトリックは、複数のレイヤー間で同じ参照を共有することです。もちろん同じ形状を使用する必要がありますが、これはテーブル/コレクションビューのセルで一般的です。

インスタンスを共有するとなぜ高速になるのかわかりませんが、シャドウのレンダリングをキャッシュし、ビュー内の他のインスタンスに再利用できると推測しています。私はこれがさらに速くなるのだろうか

0
Rufus Mall