Apa cara terbaik untuk menambahkan drop shadow ke UIView saya

108

Saya mencoba untuk menambahkan bayangan jatuh ke tampilan yang berlapis di atas satu sama lain, tampilan runtuh memungkinkan konten di tampilan lain untuk dilihat, dalam hal ini saya ingin tetap view.clipsToBoundsAKTIF sehingga ketika tampilan menciutkan, konten mereka terpotong.

Hal ini tampaknya membuat saya sulit untuk menambahkan drop shadow ke lapisan seperti saat saya clipsToBoundsMENGAKTIFKAN bayangan juga terpotong.

Saya telah mencoba untuk memanipulasi view.framedan view.boundsuntuk menambahkan bayangan jatuh ke bingkai tetapi membiarkan batasnya cukup besar untuk melingkupinya, namun saya tidak beruntung dengan ini.

Berikut adalah kode yang saya gunakan untuk menambahkan Shadow (ini hanya berfungsi dengan clipsToBoundsOFF seperti yang ditunjukkan)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;

Berikut adalah tangkapan layar dari bayangan yang sedang diterapkan ke lapisan abu-abu paling terang atas. Semoga ini memberi gambaran tentang bagaimana konten saya akan tumpang tindih jika clipsToBoundsNONAKTIF.

Aplikasi Bayangan.

Bagaimana cara menambahkan bayangan ke saya UIViewdan menjaga konten saya tetap terpotong?

Sunting: Hanya ingin menambahkan bahwa saya juga telah bermain-main dengan menggunakan gambar latar belakang dengan bayangan aktif, yang berfungsi dengan baik, namun saya masih ingin mengetahui solusi kode terbaik untuk ini.

Wez
sumber

Jawaban:

280

Coba ini:

UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;

Pertama-tama : The UIBezierPathused as shadowPathsangat penting. Jika Anda tidak menggunakannya, Anda mungkin tidak melihat perbedaan pada awalnya, tetapi mata yang tajam akan mengamati kelambatan tertentu yang terjadi selama acara seperti memutar perangkat dan / atau serupa. Ini adalah perubahan kinerja yang penting.

Mengenai masalah Anda secara khusus : Yang terpenting adalah view.layer.masksToBounds = NO. Ini menonaktifkan pemotongan sublapisan lapisan tampilan yang memperpanjang lebih jauh dari batas tampilan.

Bagi mereka yang bertanya-tanya apa perbedaan antara masksToBounds(pada lapisan) dan properti tampilan itu sendiri clipToBounds: Sebenarnya tidak ada. Beralih satu akan berdampak pada yang lain. Hanya tingkat abstraksi yang berbeda.


Swift 2.2:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.blackColor().CGColor
    layer.shadowOffset = CGSizeMake(0.0, 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.CGPath
}

Cepat 3:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.cgPath
}
pkluz.dll
sumber
1
Terima kasih, saya mencoba kode Anda dan kemudian mencoba menambahkan masksToBounds = NO;ke aslinya - dengan kedua upaya saya tetap clipsToBounds = YES;AKTIF - keduanya gagal untuk memotong konten. berikut adalah screencap tentang apa yang terjadi dengan contoh Anda> youtu.be/tdpemc_Xdps
Wez
1
Adakah ide tentang cara membuat drop shadow memeluk sudut yang membulat daripada menampilkan seolah-olah sudutnya masih persegi?
John Erck
7
@JohnErck coba ini: UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:5.0];. Tidak diuji tetapi harus memberikan hasil yang Anda inginkan.
pkluz
1
Saya telah mempelajari bahwa saat menganimasikan tampilan, bayangan meninggalkan jejak abu-abu saat bergerak. Menghapus garis shadowPath memecahkan masalah ini
ishahak
1
@ sepatu karena ukuran waktu tampilan berubah, layoutSubviews()dipanggil. Dan jika kita tidak mengimbangi di tempat itu dengan menyesuaikan shadowPathuntuk mencerminkan ukuran saat ini, kita akan memiliki tampilan yang diubah ukurannya tetapi bayangannya akan tetap menjadi ukuran (awal) yang lama dan tidak muncul atau mengintip di tempat yang tidak semestinya. .
pkluz
65

Jawaban Wasabii di Swift 2.3:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.blackColor().CGColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.CGPath

Dan di Swift 3/4/5:

let shadowPath = UIBezierPath(rect: view.bounds)
view.layer.masksToBounds = false
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
view.layer.shadowOpacity = 0.2
view.layer.shadowPath = shadowPath.cgPath

Letakkan kode ini di layoutSubviews () jika Anda menggunakan AutoLayout.

Di SwiftUI, ini semua jauh lebih mudah:

Color.yellow  // or whatever your view
    .shadow(radius: 3)
    .frame(width: 200, height: 100)
Bart van Kuik
sumber
11
Terima kasih telah mencatatlayoutSubviews
Islam Q.
1
atau bahkan lebih baik dalam viewDidLayoutSubviews ()
Max MacLeod
1
lebih memilih penginisialisasi struct cepat daripada varian Make, yaituCGSize(width: 0, height: 0.5)
Oliver Atkinson
Saya menambahkan kode ini ke layoutSubviews (), itu masih tidak dirender di IB. Apakah ada pengaturan lain yang harus saya pilih?
keledai
Terima kasih. Saya menggunakan Tata Letak Otomatis dan saya berpikir sendiri - yah .. view.boundsbelum dibuat begitu jelas sehingga shadowPathtidak bisa mereferensikan tampilan persegi tetapi beberapa CGRectZerosebagai gantinya. Bagaimanapun, meletakkan ini di bawah layoutSubviewsmenyelesaikan masalah saya akhirnya!
devdc
13

Triknya adalah menentukan masksToBoundsproperti lapisan tampilan Anda dengan benar:

view.layer.masksToBounds = NO;

dan itu harus berhasil.

( Sumber )

sergio
sumber
13

Anda dapat membuat ekstensi untuk UIView untuk mengakses nilai-nilai ini di editor desain

Opsi bayangan di editor desain

extension UIView{

    @IBInspectable var shadowOffset: CGSize{
        get{
            return self.layer.shadowOffset
        }
        set{
            self.layer.shadowOffset = newValue
        }
    }

    @IBInspectable var shadowColor: UIColor{
        get{
            return UIColor(cgColor: self.layer.shadowColor!)
        }
        set{
            self.layer.shadowColor = newValue.cgColor
        }
    }

    @IBInspectable var shadowRadius: CGFloat{
        get{
            return self.layer.shadowRadius
        }
        set{
            self.layer.shadowRadius = newValue
        }
    }

    @IBInspectable var shadowOpacity: Float{
        get{
            return self.layer.shadowOpacity
        }
        set{
            self.layer.shadowOpacity = newValue
        }
    }
}
Chris Stillwell
sumber
bagaimana merujuk ke ekstensi itu di pembuat antarmuka
Amr Angry
IBInspectable membuatnya tersedia di pembuat antarmuka
Nitesh
Bisakah saya mencapai ini di Objective C juga?
Harish Pathak
2
jika Anda ingin memiliki pratinjau waktu nyata (dalam pembuat antarmuka), di sini Anda dapat menemukan artikel bagus tentangnya spin.atomicobject.com/2017/07/18/swift-interface-builder
medskill
7

Anda juga dapat mengatur bayangan ke tampilan Anda dari storyboard

masukkan deskripsi gambar di sini

pallavi
sumber
2

On viewWillLayoutSubviews:

override func viewWillLayoutSubviews() {
    sampleView.layer.masksToBounds =  false
    sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
    sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
    sampleView.layer.shadowOpacity = 1.0
}

Menggunakan Ekstensi UIView:

extension UIView {

    func addDropShadowToView(targetView:UIView? ){
        targetView!.layer.masksToBounds =  false
        targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
        targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
        targetView!.layer.shadowOpacity = 1.0
    }
}

Pemakaian:

sampleView.addDropShadowToView(sampleView)
AG
sumber
1
Cukup tegakkan targetView: UIViewdan lepaskan poni.
bluebamboo
6
Mengapa tidak digunakan self? Sepertinya jauh lebih nyaman bagi saya.
LinusGeffarth
3
Cara yang lebih baik untuk melakukannya adalah dengan menghapus targetView sebagai parameter dan menggunakan self, bukan targetView. Ini menghilangkan redundansi dan memungkinkan seseorang untuk memanggilnya seperti ini: sampleView.addDropShadowToView ()
maxcodes
Komentar Max Nelson bagus. Saran saya adalah gunakan if letsintaks daripada!
Johnny
0

Jadi ya, Anda harus memilih properti shadowPath untuk kinerja, tetapi juga: Dari file header CALayer.shadowPath

Menentukan jalur secara eksplisit menggunakan properti ini biasanya akan * meningkatkan kinerja rendering, seperti halnya berbagi referensi jalur * yang sama di beberapa lapisan

Trik yang kurang dikenal adalah berbagi referensi yang sama di beberapa lapisan. Tentu saja mereka harus menggunakan bentuk yang sama, tetapi ini umum terjadi pada sel tampilan tabel / koleksi.

Saya tidak tahu mengapa ini menjadi lebih cepat jika Anda berbagi contoh, saya kira itu menyimpan rendering bayangan dan dapat menggunakannya kembali untuk contoh lain dalam tampilan. Saya ingin tahu apakah ini bahkan lebih cepat dengan

Rufus Mall
sumber