web-dev-qa-db-ger.com

Warum verhindert maschesToBounds = YES den CALayer-Schatten?

Mit dem folgenden Ausschnitt füge ich meinem UIView einen Schatteneffekt hinzu. Was ziemlich gut funktioniert. Sobald ich jedoch die Option masksToBounds property der Ansicht aufYESgesetzt habe. Der Schatteneffekt 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?

79
jchatard

Da der Schatten ein Effekt ist, der außerhalb der View ausgeführt wird und die auf YES eingestellten maskenToBounds den UIView anweisen, keine Objekte außerhalb der View zu zeichnen.

Wenn Sie eine abgerundete Eckenansicht mit Schatten wünschen, sollten Sie dies mit zwei Ansichten 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];
162
TheSquad

Es ist jetzt iOS 6, die Dinge könnten sich geändert haben. Die Antwort von TheSquad funktioniert nicht für mich, bis ich es geschafft habe, eine weitere Zeile view2.layer.masksToBounds = NO; hinzuzufügen, ansonsten wird der Schatten nicht angezeigt. Obwohl die Dokumentation besagt, dass masksToBounds standardmäßig NEIN ist, zeigt mein Code das Gegenteil. 

So erstelle ich eine Schaltfläche mit abgerundeten Ecken mit 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];

EDIT

Wenn Ansichten animiert oder gescrollt werden müssen, wird die Leistung von masksToBounds = YES erheblich beeinflusst, was dazu führt, dass die Animation wahrscheinlich stottert. Verwenden Sie folgenden Code, um abgerundete Ecken und Schatten zu erhalten UND glatte Animationen oder einen Bildlauf durchzuführen:

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];
16
Philip007

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

Ich habe dasselbe Konzept verwendet, als ich Änderungen in der Storyboard-Datei vorgenommen habe. Zuerst habe ich mein targetView (das, für das Eckenradius und Schatten erforderlich sind) in ein neues containerView verschoben. Dann fügte ich die folgenden Codezeilen hinzu (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. Nachdem ich containerView ausgewählt hatte, konnte ich nun im Attribut-Inspektor einen neuen Satz von Attributen finden:

 enter image description here

Abgesehen von dem Hinzufügen von Werten für diese Attribute nach meiner Wahl, habe ich meinem targetView auch einen Eckenradius hinzugefügt und die masksToBounds-Eigenschaft auf true festgelegt.

Ich hoffe das hilft :)

4
Kushal Ashok

Swift 3.0 Version mit StoryBoard

Dieselbe 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 eine UIView auf das StoryBoard mit derselben Einschränkung wie Ihre Zielansicht. Überprüfen Sie den Clip auf die Zielansicht. Stellen Sie außerdem sicher, dass die neue Ansicht vor der Zielansicht aufgeführt ist, sodass die Zielansicht die neue Ansicht abdeckt.

 enter image description here

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

Dies ist nur ein Beispiel. Sie können hier machen, 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
3
Fangming

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

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

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

 enter image description here

1
Svitlana