Почему masksToBounds = да предотвращает CALayer тень?

со следующим фрагментом я добавляю эффект тени к одному моему UIView. Который работает довольно хорошо. Но как только я установлю вид masksToBounds свойство да. Эффект тени больше не отображается.

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;

у вас есть какие-либо идеи по этому поводу?

6 ответов


потому что тень-это эффект, выполняемый вне представления,и что masksToBounds, установленный в YES, скажет UIView не рисовать ничего, что находится вне его.

Если вы хотите вид roundedCorner с тенью, я предлагаю вам сделать это с 2 видами:

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

теперь это iOS 6, все могло измениться. Ответ TheSquad не работает для меня, пока мне не удалось добавить еще одну строку view2.layer.masksToBounds = NO;, в противном случае тень не отображается. Хотя документация говорит masksToBounds по умолчанию нет, мой код показывает обратное.

вот как я делаю кнопку с закругленным углом с тенью, которая является одним из наиболее часто используемых фрагментов кода в моем приложении.

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

редактировать

если необходимо анимировать или прокручивать представления, masksToBounds = YES налоговая производительность значительно, что означает, что анимация, вероятно, будет заикаться. Чтобы получить закругленный угол и тень и плавную анимацию или прокрутку, используйте следующий код:

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

Это Swift 3 и IBDesignable версия ответа, опубликованного @TheSquad.

я использовал ту же концепцию при внесении изменений в файл Storyboard. Сначала я передвинул свой адррес targetview (тот, который требует углового радиуса и тени) внутри нового containerView. Затем я добавил следующие строки кода (ссылка:https://stackoverflow.com/a/35372901/419192), чтобы добавить некоторые атрибуты IBDesignable для UIView Класс:

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

после добавления этого кода я вернулся к раскадровке и выбрал свой containerView теперь я мог найти новый набор атрибутов в инспекторе атрибутов:

enter image description here

кроме добавления значений для этих атрибутов по моему выбору, я также добавил радиус угла к моему адррес targetview и установите свойство masksToBounds как true.

надеюсь, это поможет:)


Swift 3.0 версия с раскадровкой

та же идея с @TheSquad. Создайте новый вид под фактическим видом и добавьте тень в Нижний вид.

1. Создайте представление под фактическим представлением

перетащите UIView в раскадровку с тем же ограничением, что и ваш целевой вид. Проверьте клип на привязку для целевого вида. Также убедитесь, что новое представление указано перед целевым представлением, чтобы целевое представление охватывало новое вид.

enter image description here

2. Теперь свяжите новый вид с вашим кодом add add shadow on it

Это просто пример. Вы можете делать все, что вы хотите здесь

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

У меня также были серьезные проблемы с производительностью с тенями и закругленными углами. Вместо того, чтобы использовать часть shadowPath, я использовал следующие строки, которые отлично решили хит производительности:

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

вот одно из решений:

     @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