Wie kann man die Ausbreitung und Unschärfe von Schatten kontrollieren?

112

Ich habe UI-Elemente in einer Skizze entworfen, und eines davon hat einen Schatten mit Unschärfe 1 und Streuung 0. Ich habe im Dokument nach der Eigenschaft Layer für Ansichten gesucht, und die Ebene hat nichts mit dem Namen Streuung oder Unschärfe oder etwas Äquivalentes (das einzige Steuerelement war nur shadowOpacity) Wie kann man Dinge wie Unschärfe und Ausbreitung kontrollieren?

BEARBEITEN:

Hier sind meine Einstellungen in Sketch: Und so soll mein Schatten aussehen:Skizzieren Sie Schatteneinstellungen


Schatten wollte




Und so sieht es im Moment aus: Beachten Sie, dass Sie auf das Bild klicken müssen, um den Schatten tatsächlich zu sehen.

Aktueller Schatten

Mein Code lautet wie folgt:

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
}
Quantaliinuxit
quelle
Die Tags ios (die Plattform), das Design (die Verwendung der Software Sketch) und Core-Graphics (es ist möglich, einen UIBezierPath zu verwenden, um den Schatten zu zeichnen, der relevant sein könnte) sind alle relevant, ich verstehe nicht, warum sie sollten entfernt werden.
Quantaliinuxite
Du willst nur Schatten für diese weiße Ansicht, oder?
O-mkar
5
Es sieht so aus, als hätten Sketch und CoreAnimation Framework unterschiedliche Metriken, da es unter iOS und in Sketch mit denselben Parametern immer anders aussieht.
Timur Bernikovich
Ah. Die Freude, mit Designern zusammenzuarbeiten, die Tools verwenden, die wenig oder gar keine Ähnlichkeit mit der Funktionsweise von iOS haben. Wenn Sie zu etwas wie PaintCode anstelle von Sketch wechseln, funktioniert es nicht nur wie iOS, sondern gibt Ihnen auch den Code, den Sie benötigen. :-)
Fogmeister
Was ist, wenn Sie sowohl den Radius als auch die Unschärfe in der Skizze festgelegt haben?
Vlad

Antworten:

256

So wenden Sie alle 6 Sketch-Schatteneigenschaften mit nahezu perfekter Genauigkeit auf die Ebene eines UIView an:

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

Angenommen, wir möchten Folgendes darstellen:

Geben Sie hier die Bildbeschreibung ein

Sie können dies einfach tun über:

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

oder prägnanter:

myView.layer.applySketchShadow(y: 0)

Beispiel:

Geben Sie hier die Bildbeschreibung ein

Links: iPhone 8 UIView-Screenshot; rechts: Skizzenrechteck.

Hinweis:

  • Bei Verwendung einer Nicht-Null spreadwird ein Pfad basierend auf dem boundsdes CALayer fest codiert. Wenn sich die Grenzen der Ebene jemals ändern, möchten Sie die applySketchShadow()Methode erneut aufrufen .
Sinnvoll
quelle
Hm. Wenn Sie in Sketch Spread anwenden, wird es nach außen verschoben, wo die Unschärfe / der Farbverlauf des Schattens beginnt. Das heißt - es bleibt bei der konstanten Schattenfarbe, bis die Unschärfe beginnt. Aber wenn wir die Schattenschicht bewegen, würde der Schatten nur von dieser Ebene aus beginnen, oder? (Angenommen, die Streuung ist 10, der Schatten würde erst nach einem Versatz von 10 beginnen, während wie in Sketch die Unschärfe / der Gradient nach einem Versatz von 10 beginnen würde.) Sie passen also nicht wirklich zusammen, oder?
Stephan
1
@Senseful Ich bin interessiert zu wissen, wie Sie diese Formel erreicht haben?!
Hashem Aboonajmi
2
Du hast vergessen zu setzen maskToBounds = false.
ScottyBlades
1
Wie oben von ScottyBlades erwähnt, funktioniert dies nicht ohne maskToBounds = false. Ein Beispiel, bei dem dies fehlschlägt, ist das Anwenden dieses Schattens auf ein UIButton.
José
5
Sollte dies shadowRadius = blur / UIScreen.main.scaleim Gegensatz zu Hardcodierung sein blur / 2.0? Oder gibt es einen anderen Grund, durch 2 zu teilen? @matchifang Hast du das herausgefunden?
JWK
59

Sie können dies versuchen ... Sie können mit den Werten spielen. Das bestimmt shadowRadiusdie Menge der Unschärfe. shadowOffsetdiktiert, wohin der Schatten geht.

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

Beispiel mit Spread

Beispiel mit Spread

So erstellen Sie einen einfachen Schatten

    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

Grundlegendes Schattenbeispiel in Swift 2.0

AUSGABE

O-mkar
quelle
2
Die Informationen sind falsch, Offset ist keine Kontrolle der Streuung, sondern eine Kontrolle von x und y.
Quantaliinuxite
@Quantaliinuxite Ich habe versehentlich die Kommentare ausgetauscht. Ich habe die Antworten aktualisiert
O-mkar
Richtig, und jetzt kommt der Kern meiner Frage: Wie kontrolliere ich die Ausbreitung?
Quantaliinuxite
@Quantaliinuxite Der aktualisierte Code ändert jetzt 2.1 in die Menge an Spread, die Sie benötigen
O-mkar
Danke für die Antwort. Hier kann ich shadowOpacity nicht verstehen (warum es verwendet wird).
Sunita
11

Skizzenschatten mit IBDesignable und IBInspectable in Swift 4

SKIZZE UND XCODE SEITE FÜR SEITE

Schattenbeispiel

CODE

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

AUSGABE

DEMO-AUSGANG

WIE MAN ES BENUTZT

DEMO

O-mkar
quelle
Sollten Sie nicht layer.shadowRadius * 2für shadowBlur getter
berkuqo
7

Dieser Code hat bei mir sehr gut funktioniert:

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
quelle
Der Unschärfewert in Sketch stimmt nicht mit dem Schattenradius von Core Animation überein.
CIFilter
Was für uns nahe genug kam, war die Halbierung des Unschärfewerts in Sketch for Core Animations Schattenradius.
CIFilter
2

Für diejenigen, die versuchen, einen Schatten auf einen vordefinierten Pfad anzuwenden (wie zum Beispiel für eine Kreisansicht), habe ich Folgendes erhalten:

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

Ich werde später einige Beispiele veröffentlichen, aber dies hat für mich bei kreisförmigen Ansichten genau funktioniert.

Zig
quelle
Dies funktioniert "fast", aber der Spread ist falsch, wenn Sie einen Spread von 1mit xund verwenden y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). In diesem Fall hat der Schatten einen Versatz, während dies nicht der Fall sein sollte. Hier sollte das Dreieck hat den Schatten Ausbreitung 1px in alle Richtungen ibb.co/YjwRb9B
Jan
1

Meine Lösung basierend auf diesem Beitrag antwortet: (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
quelle
0

Ich mag die hier gepostete Antwort und die Vorschläge in den Kommentaren sehr. So habe ich diese Lösung geändert:

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

VERWENDUNG:

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

Es mag ein wenig in der Geschichte sein, aber vielleicht hatten einige das gleiche Problem. Ich habe ein Codebeispiel aus einer akzeptierten Antwort verwendet. Die Effekte sind jedoch sehr unterschiedlich: - Der y-Wert muss ungefähr halb so groß sein wie der gleiche Wert in der Skizze. - Ich habe versucht, Schatten auf die Navigationsleiste anzuwenden, und der Effekt ist schrecklich unterschiedlich - kaum sichtbar, wenn dieselben Werte wie in der Skizze verwendet werden.

Es scheint also, dass die Methode die Skizzenparameter überhaupt nicht widerspiegelt. Irgendwelche Hinweise?

Piotr Gawłowski
quelle
Dies ist keine Antwort - Kommentare gehen in Kommentare!
Dave Y