Come controllare la diffusione e la sfocatura delle ombre?


113

Ho progettato elementi dell'interfaccia utente nello schizzo e uno di essi ha un'ombra con sfocatura 1 e diffusione 0. Ho guardato il documento per la proprietà del livello delle viste e il livello non ha nulla denominato diffusione o sfocatura o qualcosa di equivalente (l'unico controllo era semplicemente shadowOpacity) Come posso controllare cose come sfocatura e diffusione?

MODIFICARE:

Ecco le mie impostazioni in Sketch: Ed ecco come voglio che appaia la mia ombra:Impostazioni dell'ombra dello schizzo


Shadow voleva




Ed ecco come appare al momento: Nota, devi fare clic sull'immagine per vedere effettivamente l'ombra.

Ombra attuale

Il mio codice è il seguente:

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
}

I tag ios (la piattaforma), design (l'uso del software Sketch) e Core-Graphics (è possibile utilizzare un UIBezierPath per disegnare l'ombra, che potrebbe essere rilevante) sono tutti rilevanti, non vedo perché dovrebbero essere rimosso.
Quantaliinuxite

vuoi solo l'ombra per quella vista bianca giusto?
O-mkar

5
Sembra che il framework Sketch e CoreAnimation abbia metriche diverse, perché ha sempre un aspetto diverso su iOS e in Sketch con gli stessi parametri.
Timur Bernikovich

Ah. Il piacere di lavorare con designer che utilizzano strumenti che hanno poca o nessuna somiglianza con il modo in cui funziona iOS. Se ti sposti su qualcosa come PaintCode invece di Sketch, non solo funzionerà come iOS, ma ti darà anche il codice di cui hai bisogno. :-)
Fogmeister

Cosa succede se hai impostato sia il raggio che la sfocatura nello schizzo?
Vladimir

Risposte:


258

Ecco come applicare tutte e 6 le proprietà dell'ombra dello schizzo a un livello di UIView con una precisione quasi perfetta:

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

Supponiamo di voler rappresentare quanto segue:

inserisci qui la descrizione dell'immagine

Puoi farlo facilmente tramite:

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

o più succintamente:

myView.layer.applySketchShadow(y: 0)

Esempio:

inserisci qui la descrizione dell'immagine

A sinistra: screenshot di iPhone 8 UIView; a destra: rettangolo di schizzo.

Nota:

  • Quando si utilizza un valore diverso da zero spread, codifica un percorso basato sul boundsCALayer. Se i limiti del livello cambiano, dovresti chiamare di applySketchShadow()nuovo il metodo.

Hm. In Sketch, se applichi la diffusione, "si sposta", dove inizia la sfocatura / sfumatura dell'ombra. Il che significa che rimane al colore dell'ombra costante, finché non inizia la sfocatura. Ma se spostiamo lo shadowLayer, l'ombra inizierebbe solo da quel layer, giusto? (Supponiamo che la diffusione sia 10, l'ombra inizierebbe solo dopo un offset di 10, dove come in Sketch, la sfocatura / sfumatura inizierebbe dopo un offset di 10). Quindi non corrispondono davvero, giusto?
Stephan

1
@Senseful Sono interessato a sapere come hai ottenuto questa formula ?!
Hashem Aboonajmi

2
Hai dimenticato di impostare maskToBounds = false.
ScottyBlades

1
Come accennato in precedenza da ScottyBlades, questo non funzionerà senza maskToBounds = false. Un esempio in cui fallirà è quando si applica questa ombra a un file UIButton.
José

5
Questo dovrebbe essere shadowRadius = blur / UIScreen.main.scalein contrasto con l'hard-coding blur / 2.0? Oppure c'è un altro motivo per dividere per 2? @matchifang Hai capito?
JWK

59

Puoi provare questo ... puoi giocare con i valori. Il shadowRadiusdetermina la quantità di sfocatura. shadowOffsetdetta dove va l'ombra.

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

Esempio con diffusione

Esempio con diffusione

Per creare un'ombra di base

    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

Esempio di ombra di base in Swift 2.0

PRODUZIONE


2
L'informazione è sbagliata, l'offset non è un controllo della diffusione, è un controllo di x e y.
Quantaliinuxite

@Quantaliinuxite ho scambiato accidentalmente i commenti ho aggiornato le risposte
O-mkar

Bene, e ora arriva il nocciolo della mia domanda: come controllo la diffusione?
Quantaliinuxite

@Quantaliinuxite Il codice aggiornato ora cambia 2.1 alla quantità di spread che ti serve
O-mkar

Grazie per la risposta. Qui non sono in grado di capire shadowOpacity (perché viene utilizzato).
Sunita

11

Disegna l'ombra usando IBDesignable e IBInspectable in Swift 4

SKETCH E XCODE FIANCO A FIANCO

Shadow Exampl

CODICE

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

PRODUZIONE

USCITA DEMO

COME USARLO

DEMO


Non dovresti tornare layer.shadowRadius * 2per shadowBlur getter
berkuqo

7

Questo codice ha funzionato molto bene per me:

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

Il valore di sfocatura in Sketch non corrisponde al raggio dell'ombra di Core Animation.
Filtro CIF

Quello che ha finito per funzionare abbastanza vicino per noi è stato dimezzare il valore di sfocatura in Sketch per il raggio dell'ombra di Core Animation.
CIFilter

2

Per coloro che stanno tentando di applicare un'ombra a un percorso predefinito (come per una vista circolare, ad esempio), ecco cosa ho ottenuto:

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

Pubblicherò alcuni esempi più tardi, ma per me questo ha funzionato perfettamente per le viste circolari.


Questo "quasi" funziona, ma lo spread è sbagliato quando usi uno spread 1con xe y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). In questo caso l'ombra ha un offset mentre non dovrebbe. Qui il triangolo dovrebbe avere l'ombra diffusa di 1px in tutte le direzioni ibb.co/YjwRb9B
Jan

1

La mia soluzione basata su questo post risponde: (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

Mi piace molto la risposta pubblicata qui e i suggerimenti nei commenti. Ecco come ho modificato quella soluzione:

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)

-1

Potrebbe essere un po 'scavato nella storia, ma forse alcuni hanno avuto lo stesso problema. Ho usato un esempio di codice dalla risposta accettata. Tuttavia gli effetti sono abbastanza diversi: - il valore y deve essere circa la metà rispetto allo stesso valore nello schizzo - Ho provato ad applicare l'ombra sulla barra di navigazione e l'effetto è terribilmente diverso - appena visibile usando gli stessi valori dello schizzo.

Quindi sembra che il metodo non rifletta totalmente i parametri dello schizzo. Eventuali suggerimenti?


Questa non è una risposta: i commenti vanno nei commenti!
Dave Y
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.