UIView'uma gölge eklemenin en iyi yolu nedir


108

Üst üste katmanlanmış görünümlere alt gölge eklemeye çalışıyorum, görünümler çökerek diğer görünümlerdeki içeriğin görülmesine izin veriyor, bu damarda tutmak istiyorum view.clipsToBounds AÇIK böylece görünümler daraldığında içerikleri kırpılır.

Bu, döndüğümde olduğu gibi katmanlara gölge eklememi zorlaştırmış gibi görünüyor. clipsToBounds AÇIK'ı gölgeler de kırpılıyor.

Manipüle etmeye çalışıyordum view.frame veview.boundsÇerçeveye bir gölge eklemek, ancak sınırların onu çevreleyecek kadar geniş olmasına izin vermek , ancak bu konuda hiç şansım olmadı.

İşte Gölge eklemek için kullandığım kod (bu yalnızca clipsToBoundsgösterildiği gibi KAPALI ile çalışır )

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

İşte en açık gri katmana uygulanan gölgenin ekran görüntüsü. Umarım bu clipsToBounds, KAPALI ise içeriğimin nasıl çakışacağına dair bir fikir verir .

Gölge Uygulaması.

Nasıl gölge ekleyebilirim UIViewİçeriğime ve içeriğimi nasıl kırpabilirim?

Düzenleme: Sadece gölgeli arka plan resimlerini kullanarak oynadığımı eklemek istedim, bu iyi çalışıyor, ancak yine de bunun için en iyi kodlanmış çözümü bilmek istiyorum.

Yanıtlar:


280

Bunu dene:

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;

Her şeyden önce : UIBezierPathkullanıldığı gibi shadowPathçok önemlidir. Kullanmazsanız, ilk başta bir fark görmeyebilirsiniz, ancak keskin göz, cihazı döndürme ve / veya benzeri olaylar sırasında meydana gelen belirli bir gecikme gözlemleyecektir. Bu önemli bir performans ayarlaması.

Özellikle sorununuzla ilgili olarak : Önemli olan satır view.layer.masksToBounds = NO. Görünümün katmanlarının görünümün sınırlarından daha fazla uzanan alt katmanlarının kırpılmasını devre dışı bırakır .

masksToBounds(Katmanda) ve görünümün kendi clipToBoundsözelliği arasındaki farkın ne olduğunu merak edenler için: Gerçekten yok. Birini değiştirmek, diğerini etkileyecektir. Sadece farklı bir soyutlama seviyesi.


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
}

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

1
Teşekkürler, kodunuzu denedim ve ardından masksToBounds = NO;orijinalime eklemeyi denedim - her iki denemede de clipsToBounds = YES;AÇIK tuttum - ikisi de içeriği kırpmada başarısız oldu. İşte örneğinizde neler olduğuna dair bir ekran görüntüsü> youtu.be/tdpemc_Xdps
Wez

1
Alt gölgenin köşe hala bir kare gibi görünmesi yerine yuvarlak bir köşeye nasıl sarılacağına dair herhangi bir fikriniz var mı?
John Erck

7
@JohnErck bu deneyin: UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:5.0];. Test edilmemiştir ancak istediğiniz sonucu vermelidir.
pkluz

1
Görünümü canlandırırken gölgenin hareket ederken gri izler bıraktığını öğrendim. ShadowPath satırlarının kaldırılması bunu çözdü
ishahak

1
@shoe, görünümün boyutu her zaman değiştiğinden, layoutSubviews()çağrılır. Ve bu yerde shadowPathmevcut boyutu yansıtacak şekilde ayarlayarak telafi etmezsek, yeniden boyutlandırılmış bir görünüme sahip oluruz, ancak gölge yine de eski (başlangıç) boyutunda olur ve gösterilmemesi veya olmaması gereken yere bakmaz. .
pkluz

65

Wasabii'nin Swift 2.3'teki cevabı:

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

Ve Swift 3/4 / 5'te:

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

AutoLayout kullanıyorsanız bu kodu layoutSubviews () içine yerleştirin.

SwiftUI'de bunların hepsi çok daha kolay:

Color.yellow  // or whatever your view
    .shadow(radius: 3)
    .frame(width: 200, height: 100)

11
layoutSubviews
ettiğiniz

1
veya daha iyi görünümdeDidLayoutSubviews ()
Max MacLeod

1
Değişken yap yerine hızlı struct başlatıcıları tercih et, yaniCGSize(width: 0, height: 0.5)
Oliver Atkinson

Bu kodu layoutSubviews () 'e ekledim, yine de IB'de görüntülenmedi. Seçmem gereken başka ayarlar var mı?
eşek

Teşekkürler. Auto Layout kullanıyordum ve kendi kendime düşündüm - peki .. view.boundso kadar açık bir şekilde yaratılmadı ki shadowPath, görüntünün doğrusuna atıfta bulunamaz, CGRectZeroonun yerine bazılarına atıfta bulunamaz . Her neyse, bunu altına koymak layoutSubviewsen sonunda sorunumu çözdü!
devdc

13

İşin püf noktası masksToBounds, görünümünüzün katmanının özelliğini doğru şekilde tanımlamaktır :

view.layer.masksToBounds = NO;

ve çalışmalıdır.

( Kaynak )


13

Tasarım düzenleyicide bu değerlere erişmek için UIView için bir uzantı oluşturabilirsiniz.

Tasarım düzenleyicide gölge seçenekleri

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

nasıl arayüz oluşturucu bu uzantı atıfta
amr Angry

IBInspectable, arayüz oluşturucuda kullanılabilir hale getirir
Nitesh

Bunu Objective C'de de başarabilir miyim?
Harish Pathak

2
gerçek zamanlı bir önizlemeye sahip olmak istiyorsanız (arayüz oluşturucuda), burada bununla ilgili güzel bir makale bulabilirsiniz spin.atomicobject.com/2017/07/18/swift-interface-builder
medskill

7

Ayrıca film şeridinden görünümünüze gölge de ayarlayabilirsiniz

görüntü açıklamasını buraya girin


2

ViewWillLayoutSubviews'te:

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
}

UIView Uzantısını Kullanma:

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

Kullanımı:

sampleView.addDropShadowToView(sampleView)

1
Sadece targetView: UIViewpatlamaları zorla ve kaldır.
blueb bamboo

6
Neden kullanmıyorsun self? Bana çok daha uygun görünüyor.
LinusGeffarth

3
Bunu yapmanın daha iyi bir yolu, targetView'ı bir parametre olarak kaldırmak ve targetView yerine self'i kullanmaktır. Bu fazlalıkları ortadan kaldırır ve birinin böyle çağırmasına izin verir: sampleView.addDropShadowToView ()
maxcodes

Max Nelson'ın yorumu harika. Benim önerim şu if letyerine sözdizimi kullanmak!
Johnny

0

Yani evet, performans için shadowPath özelliğini tercih etmelisiniz, fakat aynı zamanda: CALayer.shadowPath başlık dosyasından

Bu özelliği kullanarak yolu açıkça belirtmek, birden çok katmanda aynı yolu * referansını paylaşmak gibi genellikle * oluşturma performansını iyileştirir

Daha az bilinen bir numara, aynı referansı birden çok katman arasında paylaşmaktır. Elbette aynı şekli kullanmaları gerekir, ancak bu tablo / koleksiyon görünümü hücrelerinde yaygındır.

Örnekleri paylaşırsanız neden daha hızlı hale geldiğini bilmiyorum, sanırım gölgenin oluşturulmasını önbelleğe alıyor ve görünümdeki diğer örnekler için yeniden kullanabiliyor. Merak ediyorum, bu daha hızlı mı

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.