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

83

С помощью следующего фрагмента я добавляю эффект тени к одному из моих UIView. Что работает очень хорошо. Но как только я установил свойство masksToBounds представления в YES . Эффект падающей тени больше не отображается.

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;

У вас есть идеи по этому поводу?

jchatard
источник

Ответы:

166

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

Если вам нужен вид с закругленными углами и тенью, я предлагаю вам сделать это с двумя видами:

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];
TheSquad
источник
Большое спасибо ... Мне это очень помогло. Благодарен вам .. :)
innodeasapps
1
Просто примечание, убедитесь, что супервизор [view2 в данном случае] имеет четкий фоновый цвет, в любом случае это была моя проблема
Адам Картер
@GangstaGraham убедитесь, что вы добавляете view2 в качестве subview view1.
pxpgraphics
Именно то, что мне нужно!
Рохан Санап,
17

Сейчас 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];
Philip007
источник
4
+1 за первый ответ. Ваш обновленный ответ не будет работать для закругленных углов. Если вы установите "masksToBounds" на "NO", закругленные углы исчезнут. Вместо того, чтобы менять это, вы можете использовать свойство shouldRasterize для получения хорошей производительности.
Динеш Раджа
Это работает сейчас в 2020 году. Такое простое решение, просто добавить одну строку.
GeneCode
4

Это Swift 3 и IBDesignable версия ответа, опубликованная @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, теперь смог найти новый набор атрибутов в инспекторе атрибутов:

введите описание изображения здесь

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

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

Кушал Ашок
источник
4

Версия Swift 3.0 с StoryBoard

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

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

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

введите описание изображения здесь

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
Fangming
источник
2

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

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = UIScreen.mainScreen.scale;
салоцинкс
источник
Однако с этим нельзя анимировать или ретранслировать экран.
Ван Ду Тран,
1

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

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

введите описание изображения здесь

Светлана
источник