Bagaimana cara mengontrol shadow spread dan blur?

113

Saya telah mendesain elemen UI dalam sketsa, dan salah satunya memiliki bayangan dengan blur 1 dan spread 0. Saya melihat dokumen untuk properti lapisan tampilan dan lapisan tidak memiliki apa pun yang bernama menyebar atau kabur, atau apa pun yang setara (satu-satunya kontrol adalah just shadowOpacity) Bagaimana cara mengontrol hal-hal seperti blur dan menyebar?

EDIT:

Berikut adalah pengaturan saya di Sketch: Dan inilah yang saya ingin bayangan saya terlihat:Buat sketsa pengaturan bayangan


Bayangan ingin




Dan inilah yang terlihat saat ini: Catatan, Anda harus mengklik gambar untuk benar-benar melihat bayangannya.

Bayangan saat ini

Kode saya adalah sebagai berikut:

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
sumber
Tag ios (platform), desain (penggunaan Sketsa perangkat lunak) dan Core-Graphics (dimungkinkan untuk menggunakan UIBezierPath untuk menggambar bayangan, yang mungkin relevan) semuanya relevan, saya tidak mengerti mengapa mereka harus Dihilangkan.
Quantaliinuxite
Anda hanya menginginkan bayangan untuk tampilan putih itu, bukan?
O-mkar
5
Sepertinya framework Sketch dan CoreAnimation memiliki metrik yang berbeda, karena selalu terlihat berbeda di iOS dan di Sketch dengan parameter yang sama.
Timur Bernikovich
Ah. Kegembiraan bekerja dengan desainer yang menggunakan alat yang sedikit atau tidak mirip dengan cara kerja iOS. Jika Anda pindah ke sesuatu seperti PaintCode dan bukan Sketch, itu tidak hanya akan berfungsi seperti iOS tetapi juga akan memberi Anda kode yang Anda butuhkan. :-)
Fogmeister
Bagaimana jika Anda telah mengatur radius dan blur pada sketsa?
Vladimir

Jawaban:

258

Berikut cara menerapkan semua 6 properti bayangan Sketsa ke lapisan UIView dengan akurasi yang hampir sempurna:

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

Katakanlah kami ingin mewakili yang berikut:

masukkan deskripsi gambar di sini

Anda dapat dengan mudah melakukannya melalui:

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

atau lebih singkatnya:

myView.layer.applySketchShadow(y: 0)

Contoh:

masukkan deskripsi gambar di sini

Kiri: Tangkapan layar iPhone 8 UIView; kanan: Buat sketsa persegi panjang.

catatan:

  • Saat menggunakan bukan nol spread, ini membuat kode jalur berdasarkan pada boundsCALayer. Jika batas lapisan pernah berubah, Anda ingin memanggil applySketchShadow()metode itu lagi.
Senseful
sumber
Hm. Di Sketch, jika Anda menerapkan sebaran, itu 'bergerak keluar', di mana keburaman / gradien bayangan dimulai. Artinya - itu tetap pada warna bayangan yang konstan, sampai keburaman dimulai. Tetapi jika kita memindahkan shadowLayer, bayangan hanya akan dimulai dari lapisan itu, bukan? (Katakanlah spread adalah 10, bayangan hanya akan dimulai setelah offset 10, di mana seperti pada Sketch, blur / gradien akan dimulai setelah offset 10). Jadi mereka tidak benar-benar cocok, bukan?
Stephan
1
@Senseful Saya tertarik untuk mengetahui bagaimana Anda mencapai formula ini ?!
Hashem Aboonajmi
2
Anda lupa menyetelnya maskToBounds = false.
ScottyBlades
1
Seperti disebutkan di atas oleh ScottyBlades, ini tidak akan bekerja tanpanya maskToBounds = false. Contoh di mana itu akan gagal adalah saat menerapkan bayangan ini ke file UIButton.
José
5
Haruskah ini shadowRadius = blur / UIScreen.main.scaleberbeda dengan hard-coding blur / 2.0? Atau, adakah alasan lain untuk membagi dengan 2? @matchifang Apakah Anda mengetahui hal ini?
JWK
59

Anda dapat mencoba ini .... Anda dapat bermain-main dengan nilai. Ini shadowRadiusmenentukan jumlah buram. shadowOffsetmenentukan kemana bayangan pergi.

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

Contoh dengan spread

Contoh dengan spread

Untuk membuat bayangan dasar

    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

Contoh Basic Shadow di Swift 2.0

KELUARAN

O-mkar
sumber
2
Informasinya salah, offset bukan kontrol spread, tapi kontrol x dan y.
Quantaliinuxite
@Quantaliinuxite saya tidak sengaja menukar komentar saya telah memperbarui jawaban
O-mkar
Benar, dan sekarang muncul inti dari pertanyaan saya: bagaimana cara mengontrol penyebaran?
Quantaliinuxite
@Quantaliinuxite Kode yang diperbarui sekarang mengubah 2.1 ke jumlah penyebaran yang Anda butuhkan
O-mkar
Terima kasih atas jawabannya. Di sini saya tidak dapat memahami shadowOpacity (mengapa digunakan).
Sunita
11

Sketsa Bayangan Menggunakan IBDesignable dan IBInspectable di Swift 4

SKETSA DAN XCODE SISI DENGAN SISI

Contoh Bayangan

KODE

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

KELUARAN

OUTPUT DEMO

BAGAIMANA CARA MENGGUNAKANNYA

DEMO

O-mkar
sumber
layer.shadowRadius * 2Tidakkah Anda harus kembali untuk pengambil
shadowBlur
7

Kode ini bekerja dengan sangat baik untuk saya:

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
sumber
Nilai blur di Sketch tidak sesuai dengan radius bayangan Core Animation.
CIFilter
Apa yang akhirnya bekerja cukup dekat bagi kami adalah membagi dua nilai blur dalam radius bayangan Sketch untuk Core Animation.
CIFilter
2

Bagi mereka yang mencoba menerapkan bayangan ke jalur yang telah ditentukan (Seperti untuk tampilan melingkar, misalnya), inilah yang saya dapatkan:

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

Saya akan memposting beberapa contoh nanti, tetapi ini telah berhasil untuk tampilan melingkar bagi saya.

Zig
sumber
Ini "hampir" berfungsi tetapi penyebarannya salah saat Anda menggunakan penyebaran 1dengan xdan y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). Dalam hal ini bayangan memiliki offset sementara seharusnya tidak. Di sini segitiga harus memiliki bayangan menyebar 1px ke segala arah ibb.co/YjwRb9B
Jan
1

Solusi saya berdasarkan balasan posting ini: (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
sumber
0

Saya sangat suka jawaban yang diposting di sini dan saran di komentar. Beginilah cara saya mengubah solusi itu:

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

PEMAKAIAN:

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

Ini mungkin sedikit diggin dalam sejarah, tetapi mungkin beberapa memiliki masalah yang sama. Saya menggunakan sampel kode dari jawaban yang diterima. Namun efeknya sangat berbeda: - nilai y harus sekitar setengah dibandingkan dengan nilai yang sama dalam sketsa - Saya mencoba menerapkan bayangan pada bilah navigasi dan efeknya sangat berbeda - hampir tidak terlihat saat menggunakan nilai yang sama dengan sketsa.

Jadi tampaknya metode tersebut sama sekali tidak mencerminkan parameter sketsa. Ada petunjuk?

Piotr Gawłowski
sumber
Ini bukan jawaban - komentar masuk ke komentar!
Dave Y