Чому masksToBounds = YES запобігає тіні CALayer?


83

За допомогою наступного фрагмента я додаю ефект тіні до одного мого UIView. Що працює досить добре. Але як тільки я встановив для властивості view 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;

У вас є якісь ідеї з цього приводу?

Відповіді:


166

Оскільки тінь - це ефект, зроблений за межами подання, і якщо masksToBounds, встановлений у ТАК, скаже 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];

9
Проклятий! Що має сенс!
jchatard

Щиро дякую ... Це мені дуже допомогло. Вдячний вам .. :)
innodeasapps

1
Тільки примітка, переконайтесь, що у суперпрегляду [view2 у даному випадку] є чіткий колір фону, це все одно була моя проблема
Адам Картер

@GangstaGraham переконайтеся, що ви додаєте view2 як підпрогляд 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];

4
+1 за першу відповідь. Ваша оновлена ​​відповідь не буде працювати для закруглених кутів .. Якщо ви встановите для "masksToBounds" значення "НІ", то округлі кути зникнуть. Замість того, щоб змінити, що ви можете використовувати властивість "shouldRasterize", щоб отримати хорошу продуктивність.
Dinesh Raja

Це працює зараз у 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. Створіть подання під фактичним видом

Перетягніть a 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

2

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

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

Однак не вдається анімувати або передати екран за допомогою цього.
Van Du Tran

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.