Bir UIView altında nasıl gölge çizerim?


352

Bir UIViewkakao dokunuşunun alt kenarının altına bir gölge çizmeye çalışıyorum . CGContextSetShadow()Gölgeyi çizmek için kullanmam gerektiğini anlıyorum , ancak Quartz 2D programlama kılavuzu biraz belirsiz:

  1. Grafik durumunu kaydedin.
  2. CGContextSetShadowUygun değerleri ileterek işlevi çağırın .
  3. Gölge uygulamak istediğiniz tüm çizimi gerçekleştirin.
  4. Grafik durumunu geri yükleme

Bir UIViewalt sınıfta aşağıdakileri denedim :

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    CGContextRestoreGState(currentContext);
    [super drawRect: rect];
}

.. ama bu benim için işe yaramıyor ve ben (a) bir sonraki adımda nereye gideceğim ve (b) UIViewbu işi yapmak için yapmam gereken bir şey varsa ?

Yanıtlar:


98

Geçerli kodunuzda, GStategeçerli içeriği kaydeder, bir gölge çizecek şekilde yapılandırırsınız ve bir gölge çizmek için yapılandırmadan önce eski haline geri yüklersiniz. Son olarak, üst sınıfın drawRect:

Gölge ayarı ihtiyaçları etkileneceğine Herhangi çizim gerçekleşmesi sonrasında

CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);

ama önce

CGContextRestoreGState(currentContext);

Eğer üst sınıfların drawRect:bir gölgeye 'sarılmasını' istiyorsanız, kodunuzu bu şekilde yeniden düzenlerseniz ne olur?

- (void)drawRect:(CGRect)rect {
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);
    CGContextSetShadow(currentContext, CGSizeMake(-15, 20), 5);
    [super drawRect: rect];
    CGContextRestoreGState(currentContext);
}

789

Çok daha kolay bir yaklaşım, başlatma hakkındaki görünümün bazı katman niteliklerini ayarlamaktır:

self.layer.masksToBounds = NO;
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

QuartzCore'u içe aktarmanız gerekir.

#import <QuartzCore/QuartzCore.h>

4
Ancak bunun yalnızca iOS 3.2 ve sonraki sürümlerinde çalıştığını unutmayın, bu nedenle uygulamanız eski sürümde çalışacaksa, bir seçenekse Christian'ın çözümünü veya görünümün arkasındaki statik bir görüntüyü kullanmanız gerekir.
florianbuerger

119
Bu çözüm aynı zamanda #import <QuartzCore/QuartzCore.h>".h dosyasına eklenmeyi gerektirir .
MusiGenesis

26
Ayar masksToBoundsiçin NOboşa çıkaracak cornerRadiushayır?
pixelfreak

4
Tamam, bunu çözmek için backgroundColor katman üzerinde ayarlanmalı ve görünümün şeffaf olması gerekir.
pixelfreak

@pixelfreak Bunu nasıl yapıyorsunuz? Denedim self.layer.backgroundColor = [[UIColor whiteColor] CGColor];ama şans yok. Hangi görüşün şeffaf olması gerekir?
Victor Van Hee

232
self.layer.masksToBounds = NO;
self.layer.cornerRadius = 8; // if you like rounded corners
self.layer.shadowOffset = CGSizeMake(-15, 20);
self.layer.shadowRadius = 5;
self.layer.shadowOpacity = 0.5;

Bu uygulamayı yavaşlatır. Aşağıdaki satırı eklemek, görünümünüz görünür şekilde dikdörtgen olduğu sürece performansı artırabilir:

self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;

1
Muhtemelen bu optimizasyonun yalnızca görünümünüz gözle görülür bir şekilde dikdörtgense faydalıdır.
Benjamin Dobell

1
self.layer.shadowPath ... ne yerine? ya da sadece ekleyerek
Chrizzz

2
Bu ek satırı diğerlerine ek olarak eklemeniz yeterli.
christophercotton

11
@NathanGaskin - gölgeler çizmek pahalı bir işlemdir, bu nedenle örneğin uygulamanız diğer arabirim yönlendirmesine izin veriyorsa ve gölgenin yolunu açıkça belirtmeden aygıtı döndürmeye başlarsanız, bu animasyon sırasında birkaç kez görüntülenmesi gerekir; şekli, muhtemelen animasyonu gözle görülür biçimde yavaşlatır
Peter Pajchl

9
@BenjaminDobell Bu satır yalnızca dikdörtgenler için kullanılabilir, ancak dikdörtgen olmayan yollar da oluşturabilirsiniz. Örneğin, yuvarlatılmış bir dikdörtgeniniz varsa bezierPathWithRoundedRect: cornerRadius'u kullanabilirsiniz:
Dan Dyer

160

Aynı çözüm, ama sadece size hatırlatmak için: Gölgeyi doğrudan film şeridinde tanımlayabilirsiniz.

Ör:

resim açıklamasını buraya girin


2
Ne yazık ki, CGColor'un film şeridinden ulaşılamayacağını düşünüyorum :(
Antzi

1
sadece bir kategori tanımlayın UIViewya da mülk için CGLayerbir UIColorCGColor
sarıcıdır

1
Bu bağlantıda bunun nasıl yapılacağı anlatılmaktadır: cocoadventures.org/post/104137872754/…
Kamchatka

8
İnsanların kopyalayıp yapıştırmalarını kolaylaştırmak için: layer.masksToBound, layer.shadowOffset, layer.shadowRadius, layer.shadowOpacity. Typos burada seni öldürecek.
etayluz

3
Kapasite değeri 0,5 değil 0,5 olmalıdır
Desert Rose

43

Bunu deneyebilirsiniz .... değerlerle oynayabilirsiniz. shadowRadiusDikte bulanıklık miktarı. shadowOffsetgölgenin nereye gittiğini belirler.

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

Forma ile örnek

Forma ile örnek

Temel bir gölge oluşturmak için

    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

Swift 2.0'da Temel Gölge örneği

ÇIKTI


19

Interface Builder'ı kullanarak basit ve temiz bir çözüm

Projenize UIView.swift adlı bir dosya ekleyin (veya herhangi bir dosyaya yapıştırın):

import UIKit

@IBDesignable extension UIView {

    /* The color of the shadow. Defaults to opaque black. Colors created
    * from patterns are currently NOT supported. Animatable. */
    @IBInspectable var shadowColor: UIColor? {
        set {
            layer.shadowColor = newValue!.CGColor
        }
        get {
            if let color = layer.shadowColor {
                return UIColor(CGColor:color)
            }
            else {
                return nil
            }
        }
    }

    /* The opacity of the shadow. Defaults to 0. Specifying a value outside the
    * [0,1] range will give undefined results. Animatable. */
    @IBInspectable var shadowOpacity: Float {
        set {
            layer.shadowOpacity = newValue
        }
        get {
            return layer.shadowOpacity
        }
    }

    /* The shadow offset. Defaults to (0, -3). Animatable. */
    @IBInspectable var shadowOffset: CGPoint {
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
    }

    /* The blur radius used to create the shadow. Defaults to 3. Animatable. */
    @IBInspectable var shadowRadius: CGFloat {
        set {
            layer.shadowRadius = newValue
        }
        get {
            return layer.shadowRadius
        }
    }
}

Daha sonra bu, Yardımcı Programlar Paneli> Özellikler Denetçisindeki her görünüm için Arayüz Oluşturucu'da kullanılabilir:

Yardımcı Programlar Paneli

Şimdi gölgeyi kolayca ayarlayabilirsiniz.

Notlar:
- Gölge IB'de görünmez, yalnızca çalışma zamanında.
- Mazen Kasser'in dediği gibi

Bunu çalıştırmayı başaramayanlar için [...] Klip Alt Görünümlerinin ( clipsToBounds) etkin olmadığından emin olun


Bu doğru cevaptır - gelecekteki arayüzler için kod üzerinde konfigürasyon
Crake

Bu çözüm beni aşağıdaki uyarı iletisiyle (her mülk için bir tane) çalışmayan bir davranışa götürür:Failed to set (shadowColor) user defined inspected property on (UICollectionViewCell): [<UICollectionViewCell> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key shadowColor.
Xvolks

1
Çalıştırmak için almak zorunda kaldı UIKit, FoundationXCode tarafından oluşturulan temel ithalat yeterli değil, ama derler iyi. Tüm kaynak kodunu kopyalamam gerekirdi. Axel bu güzel çözüm için teşekkürler.
Xvolks

13

Bunu benim araçlarımın bir parçası olarak kullanıyorum. Bununla sadece gölge ayarlayamaz, aynı zamanda herhangi biri için yuvarlak bir köşe elde edebiliriz UIView. Ayrıca hangi renk gölgesini tercih edeceğinizi de ayarlayabilirsiniz. Normalde siyah tercih edilir, ancak bazen arka plan beyaz olmadığında başka bir şey isteyebilirsiniz. İşte kullandığım -

in utils.m
+ (void)roundedLayer:(CALayer *)viewLayer 
              radius:(float)r 
              shadow:(BOOL)s
{
    [viewLayer setMasksToBounds:YES];
    [viewLayer setCornerRadius:r];        
    [viewLayer setBorderColor:[RGB(180, 180, 180) CGColor]];
    [viewLayer setBorderWidth:1.0f];
    if(s)
    {
        [viewLayer setShadowColor:[RGB(0, 0, 0) CGColor]];
        [viewLayer setShadowOffset:CGSizeMake(0, 0)];
        [viewLayer setShadowOpacity:1];
        [viewLayer setShadowRadius:2.0];
    }
    return;
}

Bunu kullanmak için şunu çağırmalıyız - [utils roundedLayer:yourview.layer radius:5.0f shadow:YES];


7

Hızlı 3

extension UIView {
    func installShadow() {
        layer.cornerRadius = 2
        layer.masksToBounds = false
        layer.shadowColor = UIColor.black.cgColor
        layer.shadowOffset = CGSize(width: 0, height: 1)
        layer.shadowOpacity = 0.45
        layer.shadowPath = UIBezierPath(rect: bounds).cgPath
        layer.shadowRadius = 1.0
    }
}

Eğer bu yönteme gidiyorduysanız, dinamik bir şekilde değerleri ayarlayabilmeniz için parametreler eklemeyi
Josh O'Connor

Bana yuvarlak köşe vermiyor, yanlış bir şey mi yapıyorum?
Nikhil Manapure

@NikhilManapure hiçbir şey olmuyor, bunun nedeni fonksiyonun installShadow()çağrılmamış olması viewDidLoad()veyaviewWillAppear()
neoneye

Aşırı yüklenmiş drawyöntemden çağırıyordum . Gölge doğru geliyor ama yuvarlak köşe gelmiyor.
Nikhil Manapure

@NikhilManapure yuvarlatılmış kenarlık yok, bunun nedeni görünümün UIStackViewyalnızca düzen yapan ve görünümü olmayan bir durum olması olabilir . Her UIViewşey için bir kapsayıcı olarak bir normal eklemeniz gerekebilir .
neoneye

6

StoryBoard'u kullanmak istiyorsanız ve çalışma zamanı niteliklerinde yazmaya devam etmek istemiyorsanız, görünümlere kolayca bir uzantı oluşturabilir ve bunları storyboard'da kullanılabilir hale getirebilirsiniz.

1. Adım. Uzantı oluşturma

extension UIView {

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

@IBInspectable var shadowOpacity: Float {
    get {
        return layer.shadowOpacity
    }
    set {
        layer.shadowOpacity = newValue
    }
}

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

@IBInspectable var maskToBound: Bool {
    get {
        return layer.masksToBounds
    }
    set {
        layer.masksToBounds = newValue
    }
}
}

adım 2. Artık bu özellikleri film şeridinde kullanabilirsinizfilm şeridi görüntüsü


3

Buradaki tüm yanıtları denedikten sonra (kendim gibi!) Çalışmayı başaramayanlara , Nitelikler denetçisinde Klip Alt Görüşlerinin etkinleştirilmediğinden emin olun ...


1

Hızlı 3

self.paddingView.layer.masksToBounds = false
self.paddingView.layer.shadowOffset = CGSize(width: -15, height: 10)
self.paddingView.layer.shadowRadius = 5
self.paddingView.layer.shadowOpacity = 0.5

1

Gölge ve köşe yarıçapı için oluşturulan yardımcı program işlevimi aşağıdaki gibi kullanabilirsiniz:

- (void)addShadowWithRadius:(CGFloat)shadowRadius withShadowOpacity:(CGFloat)shadowOpacity withShadowOffset:(CGSize)shadowOffset withShadowColor:(UIColor *)shadowColor withCornerRadius:(CGFloat)cornerRadius withBorderColor:(UIColor *)borderColor withBorderWidth:(CGFloat)borderWidth forView:(UIView *)view{

    // drop shadow
    [view.layer setShadowRadius:shadowRadius];
    [view.layer setShadowOpacity:shadowOpacity];
    [view.layer setShadowOffset:shadowOffset];
    [view.layer setShadowColor:shadowColor.CGColor];

    // border radius
    [view.layer setCornerRadius:cornerRadius];

    // border
    [view.layer setBorderColor:borderColor.CGColor];
    [view.layer setBorderWidth:borderWidth];
}

Size yardımcı olacağını umuyoruz !!!


1

Hepsi iyi cevap ama bir puan daha eklemek istiyorum

Tablo hücreleriniz olduğunda bir sorunla karşılaşırsanız, yeni bir hücreyi deque gölgesinde bir uyuşmazlık var, bu durumda gölge kodunuzu her koşulda güzel davranması için bir layoutSubviews yöntemine yerleştirmeniz gerekir.

-(void)layoutSubviews{
    [super layoutSubviews];

    [self.contentView setNeedsLayout];
    [self.contentView layoutIfNeeded];
    [VPShadow applyShadowView:self];
}

veya belirli bir görünüm için ViewControllers'da gölge kodu aşağıdaki yöntemin içine yerleştirin, böylece iyi çalışır

-(void)viewDidLayoutSubviews{
    [super viewDidLayoutSubviews];

    [self.viewShadow layoutIfNeeded];
    [VPShadow applyShadowView:self.viewShadow];
}

Daha genel bir form için yeni geliştiriciler için gölge uygulamamı değiştirdim, örneğin:

/*!
 @brief Add shadow to a view.

 @param layer CALayer of the view.

 */
+(void)applyShadowOnView:(CALayer *)layer OffsetX:(CGFloat)x OffsetY:(CGFloat)y blur:(CGFloat)radius opacity:(CGFloat)alpha RoundingCorners:(CGFloat)cornerRadius{
    UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:cornerRadius];
    layer.masksToBounds = NO;
    layer.shadowColor = [UIColor blackColor].CGColor;
    layer.shadowOffset = CGSizeMake(x,y);// shadow x and y
    layer.shadowOpacity = alpha;
    layer.shadowRadius = radius;// blur effect
    layer.shadowPath = shadowPath.CGPath;
}

1

Diğer Xamarians için, cevabın Xamarin.iOS / C # sürümü aşağıdaki gibi görünecektir:

public override void DrawRect(CGRect area, UIViewPrintFormatter formatter)
{
    CGContext currentContext = UIGraphics.GetCurrentContext();
    currentContext.SaveState();
    currentContext.SetShadow(new CGSize(-15, 20), 5);
    base.DrawRect(area, formatter);
    currentContext.RestoreState();                
}

Temel fark, CGContextdoğrudan uygun yöntemleri çağırdığınız bir örneği edinmenizdir.


1

ExtensionGölge eklemek için bunu kullanabilirsiniz

extension UIView {

    func addShadow(offset: CGSize, color: UIColor, radius: CGFloat, opacity: Float)
    {
        layer.masksToBounds = false
        layer.shadowOffset = offset
        layer.shadowColor = color.cgColor
        layer.shadowRadius = radius
        layer.shadowOpacity = opacity

        let backgroundCGColor = backgroundColor?.cgColor
        backgroundColor = nil
        layer.backgroundColor =  backgroundCGColor
    }
}

şöyle diyebilirsin

your_Custom_View.addShadow(offset: CGSize(width: 0, height: 1), color: UIColor.black, radius: 2.0, opacity: 1.0)

0

Swift 4'te IBDesignable ve IBInsecectable Kullanılarak Çizim Gölgesi

BU NASIL KULLANILIR

DEMO

YAN GÖRE kroşe ve XCODE

Gölge Örneği

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

ÇIKTI

DEMO ÇIKIŞI


Gölge öneklerini özniteliklerden kaldırarak kodu geliştirebilirsiniz. ShadowView'un amacı gölge operasyonu hakkında açıktır. Bir şey daha var, bu sınıfa köşe yarıçapı ekleyebilirsiniz. (Bugün, çoğu gölge görünümü köşe yarıçapını içerir)
matematik
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.