web-dev-qa-db-ja.com

どうやってUIViewの下に影を描くの?

Cocoa TouchのUIViewの下端に影を描こうとしています。私は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に必要なことがあれば、ちょっと立ち往生していますか?

345
Fraser Speirs

現在のコードでは、現在のコンテキストの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);
}
94

はるかに簡単な方法は、初期化時にビューのいくつかのレイヤ属性を設定することです。

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

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

#import <QuartzCore/QuartzCore.h>
784
ollie
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;
230
ZYiOS

同じソリューションですが、ただ思い出させるために、ストーリーボードで直接影を定義できます。

例:

enter image description here

156
Antzi

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

スウィフト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

スイフト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

見開き付きの例

Example with spread

基本的な影を作成する

    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での基本的な影の例

OUTPUT

42
O-mkar

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パネル> Attributesインスペクタのすべてのビューに対してInterface Builderで利用可能になります。

Utilities Panel

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

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

これをうまく動作させることに失敗した人には[Clip Subviews(clipsToBounds)]が有効になっていないことを確認してください。

19
Axel Guilmin

私は私のutilsの一部としてこれを使います。これにより、影を設定できるだけでなく、任意の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];

13

スイフト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
    }
}
7
neoneye

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.ストーリーボードでこれらの属性を使用できるようになりました storyboard image

5
king_T

ここですべての答えを試した後、これを機能させることに失敗した人たち(私自身も!)に、属性インスペクタでClip Subviewsが有効になっていないことを確認してください。

3
Mazen Kasser

すべてよく答えますが、もう1つポイントを付け加えたいと思います。

テーブルセルがあるときに問題が発生した場合は、新しいセルのDequeにシャドウの不一致があるため、シャドウコードを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
Vishal16

他の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
Martin Zikmund

あなたは以下のように影と角の半径のために作られた私の効用関数を使うことができます:

- (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

スイフト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
0
Josh O'Connor