影の広がりとぼかしを制御する方法は?


112

私はスケッチでUI要素を設計しましたが、そのうちの1つにぼかし1と広がり0の影があります。ビューのレイヤープロパティのドキュメントを調べましたが、レイヤーには広がりまたはぼかし、または同等の名前の付いたものはありません(唯一のコントロールは単にshadowOpacity)ぼかしや広がりなどをどのように制御できますか?

編集:

ここに私のスケッチの設定があります: そして、これは私が私の影をどのように見せたいかです:影の設定をスケッチする


シャドウが欲しかった




現時点での表示は次のとおりです。注:実際に影を表示するには、画像をクリックする必要があります。

現在の影

私のコードは次のとおりです:

func setupLayer(){
    view.layer.cornerRadius = 2
    view.layer.shadowColor = Colors.Shadow.CGColor
    view.layer.shadowOffset = CGSize(width: 0, height: 1)
    view.layer.shadowOpacity = 0.9
    view.layer.shadowRadius = 5
}

タグios(プラットフォーム)、デザイン(ソフトウェアSketchの使用)、およびCore-Graphics(UIBezierPathを使用して影を描画することは可能ですが、関連性があるかもしれません)はすべて関連性があります。除去される。
Quantaliinuxite

あなたはその白いビューの影だけが欲しいですか?
O-mkar

5
iOSとSketchでは同じパラメータで常に異なるため、SketchとCoreAnimationフレームワークは異なるメトリックを持っているように見えます。
ティムールベルニコビッチ2017年

ああ。iOSの動作にほとんどまたはまったく似ていないツールを使用するデザイナーと一緒に作業する喜び。SketchではなくPaintCodeのようなものに移動すると、iOSのように機能するだけでなく、必要なコードも提供されます。:-)
フォグマイスター2017

スケッチで半径とぼかしの両方を設定した場合はどうなりますか?
2018年

回答:


256

以下は、ほぼ完全な精度で6つのスケッチシャドウプロパティすべてをUIViewのレイヤーに適用する方法です。

extension CALayer {
  func applySketchShadow(
    color: UIColor = .black,
    alpha: Float = 0.5,
    x: CGFloat = 0,
    y: CGFloat = 2,
    blur: CGFloat = 4,
    spread: CGFloat = 0)
  {
    shadowColor = color.cgColor
    shadowOpacity = alpha
    shadowOffset = CGSize(width: x, height: y)
    shadowRadius = blur / 2.0
    if spread == 0 {
      shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

次のように表現したいとします。

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

これは次の方法で簡単に実行できます。

myView.layer.applySketchShadow(
  color: .black, 
  alpha: 0.5, 
  x: 0, 
  y: 0, 
  blur: 4, 
  spread: 0)

またはもっと簡潔に:

myView.layer.applySketchShadow(y: 0)

例:

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

左:iPhone 8 UIViewスクリーンショット。右:長方形をスケッチします。

注意:

  • 0以外を使用する場合、CALayerのにspread基づいてパスをハードコードしますbounds。レイヤーの境界が変更される場合は、applySketchShadow()メソッドを再度呼び出します。

うーん。スケッチでは、スプレッドを適用すると、シャドウがぼかれる/グラデーションが始まる「外へ」移動します。つまり、ぼかしが始まるまで、一定の影の色が維持されます。しかし、shadowLayerを移動すると、影はそのレイヤーからのみ開始されますよね?(たとえば、広がりが10の場合、シャドウはオフセット10の後にのみ開始されます。スケッチの場合と同様に、ぼかし/グラデーションはオフセット10の後に開始されます)。だから彼らは本当に一致しませんよね?
ステファン

1
@Sensefulこの式をどのように達成したか知りたい!?
Hashem Aboonajmi

2
設定するのを忘れましたmaskToBounds = false
ScottyBlades 2018

1
上記のScottyBladesが述べたように、これはなしでは機能しませんmaskToBounds = false。失敗する例は、このシャドウをに適用する場合ですUIButton
ホセ

5
これはshadowRadius = blur / UIScreen.main.scaleハードコーディングとは対照的blur / 2.0ですか?または、2で割る別の理由はありますか?@matchifang分かりましたか?
JWK 2019

59

これを試すことができます....値で遊ぶことができます。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の基本的なシャドウの例

出力


2
情報は間違っています。オフセットは拡散の制御ではなく、xとyの制御です。
Quantaliinuxite 2016年

@Quantaliinuxite私は誤ってコメントを入れ替えました。私は回答を更新しました
O-mkar

右、そして今私の質問の核心に来ています:どのようにスプレッドを制御するのですか?
Quantaliinuxite 2016年

@Quantaliinuxite更新されたコードは、2.1を必要なスプレッドの量に変更します
O-mkar

答えてくれてありがとう。ここでは、shadowOpacityを理解できません(なぜ使用されているのか)。
Sunita、2016年

11

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

出力

デモ出力

どうやって使うのですか

デモ


layer.shadowRadius * 2shadowBlurゲッターに戻るべきではない
berkuqo

7

このコードは私にとって非常にうまくいきました:

yourView.layer.shadowOpacity = 0.2 // opacity, 20%
yourView.layer.shadowColor = UIColor.black.cgColor
yourView.layer.shadowRadius = 2 // HALF of blur
yourView.layer.shadowOffset = CGSize(width: 0, height: 2) // Spread x, y
yourView.layer.masksToBounds = false

スケッチのぼかしの値がコアアニメーションのシャドウの半径と一致しませ
CIFilter

最終的に私たちにとって十分に近く機能したのは、Sketch for Core Animationのシャドウ半径のブラー値を半分にすることでした。
CIFilter 2018年

2

定義済みのパス(たとえば、円形のビューのように)にシャドウを適用しようとしている人のために、これが私が最終的に得たものです:

extension CALayer {
    func applyShadow(color: UIColor = .black,
                     alpha: Float = 0.5,
                     x: CGFloat = 0,
                     y: CGFloat = 2,
                     blur: CGFloat = 4,
                     spread: CGFloat = 0,
                     path: UIBezierPath? = nil) {
        shadowColor = color.cgColor
        shadowOpacity = alpha
        shadowRadius = blur / 2
        if let path = path {
            if spread == 0 {
                shadowOffset = CGSize(width: x, height: y)
            } else {
                let scaleX = (path.bounds.width + (spread * 2)) / path.bounds.width
                let scaleY = (path.bounds.height + (spread * 2)) / path.bounds.height

                path.apply(CGAffineTransform(translationX: x + -spread, y: y + -spread).scaledBy(x: scaleX, y: scaleY))
                shadowPath = path.cgPath
            }
        } else {
            shadowOffset = CGSize(width: x, height: y)
            if spread == 0 {
                shadowPath = nil
            } else {
                let dx = -spread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
        shouldRasterize = true
        rasterizationScale = UIScreen.main.scale
    }
}

いくつかの例を後で掲載しますが、これは私にとって循環ビューのスポットとして機能しました。


これは「ほぼ」機能しますが、1with xとのスプレッドを使用すると、スプレッドは間違っていますy 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path)。この場合、シャドウにはオフセットがありますが、そうではありません。ここでは、三角形の影がすべての方向に1 ピクセル
Jan

1

この投稿の返信に基づく私の解決策:(Swift 3)

let shadowPath = UIBezierPath(rect: CGRect(x: -1,
                                           y: -2,
                                           width: target.frame.width + 2,
                                           height: target.frame.height + 2))

target.layer.shadowColor = UIColor(hexString: shadowColor).cgColor
target.layer.shadowOffset = CGSize(width: CGFloat(shadowOffsetX), height: CGFloat(shadowOffsetY))
target.layer.masksToBounds = false
target.layer.shadowOpacity = Float(shadowOpacity)
target.layer.shadowPath = shadowPath.cgPath

0

ここに投稿された回答とコメントの提案が本当に気に入っいます。これは私がそのソリューションを変更した方法です:

extension UIView {
  func applyShadow(color: UIColor, alpha: Float, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat) {
    layer.masksToBounds = false
    layer.shadowColor = color.cgColor
    layer.shadowOpacity = alpha
    layer.shadowOffset = CGSize(width: x, height: y)
    layer.shadowRadius = blur / UIScreen.main.scale
    if spread == 0 {
      layer.shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      layer.shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

使用法:

myButton.applyShadow(color: .black, alpha: 0.2, x: 0, y: 1, blur: 2, spread: 0)

-1

歴史的に少し掘り下げたものかもしれませんが、同じ問題を抱えている人もいるでしょう。受け入れられた回答のコードサンプルを使用しました。ただし、効果はかなり異なります。-スケッチ内の同じ値と比較してy値は約半分でなければなりません-ナビゲーションバーに影を適用しようとしましたが、効果が大きく異なります-スケッチと同じ値を使用してもほとんど見えません。

したがって、この方法はスケッチパラメータをまったく反映していないようです。ヒントはありますか?


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