如何控制阴影扩散和模糊?


112

我已经在草图中设计了UI元素,其中一个元素的阴影带有模糊1和散布0。只是shadowOpacity)如何控制像模糊和扩散之类的东西?

编辑:

这是我在Sketch中的设置: 这就是我希望我的阴影看起来像的样子:草图阴影设置


暗影通缉




这是当前的样子:注意,您必须单击图片才能实际看到阴影。

当前阴影

我的代码如下:

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 2015年

您只想为白色视图添加阴影吗?
O-mkar 2015年

5
看起来Sketch和CoreAnimation框架具有不同的指标,因为它在iOS和带有相同参数的Sketch上总是看起来不同。
Timur Bernikovich

啊。与使用与iOS的工作方式几乎没有或几乎没有相似之处的工具的设计师合作的乐趣。如果您使用的是PaintCode而不是Sketch,那么它不仅会像iOS一样工作,而且还会为您提供所需的代码。:-)
Fogmeister

如果在草图中同时设置了半径和模糊度怎么办?
弗拉德

Answers:


256

以下是将所有6个Sketch阴影属性几乎完美地应用于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屏幕截图;右:草图矩形。

注意:

  • 当使用非零值时spread,它将根据boundsCALayer的硬编码路径。如果图层的边界发生了变化,则需要applySketchShadow()再次调用该方法。

嗯 在Sketch中,如果您应用散布,它将“移出”,阴影的模糊/渐变开始于此。这意味着-它保持恒定的阴影颜色,直到模糊开始。但是,如果我们移动shadowLayer,则阴影只会从该层开始,对吗?(说得过去是10,阴影只会在偏移量10之后开始,而在Sketch中,模糊/渐变将在偏移量10之后开始)。所以他们真的不匹配,对吗?
斯蒂芬

1
@Senseful我很想知道您是如何实现这个公式的?
Hashem Aboonajmi

2
你忘记了maskToBounds = false
ScottyBlades

1
正如ScottyBlades上文所述,如果没有,这将无法工作maskToBounds = false。将阴影应用到时,将失败的示例UIButton
何塞

5
应该shadowRadius = blur / UIScreen.main.scale与硬编码相对blur / 2.0吗?或者,还有其他原因除以2吗?@matchifang你明白了吗?
JWK

59

您可以尝试....您可以使用这些值。该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

传播的例子

传播的例子

创建基本阴影

    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中的基本Shadow示例

输出值


2
信息是错误的,偏移量不是扩展的控制,而是x和y的控制。
2016年

@Quantaliinuxite我无意间交换了评论,我已经更新了答案
O-mkar

是的,现在是我的问题的核心:如何控制传播?
Quantaliinuxite

@Quantaliinuxite更新的代码现在将2.1更改为您所需的点差量
O-mkar,2016年

感谢你的回答。在这里,我无法理解shadowOpacity(为什么使用它)。
Sunita

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 * 2找shadowBlur getter
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

Sketch中的模糊值与Core Animation的阴影半径匹配。
CIFilter

最终,对于我们来说足够接近的工作是将Sketch中的模糊值减为Core Animation的阴影半径的一半。
CIFilter

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)。在这种情况下,阴影具有偏移量,而不应具有偏移量。在这里,三角形的阴影应在所有方向上扩展1px ibb.co/YjwRb9B
1

1

基于此帖子的我的解决方案回复:(快速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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.