Почему masksToBounds = YES предотвращает тень CALayer? - PullRequest
80 голосов
/ 11 сентября 2010

С помощью следующего фрагмента я добавляю эффект падающей тени к одному из моих 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 ]

163 голосов
/ 11 сентября 2010

Поскольку тень - это эффект, выполняемый вне представления, а то, что для masksToBounds установлено значение YES, UIView будет указывать не рисовать ничего, что находится вне его.это с 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];
16 голосов
/ 24 марта 2013

Сейчас 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];
4 голосов
/ 05 августа 2017

Это вариант ответа Swift 3 и IB, разработанный @ TheSquad.

Я использовал ту же концепцию при внесении изменений в файл раскадровки. Сначала я переместил свой 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.

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

3 голосов
/ 20 сентября 2017

Swift 3.0 версия с StoryBoard

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

1. Создать вид под фактическим видом

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

enter image description here

2. Теперь свяжите новый вид с вашим кодом, добавьте на него тень

Это всего лишь образец. Здесь вы можете делать все, что захотите

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 голосов
/ 25 августа 2015

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

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;
1 голос
/ 28 ноября 2017

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

     @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

...