Який найкращий спосіб додати тінь для краплі до мого UIView


108

Я намагаюся додати тінь краплі до поглядів, які шаруються один на одного, погляди руйнуються, що дозволяє бачити вміст в інших видах, в цьому руслі я хочу тримати view.clipsToBoundsВКЛ, щоб, коли погляди згорталися, їхній вміст відсікався.

Це, здається, ускладнило мені додавання тіні крапель шарам, як коли я clipsToBoundsвмикаю, тіні також вирізані.

Я намагався маніпулювати view.frameі view.boundsдля того , щоб додати тінь до рами , але дозволяють кордону , щоб бути досить великим , щоб охопити його, проте у мене не було ніякої удачі з цим.

Ось код, який я використовую, щоб додати тінь (це працює лише з clipsToBoundsВИКЛ., Як показано)

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

Ось скріншот тіні, що застосовується до верхнього найлегшого сірого шару. Сподіваємось, це дає уявлення про те, як мій вміст буде перекриватися, якщо його clipsToBoundsнемає.

Тіньовий додаток.

Як я можу додати тінь до свого UIViewі зберегти обрізаний вміст?

Редагувати: Просто хотілося додати, що я також грав разом із використанням фонових зображень із затінками, що добре працює, однак я все одно хотів би знати найкраще кодоване рішення для цього.

Відповіді:


280

Спробуйте це:

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власністю перегляду: насправді не існує. Боротьба з одним вплине на іншу. Просто інший рівень абстракції.


Швидкий 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
}

Швидкий 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
}

1
Дякую, я спробував ваш код, а потім спробував додати masksToBounds = NO;до мого оригіналу - і з обох спроб я clipsToBounds = YES;ввімкнув - обидві не вдалося вирізати вміст. ось скріншот того, що відбувається з вашим прикладом> youtu.be/tdpemc_Xdps
Wez

1
Будь-які ідеї, як змусити краплинку тіні обійняти закруглений кут, а не візуалізувати так, ніби кут як і раніше квадрат?
Джон Ерк

7
@JohnErck спробувати це: UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:5.0];. Не тестується, але повинен дати бажаний результат.
pkluz

1
Я дізнався, що, оживляючи погляд, тінь залишає сірі сліди під час руху. Видалення ліній shadowPath вирішило це
ishahak

1
@shoe, оскільки будь-який час зміни розміру перегляду layoutSubviews()називається. І якщо ми не компенсуємо в цьому місці, налаштовуючи shadowPathна відображення поточного розміру, у нас буде розмір розміру, але тінь все одно буде старим (початковим) розміром і не відображатиметься, або не заглядає туди, де він не повинен .
pkluz

65

Відповідь Васабія в 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/5:

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.

У SwiftUI все це набагато простіше:

Color.yellow  // or whatever your view
    .shadow(radius: 3)
    .frame(width: 200, height: 100)

11
Дякую за те, що зауваживlayoutSubviews
Іслам Q.

1
або ще краще в viewDidLayoutSubviews ()
Макс Маклеод

1
віддайте перевагу швидким ініціалізаторам структур над варіантами Make, тобтоCGSize(width: 0, height: 0.5)
Олівер Аткінсон,

Я додаю цей код до layoutSubviews (), він все ще не відображався в IB. Чи є інші налаштування, які я повинен увімкнути?
осел

Дякую. Я використовував автоматичний макет, і я подумав, що для себе - ну .. view.boundsне створено так очевидно, що shadowPathне може посилатися на пряму зору, а CGRectZeroнатомість. Як би там не було, layoutSubviewsнарешті , це вирішило мою проблему, нарешті!
devdc

13

Трюк полягає в masksToBoundsправильному визначенні властивості шару вашого перегляду:

view.layer.masksToBounds = NO;

і це має працювати.

( Джерело )


13

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

Параметри тіней у редакторі дизайну

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

як посилатися на це розширення в розробнику інтерфейсів
Amr Angry

IBInspectable робить його доступним у програмі для створення інтерфейсів
Нітеш

Чи можу я досягти цього і в Цілі C?
Харіш Патхак

2
якщо ви хочете , щоб мати перегляд в реальному часі (в Interface Builder), тут ви можете знайти гарну статтю про це spin.atomicobject.com/2017/07/18/swift-interface-builder
medskill

7

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

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


2

На viewWillLayoutSubviews:

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)

1
Просто примусьте targetView: UIViewі видаліть чубок.
bluebamboo

6
Чому б не використовувати self? Здається мені набагато зручніше.
LinusGeffarth

3
Кращий спосіб зробити це - видалити targetView як параметр і використовувати self замість targetView. Це виключає надмірність і дозволяє називати його так: sampleView.addDropShadowToView ()
maxcodes

Коментар Макса Нельсона чудовий. Моя пропозиція - використовувати if letсинтаксис замість!
Johnny

0

Так що так, вам слід віддати перевагу властивості shadowPath для продуктивності, але також: Із заголовного файлу CALayer.shadowPath

Зазначення шляху, явно використовуючи це властивість, як правило, * покращить ефективність візуалізації, як і обмін тим самим шляхом * посиланням на кілька шарів

Менш відомий трюк - це обмін однаковими посиланнями на кілька шарів. Звичайно, вони повинні використовувати однакову форму, але це звичайно для комірок подання таблиці / колекції.

Я не знаю, чому це стає швидше, якщо ви ділитесь екземплярами, я здогадуюсь, що це кешування візуалізації тіні і може повторно використовувати його для інших примірників у вікні. Цікаво, чи це ще швидше

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