Warum verhindert masksToBounds = YES CALayer-Schatten?


82

Mit dem folgenden Snippet füge ich einem meiner UIView einen Schlagschatteneffekt hinzu. Welches funktioniert ziemlich gut. Aber sobald ich die masksToBounds- Eigenschaft der Ansicht auf YES gesetzt habe . Der Schlagschatteneffekt wird nicht mehr gerendert.

self.myView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.myView.layer.shadowOpacity = 1.0;
self.myView.layer.shadowRadius = 10.0;
self.myView.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
self.myView.layer.cornerRadius = 5.0;
self.myView.layer.masksToBounds = YES; // <-- This is causing the Drop shadow to not be rendered
UIBezierPath *path = [UIBezierPath bezierPathWithCurvedShadowForRect:self.myView.bounds];
self.myView.layer.shadowPath = path.CGPath;
self.myView.layer.shouldRasterize = YES;

Hast du irgendwelche Ideen dazu?

Antworten:


165

Da Schatten ein Effekt ist, der außerhalb der Ansicht ausgeführt wird, und dass auf YES gesetzte Masken ToBounds die UIView anweisen, nichts außerhalb von sich selbst zu zeichnen.

Wenn Sie eine roundCorner-Ansicht mit Schatten wünschen, empfehlen wir Ihnen, dies mit zwei Ansichten zu tun:

UIView *view1 = [[UIView alloc] init];
UIView *view2 = [[UIView alloc] init];

view1.layer.cornerRadius = 5.0;
view1.layer.masksToBounds = YES;
view2.layer.cornerRadius = 5.0;
view2.layer.shadowColor = [[UIColor blackColor] CGColor];
view2.layer.shadowOpacity = 1.0;
view2.layer.shadowRadius = 10.0;
view2.layer.shadowOffset = CGSizeMake(0.0f, 0.0f);
[view2 addSubview:view1];
[view1 release];

9
Verdammt! Das macht Sinn!
Jchatard

Vielen Dank ... Es hat mir sehr geholfen. Dankbar für Sie .. :)
innodeasapps

1
Nur eine Anmerkung, stellen Sie sicher, dass die Übersicht [in diesem Fall view2] eine klare Hintergrundfarbe hat, das war sowieso mein Problem
Adam Carter

@GangstaGraham stellen Sie sicher, dass Sie view2 als Unteransicht von view1 hinzufügen.
pxpgraphics

Genau das, was ich brauchte!
Rohan Sanap

17

Es ist jetzt iOS 6, möglicherweise haben sich die Dinge geändert. Die Antwort von TheSquad funktioniert bei mir erst, wenn ich eine weitere Zeile hinzugefügt habe view2.layer.masksToBounds = NO;, da sonst kein Schatten angezeigt wird . Obwohl in der Dokumentation masksToBoundsstandardmäßig NEIN angegeben ist, zeigt mein Code das Gegenteil.

So erstelle ich eine Schaltfläche mit abgerundeten Ecken und Schatten, die zu den am häufigsten verwendeten Codeausschnitten in meiner App gehört.

button.layer.masksToBounds = YES;
button.layer.cornerRadius = 10.0f;

view.layer.masksToBounds = NO;      // critical to add this line
view.layer.cornerRadius = 10.0f;
view.layer.shadowOpacity = 1.0f;
// set shadow path to prevent horrible performance
view.layer.shadowPath = 
    [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;      

[view addSubview:button];

BEARBEITEN

Wenn Ansichten animiert oder gescrollt werden müssen, ist die masksToBounds = YESSteuerleistung erheblich, was bedeutet, dass die Animation wahrscheinlich stottern wird. Verwenden Sie stattdessen den folgenden Code, um abgerundete Ecken und Schatten zu erhalten UND Animationen oder Bildläufe zu glätten:

button.backgroundColor = [UIColor clearColor];
button.layer.backgroundColor = [UIColor redColor].CGColor;
button.layer.masksToBounds = NO;
button.layer.cornerRadius = 10.0f;

view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:_button.bounds cornerRadius:10.0f].CGPath;
view.layer.shadowOffset = CGSizeMake(0.0f, 4.0f);
view.layer.shadowRadius = 2.0f;
view.layer.masksToBounds = NO;
view.layer.cornerRadius = 10.0f;  

[view addSubview:button];

4
+1 für die erste Antwort. Ihre aktualisierte Antwort funktioniert nicht für abgerundete Ecken. Wenn Sie "masksToBounds" auf "NEIN" setzen, verschwinden die abgerundeten Ecken. Anstatt dies zu ändern, können Sie die Eigenschaft "shouldRasterize" verwenden, um die gute Leistung zu erzielen.
Dinesh Raja

Dies funktioniert jetzt im Jahr 2020. So einfache Lösung, nur eine Zeile hinzuzufügen.
GeneCode

4

Dies ist die Swift 3 und IBDesignable-Version der Antwort von @TheSquad.

Ich habe das gleiche Konzept verwendet, als ich Änderungen an der Storyboard-Datei vorgenommen habe. Zuerst habe ich meine Zielansicht (die Eckradius und Schatten erfordert) in eine neue Containeransicht verschoben . Dann habe ich die folgenden Codezeilen hinzugefügt (Referenz: https://stackoverflow.com/a/35372901/419192 ), um einige IBDesignable-Attribute für die UIView-Klasse hinzuzufügen:

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

/* The corner radius of the view. */
@IBInspectable var cornerRadius: CGFloat {
    set {
        layer.cornerRadius = newValue
    }
    get {
        return layer.cornerRadius
    }
}

Nachdem ich diesen Code hinzugefügt hatte, kehrte ich zum Storyboard zurück und konnte bei Auswahl meiner containerView im Attributinspektor einen neuen Satz von Attributen finden:

Geben Sie hier die Bildbeschreibung ein

Neben dem Hinzufügen von Werten für diese Attribute gemäß meiner Auswahl habe ich meiner targetView auch einen Eckradius hinzugefügt und die Eigenschaft masksToBounds auf true festgelegt.

Ich hoffe das hilft :)


3

Swift 3.0 Version mit StoryBoard

Die gleiche Idee mit @TheSquad. Erstellen Sie eine neue Ansicht unter der tatsächlichen Ansicht und fügen Sie der unteren Ansicht Schatten hinzu.

1. Erstellen Sie eine Ansicht unter der tatsächlichen Ansicht

Ziehen Sie a UIViewmit der gleichen Einschränkung wie Ihre Zielansicht auf StoryBoard. Aktivieren Sie den zu bindenden Clip für die Zielansicht. Stellen Sie außerdem sicher, dass die neue Ansicht vor der Zielansicht aufgeführt ist, damit die Zielansicht die neue Ansicht abdeckt.

Geben Sie hier die Bildbeschreibung ein

2. Verknüpfen Sie nun die neue Ansicht mit Ihrem Code. Fügen Sie Schatten hinzu

Dies ist nur ein Beispiel. Sie können hier tun, was Sie wollen

shadowView.layer.masksToBounds = false
shadowView.layer.shadowColor = UIColor.red.cgColor
shadowView.layer.shadowOpacity = 0.5
shadowView.layer.shadowOffset = CGSize(width: -1, height: 1)
shadowView.layer.shadowRadius = 3

shadowView.layer.shadowPath = UIBezierPath(rect: coverImage.bounds).cgPath
shadowView.layer.shouldRasterize = true

2

Ich hatte auch drastische Leistungsprobleme mit Schatten und abgerundeten Ecken. Anstatt den shadowPath-Teil zu verwenden, habe ich die folgenden Zeilen verwendet, die den Leistungseinbruch perfekt gelöst haben:

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;

Der Bildschirm kann damit jedoch nicht animiert oder weitergeleitet werden.
Van Du Tran

1

Hier ist eine der Lösungen:

     @IBOutlet private weak var blockView: UIView! {
         didSet {
          blockView.backgroundColor = UIColor.white
          blockView.layer.shadowColor = UIColor.black.cgColor
          blockView.layer.shadowOpacity = 0.5
          blockView.layer.shadowOffset = CGSize.zero

          blockView.layer.cornerRadius = 10
        }
      }
      @IBOutlet private weak var imageView: UIImageView! {
        didSet {
          imageView.layer.cornerRadius = 10
          imageView.layer.masksToBounds = true

          imageView.layer.shouldRasterize = true
        }
      }

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.