Какой лучший способ добавить тень на мой UIView - PullRequest
103 голосов
/ 18 марта 2012

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

Это, кажется, затруднило мне добавление тени к слоям, так как когда я включаю clipsToBounds ON, тени также обрезаются.

Я пытался манипулировать view.frame и view.bounds, чтобы добавить тень на рамку, но чтобы границы были достаточно большими, чтобы охватить ее, однако мне не повезло с этим.

Вот код, который я использую для добавления тени (это работает только при clipsToBounds OFF, как показано)

view.clipsToBounds = NO;
view.layer.shadowColor = [[UIColor blackColor] CGColor];
view.layer.shadowOffset = CGSizeMake(0,5);
view.layer.shadowOpacity = 0.5;

Вот скриншот тени, накладываемой на самый светлый серый слой. Надеемся, что это дает представление о том, как мой контент будет перекрываться, если clipsToBounds выключен.

Shadow Application.

Как я могу добавить тень к своему UIView и сохранить мой контент обрезанным?

Редактировать: Я просто хотел добавить, что я также поиграл с использованием фоновых изображений с тенями, что хорошо работает, однако я все еще хотел бы знать лучшее кодированное решение для этого.

Ответы [ 7 ]

271 голосов
/ 18 марта 2012

Попробуйте это:

UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:view.bounds];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = shadowPath.CGPath;

Прежде всего : значение UIBezierPath, используемое как shadowPath, имеет решающее значение. Если вы не используете его, вы можете сначала не заметить различий, но острый глаз будет наблюдать определенную задержку, возникающую во время таких событий, как вращение устройства и / или подобное. Это важная настройка производительности.

Относительно вашей проблемы конкретно : Важная строка - view.layer.masksToBounds = NO. Он отключает отсечение подслоев слоя представления , которые простираются дальше границ представления.

Для тех, кому интересно, в чем разница между masksToBounds (на слое) и собственным свойством clipToBounds представления: на самом деле их нет. Переключение одного будет влиять на другое. Просто другой уровень абстракции.


Swift 2.2:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.blackColor().CGColor
    layer.shadowOffset = CGSizeMake(0.0, 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.CGPath
}

Swift 3:

override func layoutSubviews()
{
    super.layoutSubviews()

    let shadowPath = UIBezierPath(rect: bounds)
    layer.masksToBounds = false
    layer.shadowColor = UIColor.black.cgColor
    layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
    layer.shadowOpacity = 0.5
    layer.shadowPath = shadowPath.cgPath
}
62 голосов
/ 12 декабря 2014

Ответ Васабии в Swift 2.3:

    let shadowPath = UIBezierPath(rect: view.bounds)
    view.layer.masksToBounds = false
    view.layer.shadowColor = UIColor.blackColor().CGColor
    view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
    view.layer.shadowOpacity = 0.2
    view.layer.shadowPath = shadowPath.CGPath

И в Swift 3/4:

    let shadowPath = UIBezierPath(rect: view.bounds)
    view.layer.masksToBounds = false
    view.layer.shadowColor = UIColor.black.cgColor
    view.layer.shadowOffset = CGSize(width: 0, height: 0.5)
    view.layer.shadowOpacity = 0.2
    view.layer.shadowPath = shadowPath.cgPath

Поместите этот код в layoutSubviews (), если вы используете AutoLayout.

13 голосов
/ 06 апреля 2017

Вы можете создать расширение для UIView для доступа к этим значениям в редакторе дизайна

Shadow options in design editor

extension UIView{

    @IBInspectable var shadowOffset: CGSize{
        get{
            return self.layer.shadowOffset
        }
        set{
            self.layer.shadowOffset = newValue
        }
    }

    @IBInspectable var shadowColor: UIColor{
        get{
            return UIColor(cgColor: self.layer.shadowColor!)
        }
        set{
            self.layer.shadowColor = newValue.cgColor
        }
    }

    @IBInspectable var shadowRadius: CGFloat{
        get{
            return self.layer.shadowRadius
        }
        set{
            self.layer.shadowRadius = newValue
        }
    }

    @IBInspectable var shadowOpacity: Float{
        get{
            return self.layer.shadowOpacity
        }
        set{
            self.layer.shadowOpacity = newValue
        }
    }
}
13 голосов
/ 18 марта 2012

Трюк правильно определяет свойство masksToBounds слоя вашего вида:

view.layer.masksToBounds = NO;

, и оно должно работать.

( Источник )

6 голосов
/ 22 марта 2018

Вы также можете задать тень для вашего вида из раскадровки

enter image description here

1 голос
/ 13 апреля 2016

По просмотруWillLayoutSubviews:

override func viewWillLayoutSubviews() {
    sampleView.layer.masksToBounds =  false
    sampleView.layer.shadowColor = UIColor.darkGrayColor().CGColor;
    sampleView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
    sampleView.layer.shadowOpacity = 1.0
}

Использование расширения UIView:

extension UIView {

    func addDropShadowToView(targetView:UIView? ){
        targetView!.layer.masksToBounds =  false
        targetView!.layer.shadowColor = UIColor.darkGrayColor().CGColor;
        targetView!.layer.shadowOffset = CGSizeMake(2.0, 2.0)
        targetView!.layer.shadowOpacity = 1.0
    }
}

Использование:

sampleView.addDropShadowToView(sampleView)
0 голосов
/ 04 мая 2019

Так что да, вы должны предпочесть свойство shadowPath для производительности, но также: Из файла заголовка CALayer.shadowPath

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

Менее известная уловка - использование одной и той же ссылки на нескольких уровнях.Конечно, они должны использовать одну и ту же форму, но это характерно для ячеек представления таблицы / коллекции.

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

...