Jak kontrolować rozprzestrzenianie się i rozmycie cienia?

113

Zaprojektowałem elementy interfejsu użytkownika w szkicu, a jeden z nich ma cień z rozmyciem 1 i rozłożeniem 0. Spojrzałem na dokument pod kątem właściwości warstwy widoków, a warstwa nie ma nic o nazwie rozprzestrzenianie się lub rozmycie, ani nic równoważnego (jedyną kontrolką było Tylko shadowOpacity) Jak można kontrolować takie rzeczy, jak rozmycie i rozprzestrzenianie się?

EDYTOWAĆ:

Oto moje ustawienia w Sketch: A oto, jak powinien wyglądać mój cień:Szkicuj ustawienia cienia


Poszukiwany cień




A oto jak to wygląda w tej chwili: Uwaga, aby zobaczyć cień, musisz kliknąć zdjęcie.

Aktualny cień

Mój kod wygląda następująco:

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
}
Quantaliinuxite
źródło
Tagi ios (platforma), projekt (użycie programu Sketch) i Core-Graphics (można użyć UIBezierPath do narysowania cienia, co może być istotne) są istotne, nie rozumiem, dlaczego powinny zostać usuniętym.
Quantaliinuxite
chcesz cienia tylko dla tego białego widoku, prawda?
O-mkar
5
Wygląda na to, że struktury Sketch i CoreAnimation mają różne metryki, ponieważ zawsze wyglądają inaczej w iOS i Sketch z tymi samymi parametrami.
Timur Bernikovich
Ach. Radość z pracy z projektantami, którzy używają narzędzi, które niewiele lub wcale nie przypominają sposobu działania iOS. Jeśli przejdziesz do czegoś takiego jak PaintCode zamiast Sketch, będzie działać nie tylko tak, jak działa iOS, ale także da ci kod, którego potrzebujesz. :-)
Fogmeister
A co jeśli ustawiłeś promień i rozmycie w szkicu?
Vladimir

Odpowiedzi:

259

Oto jak zastosować wszystkie 6 właściwości cienia szkicu do warstwy UIView z niemal idealną dokładnością:

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

Powiedzmy, że chcemy reprezentować:

wprowadź opis obrazu tutaj

Możesz to łatwo zrobić poprzez:

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

lub bardziej zwięźle:

myView.layer.applySketchShadow(y: 0)

Przykład:

wprowadź opis obrazu tutaj

Po lewej: zrzut ekranu iPhone'a 8 UIView; po prawej: szkicuj prostokąt.

Uwaga:

  • Używając wartości niezerowej spread, koduje ścieżkę na stałe w oparciu o boundsCALayer. Jeśli granice warstwy kiedykolwiek się zmienią, zechcesz ponownie wywołać applySketchShadow()metodę.
Rozsądne
źródło
Hm. W programie Sketch, jeśli zastosujesz rozkładówkę, „wysunie się”, gdzie zaczyna się rozmycie / gradient cienia. Co oznacza - pozostaje w stałym kolorze cienia, dopóki nie zacznie się rozmycie. Ale jeśli przesuniemy shadowLayer dookoła, cień zacznie się tylko od tej warstwy, prawda? (Powiedzmy, że rozrzut wynosi 10, cień zacznie się dopiero po przesunięciu o 10, podczas gdy tak jak w szkicu, rozmycie / gradient zacznie się po przesunięciu o 10). Więc tak naprawdę nie pasują, prawda?
Stephan
1
@ Rozsądny Jestem zainteresowany tym, jak osiągnąłeś ten wzór?
Hashem Aboonajmi
2
Zapomniałeś ustawić maskToBounds = false.
ScottyBlades
1
Jak wspomniano powyżej przez ScottyBlades, bez tego nie zadziała maskToBounds = false. Przykładem niepowodzenia jest zastosowanie tego cienia do pliku UIButton.
José
5
Czy powinno to być shadowRadius = blur / UIScreen.main.scaleprzeciwieństwem twardego kodowania blur / 2.0? A może jest jeszcze jeden powód, aby podzielić przez 2? @matchifang Czy już to rozgryzłeś?
JWK
59

Możesz spróbować ... możesz bawić się wartościami. W shadowRadiusdyktuje ilość rozmycia. shadowOffsetdyktuje, dokąd idzie cień.

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

Przykład z rozszerzeniem

Przykład z rozszerzeniem

Aby stworzyć podstawowy cień

    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

Podstawowy przykład Shadow w Swift 2.0

WYNIK

O-mkar
źródło
2
Informacja jest nieprawidłowa, offset nie jest kontrolą spreadu, jest kontrolą x i y.
Quantaliinuxite
@Quantaliinuxite i przypadkowo zamieniłem komentarze Zaktualizowałem odpowiedzi
O-mkar
Racja, a teraz pojawia się sedno mojego pytania: jak kontrolować rozprzestrzenianie się?
Quantaliinuxite
@Quantaliinuxite Zaktualizowany kod zmienia teraz 2.1 na ilość potrzebnego spreadu
O-mkar
Dziękuję za odpowiedź. Tutaj nie jestem w stanie zrozumieć shadowOpacity (dlaczego jest używany).
Sunita
11

Szkicuj cień za pomocą IBDesignable i IBInspectable w Swift 4

SKETCH I XCODE STRONA PO STRONIE

Przykład cienia

KOD

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

WYNIK

WYJŚCIE DEMO

JAK TEGO UŻYĆ

PRÓBNY

O-mkar
źródło
Czy nie powinieneś wracać layer.shadowRadius * 2po shadowBlur getter
berkuqo
7

Ten kod działał dla mnie bardzo dobrze:

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
źródło
Wartość rozmycia w programie Sketch nie odpowiada promieniu cienia w programie Core Animation.
CIFilter
Ostatecznie działało wystarczająco blisko, aby zmniejszyć o połowę wartość rozmycia w promieniu cienia programu Sketch for Core Animation.
CIFilter
2

Dla tych, którzy próbują zastosować cień do wstępnie zdefiniowanej ścieżki (na przykład w przypadku widoku kołowego), oto co otrzymałem:

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

Później opublikuję kilka przykładów, ale to zadziałało w przypadku widoków kołowych.

Zig
źródło
To „prawie” działa, ale rozrzut jest nieprawidłowy, gdy używasz rozprzestrzeniania się 1z xi y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). W tym przypadku cień ma przesunięcie, a nie powinien. Tutaj trójkąt powinien mieć rozrzut cienia 1 piksel
styczeń
1

Moje rozwiązanie oparte na tym poście odpowiada: (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
źródło
0

Bardzo podoba mi się zamieszczona tutaj odpowiedź i sugestie w komentarzach. Tak zmodyfikowałem to rozwiązanie:

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

STOSOWANIE:

myButton.applyShadow(color: .black, alpha: 0.2, x: 0, y: 1, blur: 2, spread: 0)
budidino
źródło
-1

Może to trochę zagłębić się w historię, ale może niektórzy mieli ten sam problem. Użyłem przykładowego kodu z zaakceptowanej odpowiedzi. Jednak efekty są zupełnie inne: - wartość y musi być około połowy w porównaniu z tą samą wartością w szkicu - próbowałem nałożyć cień na pasek nawigacyjny i efekt jest strasznie inny - ledwo widoczny przy tych samych wartościach, które miał szkic.

Wydaje się więc, że metoda całkowicie nie odzwierciedla parametrów szkicu. Jakieś wskazówki?

Piotr Gawłowski
źródło
To nie jest odpowiedź - komentarze pojawiają się w komentarzach!
Dave Y