Como controlar a propagação e o desfoque das sombras?

113

Eu projetei elementos de interface do usuário no esboço, e um deles tem uma sombra com desfoque 1 e propagação 0. Eu olhei para o documento para a propriedade da camada de visualizações e a camada não tem nada chamado spread ou blur, ou algo equivalente (o único controle era meramente shadowOpacity) Como posso controlar coisas como borrar e espalhar?

EDITAR:

Aqui estão minhas configurações no Sketch: E aqui está como eu quero que minha sombra se pareça:Configurações de sombra de esboço


Shadow queria




E aqui está o que parece no momento: Observe, você tem que clicar na imagem para realmente ver a sombra.

Sombra atual

Meu código é o seguinte:

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
}
Quantaliinuxita
fonte
As tags ios (a plataforma), design (o uso do software Sketch) e Core-Graphics (é possível usar um UIBezierPath para desenhar a sombra, o que pode ser relevante) são todas relevantes, não vejo porque deveriam ser removido.
Quantaliinuxite
você quer sombra para aquela visão branca apenas certa?
O-mkar de
5
Parece que o framework Sketch e CoreAnimation têm métricas diferentes, porque sempre parece diferente no iOS e no Sketch com os mesmos parâmetros.
Timur Bernikovich
Ah. A alegria de trabalhar com designers que usam ferramentas que têm pouca ou nenhuma semelhança com o funcionamento do iOS. Se você mudar para algo como PaintCode em vez de Sketch, não só funcionará como o iOS, mas também fornecerá o código de que você precisa. :-)
Fogmeister
E se você tiver definido o raio e o desfoque no esboço?
Vladimir

Respostas:

258

Veja como aplicar todas as 6 propriedades de sombra Sketch para uma camada UIView com precisão quase perfeita:

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

Digamos que queremos representar o seguinte:

insira a descrição da imagem aqui

Você pode fazer isso facilmente por meio de:

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

ou mais sucintamente:

myView.layer.applySketchShadow(y: 0)

Exemplo:

insira a descrição da imagem aqui

Esquerda: captura de tela do UIView do iPhone 8; direita: retângulo de esboço.

Nota:

  • Ao usar um diferente de zero spread, ele codifica um caminho com base no boundsda CALayer. Se os limites da camada mudarem, convém chamar o applySketchShadow()método novamente.
Sensível
fonte
Hm. No Sketch, se você aplicar spread, ele 'se move para fora', onde começa o desfoque / gradiente da sombra. O que significa - ele permanece na cor de sombra constante, até que o borrão comece. Mas se movermos a shadowLayer, a sombra só começará a partir dessa camada, certo? (Digamos que a propagação seja 10, a sombra só começaria após um deslocamento de 10, onde, como no Sketch, o desfoque / gradiente começaria após um deslocamento de 10). Então eles realmente não combinam, certo?
Stephan,
1
@Senseful Estou interessado em saber como você alcançou essa fórmula ?!
Hashem Aboonajmi
2
Você se esqueceu de definir maskToBounds = false.
ScottyBlades
1
Como mencionado acima pelo ScottyBlades, isso não funcionará sem maskToBounds = false. Um exemplo em que falhará é ao aplicar essa sombra a um UIButton.
José
5
Isso deveria ser shadowRadius = blur / UIScreen.main.scaleoposto à codificação permanente blur / 2.0? Ou há outro motivo para dividir por 2? @matchifang Você descobriu isso?
JWK de
59

Você pode tentar isso .... você pode brincar com os valores. O shadowRadiusdetermina a quantidade de desfoque. shadowOffsetdita para onde vai a sombra.

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

Exemplo com propagação

Exemplo com propagação

Para criar uma sombra básica

    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

Exemplo básico de sombra em Swift 2.0

RESULTADO

O-mkar
fonte
2
A informação está errada, o deslocamento não é um controle de propagação, é um controle de xe y.
Quantaliinuxite de
@Quantaliinuxite eu acidentalmente troquei os comentários Eu atualizei as respostas
O-mkar
Certo, e agora vem o cerne da minha pergunta: como faço para controlar a propagação?
Quantaliinuxite
@Quantaliinuxite Código atualizado agora muda 2.1 para a quantidade de propagação que você precisa
O-mkar
Obrigado pela resposta. Aqui não consigo entender o shadowOpacity (por que ele é usado).
Sunita
11

Sketch Shadow usando IBDesignable e IBInspectable no Swift 4

ESBOÇO E CÓDIGO X LADO A LADO

Shadow Exampl

CÓDIGO

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

RESULTADO

DEMO SAÍDA

COMO USÁ-LO

DEMO

O-mkar
fonte
Você não deveria voltar layer.shadowRadius * 2para o shadowBlur getter
berkuqo
7

Este código funcionou muito bem para mim:

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
Cesare
fonte
O valor de desfoque no Sketch não corresponde ao raio da sombra do Core Animation.
CIFilter
O que acabou funcionando perto o suficiente para nós foi reduzir pela metade o valor de desfoque no raio de sombra do Sketch for Core Animation.
CIFilter
2

Para aqueles que estão tentando aplicar uma sombra a um caminho predefinido (como para uma visão circular, por exemplo), acabei com o seguinte:

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

Vou postar alguns exemplos mais tarde, mas funcionou bem em visualizações circulares para mim.

Zig
fonte
Este "quase" funciona, mas a propagação está errada quando você usa uma propagação de 1com xe y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). Nesse caso, a sombra tem um deslocamento, mas não deveria. Aqui o triângulo deve ter a 1px sombra espalhou em todas as direções ibb.co/YjwRb9B
Jan
1

Minha solução com base neste post responde: (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
Josep Escobar
fonte
0

Gostei muito da resposta postada aqui e das sugestões nos comentários. Foi assim que modifiquei essa solução:

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

USO:

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

Pode ser um pouco complicado na história, mas talvez alguns tenham o mesmo problema. Usei amostra de código da resposta aceita. No entanto, os efeitos são bastante diferentes: - o valor de y deve ser cerca da metade em comparação com o mesmo valor no esboço - tentei aplicar sombra na barra de navegação e o efeito é terrivelmente diferente - quase invisível usando os mesmos valores que o esboço tinha.

Portanto, parece que o método não reflete totalmente os parâmetros do esboço. Alguma dica?

Piotr Gawłowski
fonte
Isso não é uma resposta - os comentários vão nos comentários!
Dave Y