28

Próbuję narysować cień i promień narożnika na obrazie. Mogę je dodać osobno, ale nie mam możliwości dodania obu efektów jednocześnie. Dodaję cień z:Nie można dodać promienia naroża i cienia

[layer setShadowOffset:CGSizeMake(0, 3)]; 
[layer setShadowOpacity:0.4]; 
[layer setShadowRadius:3.0f]; 
[layer setShouldRasterize:YES]; 

Tutaj warstwa jest CALayerem podklasy UIView. Tak to działa, gdy mogę ustawić

[layer setMasksToBounds:NO]; 

teraz, aby dodać promień naroża to zrobić:

[layer setCornerRadius:7.0f]; 

ale muszę ustawić MasksToBounds na Tak, aby to zadziałało:

[layer setMasksToBounds:YES]; 

Czy mimo to mogę dodać oba te efekty?

Dzięki za poświęcony czas,

Denis

Odpowiedz

57

Tak, tak nie ma ...

Jeśli chcesz zarówno promień naroża i cienia, nie włączać - masksToBounds, ale raczej ustaw promień narożnika i ustaw ścieżkę beziera cienia zaokrągloną prostą. Przechowywać promień dwa takie same:

[layer setShadowOffset:CGSizeMake(0, 3)]; 
[layer setShadowOpacity:0.4]; 
[layer setShadowRadius:3.0f]; 
[layer setShouldRasterize:YES]; 

[layer setCornerRadius:12.0f]; 
[layer setShadowPath: 
        [[UIBezierPath bezierPathWithRoundedRect:[self bounds] 
               cornerRadius:12.0f] CGPath]]; 

może chcesz sprawdzić swoje wyniki bez parametru -shouldRasterize ustalonym raz ustawiasz ścieżkę cienia. Wydajność rysowania wydaje się bardzo dobra po ustawieniu ścieżki cienia.

UPDATE

ja nie spojrzał na ten problem w dość chwilę, ale wydaje się, że nie ma już potrzeby, aby ustawić shadowPath aby uzyskać to do pracy. Po prostu ustawienie cornerRadius i shadowOpacity będzie działać teraz. Myślę, że tak było, odkąd iOS5 (o ile wiem). Zapewnienie tej aktualizacji jest prawdopodobnie niepotrzebne, ponieważ ustawienie tych parametrów "po prostu działa", ale zapewniam to dla dobra potomności. Reasumując, jest to teraz wszystko, czego potrzebujesz:

[layer setShadowOpacity:0.4]; 
[layer setCornerRadius:12.0f]; 

Jeśli trzeba jeszcze lepszą wydajność, można iść do przodu i ustawić parametr shouldRasterize także:

[layer setShouldRasterize:YES]; 

A mówiąc o wydajności, warto zauważając, że jeśli zauważysz powolne animacje, będziesz chciał w końcu użyć techniki ustawiania ścieżki cienia. Ta aktualizacja miała na celu tylko wskazanie, że ustawienie ścieżki nie jest już wymagane, aby uzyskać efekt jednoczesnego wyświetlania zarówno promienia rogu, jak i cienia. Jeśli wydajność jest Twoim priorytetem, użyj ścieżki.

UPDATE 2

Ponieważ ludzie wydają się mieć problemy z dostaniem tego, aby pracować w niektórych przypadkach, będę pisać kod pełniejszy fragment tutaj z przykładowego projektu utworzonego:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    CALayer *layer = [CALayer layer]; 
    [layer setBounds:CGRectMake(0.0f, 0.0f, 100.0f, 200.0f)]; 
    [layer setPosition:[[self view] center]]; 
    [layer setBackgroundColor:[[UIColor lightGrayColor] CGColor]]; 
    [layer setShadowOpacity:0.55f]; 
    [layer setCornerRadius:8.0f]; 
    [layer setBorderWidth:1.0f]; 

    [[[self view] layer] addSublayer:layer]; 

    [[[self testView] layer] setShadowOpacity:0.55f]; 
    [[[self testView] layer] setShadowRadius:15.0f]; 
    [[[self testView] layer] setCornerRadius:8.0f]; 
    [[[self testView] layer] setBorderWidth:1.0f]; 
} 

The testView jest UIView dodałem w Interface Builder i ustaw gniazdka na. Ma to na celu upewnienie się, że działa to samo na obu warstwach, które dodajesz jawnie, jak również na warstwy w podspisach.

Testowałem to na symulatorze dla iOS5 do iOS6.1. Daje to wynik dla mnie w każdym z nich:

enter image description here

+1

Czy jest jakiś powód, aby nie ustawić shouldRasterize na TAK? – memmons

+2

To zależy od tego, jak często aktualizujesz warstwę. Jeśli masz zamiar ustawić go raz i nie aktualizować go ponownie, rasteryzacja pomoże uzyskać wydajność, jeśli animujesz warstwę. Jeśli jednak zawartość warstwy jest często aktualizowana, renderuje ją jako obraz za każdym razem, gdy zmieni się zawartość, co może zaszkodzić twojemu przewijaniu (zakładając, że przewijasz/animujesz). –

+0

Potrzebuje miejsca między ścieżkami UIBezierPath i BezierPathWithRoundedRect. –

-2
view.layer.cornerRadius=4; 
[view.layer setMasksToBounds:YES]; 
[view.layer setShadowColor:SHADOW_COLOR]; 
[view.layer setShadowOpacity:4 ]; 
[view.layer setShadowRadius:4]; 
[view.layer setShadowOffset:0];  
[view.layer setShadowPath: [[UIBezierPath bezierPathWithRoundedRect:[view bounds] cornerRadius:CORNER_RADIUS] CGPath]]; 
view.layer.borderColor=[[UIColor lightGrayColor] colorWithAlphaComponent:.5].CGColor; 
view.layer.borderWidth=.4; 
+2

okropne. nie będzie działać. – Liam

3

Poniższy kod SWIFT 3 pokazuje jak narysować cień i promień rogu na obrazie za pomocą CAShapeLayer i CALayer.

import UIKit 

class ViewController: UIViewController { 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     // constants 
     let radius: CGFloat = 20, offset = 8 
     let rect = CGRect(x: 0, y: 0, width: 200, height: 200) 

     // roundedView 
     let roundedView = UIView() 
     view.addSubview(roundedView) 

     // shadow layer 
     let shadowLayer = CALayer() 
     shadowLayer.shadowColor = UIColor.darkGray.cgColor 
     shadowLayer.shadowPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath 
     shadowLayer.shadowOffset = CGSize(width: offset, height: offset) 
     shadowLayer.shadowOpacity = 0.8 
     shadowLayer.shadowRadius = 2 
     roundedView.layer.addSublayer(shadowLayer) 

     // mask layer 
     let maskLayer = CAShapeLayer() 
     maskLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath 

     // image layer 
     let imageLayer = CALayer() 
     imageLayer.mask = maskLayer 
     imageLayer.frame = rect 
     imageLayer.contentsGravity = kCAGravityResizeAspectFill 
     imageLayer.contents = UIImage(named: "image")?.cgImage 
     roundedView.layer.addSublayer(imageLayer) 

     // auto layout 
     roundedView.translatesAutoresizingMaskIntoConstraints = false 
     roundedView.widthAnchor.constraint(equalToConstant: rect.width).isActive = true 
     roundedView.heightAnchor.constraint(equalToConstant: rect.height).isActive = true 
     roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 
     roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true 
    } 

} 

Ten kod generuje następujący ekran:

enter image description here


Dotychczasowy kod może być refactored do następujących plików SWIFT:

CustomView.swift

import UIKit 

class CustomView: UIView { 

    var imageLayer: CALayer! 
    var image: UIImage? { 
     didSet { refreshImage() } 
    } 

    override var intrinsicContentSize: 


     CGSize { 
     return CGSize(width: 200, height: 200) 
    } 

    func refreshImage() { 
     if let imageLayer = imageLayer, let image = image { 
      imageLayer.contents = image.cgImage 
     } 
    } 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     if imageLayer == nil { 
      let radius: CGFloat = 20, offset: CGFloat = 8 

      let shadowLayer = CALayer() 
      shadowLayer.shadowColor = UIColor.darkGray.cgColor 
      shadowLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath 
      shadowLayer.shadowOffset = CGSize(width: offset, height: offset) 
      shadowLayer.shadowOpacity = 0.8 
      shadowLayer.shadowRadius = 2 
      layer.addSublayer(shadowLayer) 

      let maskLayer = CAShapeLayer() 
      maskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: radius).cgPath 

      imageLayer = CALayer() 
      imageLayer.mask = maskLayer 
      imageLayer.frame = bounds 
      imageLayer.backgroundColor = UIColor.red.cgColor 
      imageLayer.contentsGravity = kCAGravityResizeAspectFill 
      layer.addSublayer(imageLayer) 
     } 


     refreshImage() 
    } 

} 

ViewController.swift

import UIKit 

class ViewController: UIViewController { 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     let roundedView = CustomView() 
     roundedView.translatesAutoresizingMaskIntoConstraints = false 
     view.addSubview(roundedView) 

     // auto layout 
     let horizontalConstraint = roundedView.centerXAnchor.constraint(equalTo: view.centerXAnchor) 
     let verticalConstraint = roundedView.centerYAnchor.constraint(equalTo: view.centerYAnchor) 
     NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint]) 

     roundedView.image = UIImage(named: "image") 
    } 

} 

Można znaleźć więcej sposobów łączyć obrazy z zaokrąglonymi rogami i cieniem na tej Github repo.

2

Ponieważ używam UIButton z obrazem tła, żadna z tych odpowiedzi nie zadziałała. Wciąż nie dostaję żadnych cieni ani żadnych zaokrąglonych krawędzi na moich przyciskach.

Najłatwiej w moim scenariuszu było tylko dodać inny widok za pomocą przycisku i dodać cień do niego tak:

button.clipsToBounds=YES; 
button.layer.cornerRadius = 25; 

UIView *shadowView = [[UIView alloc]initWithFrame:button.frame]; 

shadowView.backgroundColor = [UIColor whiteColor];//needs this to cast shadow 
shadowView.layer.cornerRadius = 25; 
shadowView.clipsToBounds = YES; 
shadowView.layer.masksToBounds = NO; 
shadowView.layer.shadowOffset = CGSizeMake(0, 2); 
shadowView.layer.shadowRadius = 1; 
shadowView.layer.shadowOpacity = 0.2; 


[[button superview]addSubview:shadowView]; 
[[button superview]bringSubviewToFront:button];