UIViewの下に影を描画するにはどうすればよいですか?


352

UIViewCocoa Touchの下端に影を付けようとしています。CGContextSetShadow()影の描画に使用する必要があることは理解していますが、Quartz 2Dプログラミングガイドは少し曖昧です。

  1. グラフィック状態を保存します。
  2. 関数を呼び出し、CGContextSetShadow適切な値を渡します。
  3. 影を付ける描画をすべて行います。
  4. グラフィック状態を復元する

私はUIViewサブクラスで次のことを試しました:

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    CGContextRestoreGState(currentContext);
    [super drawRect: rect];
}

..しかし、これは私にとってはうまくいきません、そして(a)次にどこに行くべきか、そして(b)私UIViewがこの仕事をするために私にすべきことが何かあるかどうかについて私は少し行き詰まっていますか?

回答:


98

現在のコードでGStateは、現在のコンテキストのを保存し、シャドウを描画するように構成し、シャドウを描画するように構成する前の状態に復元します。次に、最後に、スーパークラスのdrawRect:の実装を呼び出します。

シャドウ設定の影響を受ける描画は、後で実行する必要があります

CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);

しかし前に

CGContextRestoreGState(currentContext);

したがって、スーパークラスをdrawRect:シャドウで「ラップ」したい場合は、このようにコードを再配置するとどうでしょうか。

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
}

789

はるかに簡単なアプローチは、初期化時にビューのいくつかのレイヤー属性を設定することです:

self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

QuartzCoreをインポートする必要があります。

#import <QuartzCore/QuartzCore.h>

4
ただし、これはiOS 3.2以降でのみ機能するので、アプリが古いバージョンで機能する場合は、Christianのソリューションを使用するか、オプションの場合はビューの背後にある静止画像を使用する必要があります。
florianbuerger

119
このソリューションでは#import <QuartzCore/QuartzCore.h>"、.hファイルへの追加も必要です。
MusiGenesis

26
に設定masksToBoundsするNOと、は無効になりcornerRadiusますか?
pixelfreak

4
それを解決するには、backgroundColorをレイヤーに設定し、ビューを透明にする必要があります。
pixelfreak

@pixelfreakどうやってやるの?私は試しましたself.layer.backgroundColor = [[UIColor whiteColor] CGColor];が運はありませんでした。どのビューを透明にする必要がありますか?
Victor Van Hee

232
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 8; // if you like rounded corners
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

これにより、アプリケーションの速度が低下します。次の行を追加すると、ビューが目に見える長方形である限り、パフォーマンスが向上します。

self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;

1
注目に値するのは、この最適化はビューが目に見える長方形の場合にのみ役立つことです。
ベンジャミンドベル、2011

1
self.layer.shadowPath ...何の代わりに?または単にそれに追加する
Chrizzz

2
他の行に加えてその追加の行を追加するだけです。
クリストファーコットン、2011年

11
@NathanGaskin-影の描画はコストのかかる操作です。たとえば、アプリケーションが他のインターフェイスの向きを許可し、デバイスの回転を開始した場合、影のパスを明示的に指定せずに、アニメーション中に数回レンダリングする必要があります。形状、おそらく目に見えてアニメーションを遅くします
Peter Pajchl

9
@BenjaminDobellこの特定の線は長方形に対してのみ機能しますが、非長方形のパスを作成することもできます。たとえば、角丸四角形がある場合は、bezierPathWithRoundedRect:cornerRadius:
Dan Dyer

160

同じ解決策ですが、思い出させてください:ストーリーボードで直接シャドウを定義できます。

例:

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


2
悲しいことに、CGColorはストーリーボードから手が届かないと思います:(
Antzi

1
UIViewまたは、プロパティのラッパーCGLayerであるカテゴリを定義するだけです;)UIColorCGColor
DeFrenZ

1
このリンクは、そうする方法について説明します。cocoadventures.org/post/104137872754/...
カムチャツカ

8
簡単にコピーして貼り付けることができるようにするには、layer.masksToBound、layer.shadowOffset、layer.shadowRadius、layer.shadowOpacityを使用します。タイプミスはあなたをここで殺します。
etayluz、

3
layer.shadowOpacityの値は0.5ではなく0.5にする必要があります
Desert Rose

43

これを試すことができます....値で遊ぶことができます。shadowRadiusぼかしの量を指示します。shadowOffset影がどこに行くかを決定します。

Swift 2.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.CGPath

Swift 3.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height 
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height)) 
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.black.cgColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.cgPath

スプレッドの例

スプレッドの例

基本的な影を作成するには

    demoView.layer.cornerRadius = 2
    demoView.layer.shadowColor = UIColor.blackColor().CGColor
    demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread
    demoView.layer.shadowOpacity = 0.5 
    demoView.layer.shadowRadius = 5.0 //Here your control your blur

Swift 2.0の基本的なシャドウの例

出力


19

Interface Builderを使用したシンプルでクリーンなソリューション

プロジェクトにUIView.swiftという名前のファイルを追加します(またはこれを任意のファイルに貼り付けます)。

import UIKit

@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
        }
    }
}

これは、Utilities Panel> Attributes InspectorのすべてのビューのInterface Builderで利用できます。

ユーティリティパネル

これで簡単に影を設定できます。

注:
-影はIBに表示されず、実行時にのみ表示されます。
-Mazen Kasserが言ったように

これclipsToBoundsが機能しない場合は[...]クリップサブビュー()が有効になっていないことを確認してください


これが正解です。将来のインターフェースのコードの構成
Crake

次の警告メッセージ(各プロパティの1)と非作業動作にこのソリューションをリード私を:Failed to set (shadowColor) user defined inspected property on (UICollectionViewCell): [<UICollectionViewCell> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key shadowColor.
Xvolks

1
UIKit動作させるためにインポートする必要Foundationがありました。XCodeによって作成された基本的なインポートでは不十分ですが、正常にコンパイルされます。ソースコード全体をコピーする必要がありました。この素晴らしい解決策をありがとうAxel。
Xvolks 2016

13

私はこれを私のユーティリティの一部として使用しています。これにより、影を設定できるだけでなく、任意の角を丸くすることができますUIView。また、好きな色の影を設定することもできます。通常は黒が推奨されますが、背景が白以外の場合は別のものが必要になることがあります。これが私が使うものです-

in utils.m
+ (void)roundedLayer:(CALayer *)viewLayer 
              radius:(float)r 
              shadow:(BOOL)s
{
    [viewLayer setMasksToBounds:YES];
    [viewLayer setCornerRadius:r];        
    [viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
    [viewLayer setBorderWidth:1.0f];
    if(s)
    {
        [viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
        [viewLayer setShadowOffset:CGSizeMake(0, 0)];
        [viewLayer setShadowOpacity:1];
        [viewLayer setShadowRadius:2.0];
    }
    return;
}

これを使用するには、これを呼び出す必要があります- [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];


7

スウィフト3

extension UIView {
    func installShadow() {
        layer.cornerRadius = 2
        layer.masksToBounds = false
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOffset = CGSize(width: 0, height: 1)
        layer.shadowOpacity = 0.45
        layer.shadowPath = UIBezierPath(rect: bounds).cgPath
        layer.shadowRadius = 1.0
    }
}

この方法を使用している場合は、値を動的に調整できるようにパラメーターを追加することを検討します
Josh O'Connor

角が丸くなっていませんが、何か間違っているのでしょうか。
Nikhil Manapure 2017

機能があるため@NikhilManapure何も出来事が、これは可能性installShadow()から呼び出されていませんviewDidLoad()viewWillAppear()
neoneye

オーバーロードされたdrawビューのメソッドから呼び出していました。影は正しく来ていますが、角の丸いはそうではありません。
Nikhil Manapure 2017

@NikhilManapure丸みを帯びた境界線UIStackViewはありません。これは、ビューがレイアウトのみを行い、ビューを持たないためである可能性があります。UIViewすべてのコンテナとしてレギュラーを挿入する必要があるかもしれません。
neoneye 2017

6

StoryBoardを使用し、ランタイム属性を入力し続けたくない場合は、ビューの拡張を簡単に作成して、ストーリーボードで使用できるようにすることができます。

ステップ1.拡張機能を作成する

extension UIView {

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

@IBInspectable var shadowOpacity: Float {
    get {
        return layer.shadowOpacity
    }
    set {
        layer.shadowOpacity = newValue
    }
}

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

@IBInspectable var maskToBound: Bool {
    get {
        return layer.masksToBounds
    }
    set {
        layer.masksToBounds = newValue
    }
}
}

ステップ2.ストーリーボードでこれらの属性を使用できるようになりましたストーリーボード画像


3

ここですべての答えを試した後、これを機能させるのに失敗した人(自分自身!)には、属性インスペクタでクリップサブビューが有効になっていないことを確認してください...


1

スウィフト3

self.paddingView.layer.masksToBounds = false
self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10)
self.paddingView.layer.shadowRadius = 5
self.paddingView.layer.shadowOpacity = 0.5

1

次のように、シャドウとコーナーの半径用に作成された私のユーティリティ関数を使用できます。

- (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{

    // drop shadow
    [view.layer setShadowRadius:shadowRadius];
    [view.layer setShadowOpacity:shadowOpacity];
    [view.layer setShadowOffset:shadowOffset];
    [view.layer setShadowColor:shadowColor.CGColor];

    // border radius
    [view.layer setCornerRadius:cornerRadius];

    // border
    [view.layer setBorderColor:borderColor.CGColor];
    [view.layer setBorderWidth:borderWidth];
}

それがあなたを助けることを願っています!!!


1

すべて正解ですが、もう1つポイントを追加します

テーブルセルがあるときに問題が発生した場合は、新しいセルをデキューしてシャドウに不一致があるため、この場合は、すべての条件で適切に動作するように、layoutSubviewsメソッドにシャドウコードを配置する必要があります。

-(void)layoutSubviews{
    [super layoutSubviews];

    [self.contentView setNeedsLayout];
    [self.contentView layoutIfNeeded];
    [VPShadow applyShadowView:self];
}

または特定のビューのViewControllersでシャドウコードを次のメソッド内に配置して、うまく機能するようにします

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

    [self.viewShadow layoutIfNeeded];
    [VPShadow applyShadowView:self.viewShadow];
}

より一般化されたフォームexのために、新しい開発者向けにシャドウ実装を変更しました。

/*!
 @brief Add shadow to a view.

 @param layer CALayer of the view.

 */
+(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{
    UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius];
    layer.masksToBounds = NO;
    layer.shadowColor = [UIColor blackColor].CGColor;
    layer.shadowOffset = CGSizeMake(x,y);// shadow x and y
    layer.shadowOpacity = alpha;
    layer.shadowRadius = radius;// blur effect
    layer.shadowPath = shadowPath.CGPath;
}

1

仲間のXamariansの場合、Xamarin.iOS / C#バージョンの回答は次のようになります。

public override void DrawRect(CGRect area, UIViewPrintFormatter formatter)
{
    CGContext currentContext = UIGraphics.GetCurrentContext();
    currentContext.SaveState();
    currentContext.SetShadow(new CGSize(-15, 20), 5);
    base.DrawRect(area, formatter);
    currentContext.RestoreState();                
}

主な違いはCGContext、適切なメソッドを直接呼び出すインスタンスを取得することです。


1

これExtensionを使用して影を追加できます

extension UIView {

    func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float)
    {
        layer.masksToBounds = false
        layer.shadowOffset = offset
        layer.shadowColor = color.cgColor
        layer.shadowRadius = radius
        layer.shadowOpacity = opacity

        let backgroundCGColor = backgroundColor?.cgColor
        backgroundColor = nil
        layer.backgroundColor =  backgroundCGColor
    }
}

あなたはそれを

your_Custom_View.addShadow(offset: CGSize(width: 0, height: 1), color: UIColor.black, radius: 2.0, opacity: 1.0)

0

Swift 4でIBDesignableおよびIBInspectableを使用してシャドウをスケッチする

それの使い方

デモ

サイドバイサイドのスケッチとXCODE

影の例

コード

@IBDesignable class ShadowView: UIView {

    @IBInspectable var shadowColor: UIColor? {
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
        set {
            if let color = newValue {
                layer.shadowColor = color.cgColor
            } else {
                layer.shadowColor = nil
            }
        }
    }

    @IBInspectable var shadowOpacity: Float {
        get {
            return layer.shadowOpacity
        }
        set {
            layer.shadowOpacity = newValue
        }
    }

    @IBInspectable var shadowOffset: CGPoint {
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }

     }

    @IBInspectable var shadowBlur: CGFloat {
        get {
            return layer.shadowRadius
        }
        set {
            layer.shadowRadius = newValue / 2.0
        }
    }

    @IBInspectable var shadowSpread: CGFloat = 0 {
        didSet {
            if shadowSpread == 0 {
                layer.shadowPath = nil
            } else {
                let dx = -shadowSpread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                layer.shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
    }
}

出力

デモ出力


属性からシャドウプレフィックスを削除するコードを改善できます。ShadowViewの目的は、シャドウ操作について明確です。もう1つは、このクラスにコーナー半径を追加できることです。(今日、ほとんどの影のビューにはコーナー半径が含まれます)
mathema
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.