2012-03-18 4 views
92

Sto provando ad aggiungere un'ombra esterna alle viste che sono sovrapposte l'una sull'altra, le viste si accartocciano permettendo di vedere il contenuto in altre viste, in questo modo io vuoi mantenere view.clipsToBounds ON in modo che quando le viste comprimono il loro contenuto viene troncato.Qual è il modo migliore per aggiungere un'ombra esterna al mio UIView

Questo sembra aver reso difficile per me aggiungere un'ombra esterna ai livelli come quando giro clipsToBounds Anche le ombre sono troncate.

ho cercato di manipolare view.frame e view.bounds al fine di aggiungere un'ombra al telaio, ma consentono i limiti di essere sufficientemente ampia da contenere, però non ho avuto fortuna con questo.

Ecco il codice che sto usando per aggiungere un ombra (funziona solo con clipsToBounds OFF come illustrato)

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

Ecco un'immagine dell'ombra viene applicata al leggero strato superiore grigio. Spero che questo dia un'idea di come il mio contenuto si sovrapporrà se clipsToBounds è OFF.

Shadow Application.

Come posso aggiungere un'ombra alla mia UIView e mantenere il mio contenuto ritagliato?

Edit: Volevo solo aggiungere che ho anche giocato con l'utilizzo di immagini di sfondo con ombre su, che funziona bene, tuttavia mi piacerebbe comunque conoscere la migliore soluzione codificata per questo.

risposta

250

Prova questo:

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; 

Prima di tutto: Il UIBezierPath utilizzato come shadowPath è fondamentale. Se non la usi, all'inizio potresti non notare una differenza, ma l'occhio acuto osserverà un certo ritardo durante eventi come la rotazione del dispositivo e/o simili. È un importante miglioramento delle prestazioni.

Per quanto riguarda il problema specifico: La linea è importante view.layer.masksToBounds = NO. Disattiva il ritaglio di sottolivelli del livello della vista che si estendono oltre i limiti della vista.

Per coloro che si chiedono quale sia la differenza tra masksToBounds (sul livello) e la proprietà della vista clipToBounds: Non ce ne sono davvero. La commutazione di uno avrà un effetto sull'altro. Solo un diverso livello di astrazione.


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

Grazie, ho provato il codice e poi ha cercato di aggiungere 'masksToBounds = NO;' alla mia originale - con entrambi i tentativi ho continuato 'clipsToBounds = YES;' ON - sia riuscito a ritagliare il contenuto. ecco uno screencap di ciò che è successo con il tuo esempio> http://youtu.be/tdpemc_Xdps – Wez

+0

Qualche idea su come ottenere l'ombra esterna per abbracciare un angolo arrotondato anziché renderlo come se l'angolo fosse ancora un quadrato? –

+5

@JohnErck prova questo: 'UIBezierPath * shadowPath = [UIBezierPath bezierPathWithRoundedRect: view.bounds cornerRadius: 5.0];'. Non testato ma dovrebbe fornire il risultato desiderato. – pkluz

11

Il trucco sta definendo la proprietà masksToBounds dello strato della vostra vista correttamente:

view.layer.masksToBounds = NO; 

e dovrebbe funzionare.

(Source)

54

risposta di Wasabii a 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 

E a Swift 3/4:

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 

inserire questo codice in layoutSubviews() se si sta utilizzando layout automatico.

+9

Grazie per aver notato 'layoutSubviews' –

+0

o anche meglio in viewDidLayoutSubviews() –

+0

Perché è meglio? –

0

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

Uso Estensione del 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 
    } 
} 

Usage:

sampleView.addDropShadowToView(sampleView) 
+1

Imponi semplicemente 'targetView: UIView' e rimuovi la frangetta. – bluebamboo

+0

targetView: UIView? è usato per eliminare l'errore nullo. –

+4

Perché non usare 'self'? Sembra molto più conveniente per me. – LinusGeffarth

1

È possibile creare un'estensione per UIView per accedere a questi valori nell'editor di progettazione

Shadow options in design editor

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

come fare riferimento a quell'estensione nel generatore di interfacce –

+0

IBInspectable lo rende disponibile nel builder dell'interfaccia – Nitesh