Як контролювати поширення і розмиття тіней?


113

Я створив елементи інтерфейсу в ескізі, і один з них має тінь з розмиттям 1 і розширенням 0. Я переглянув документ для властивості шару перегляду, а шар не має нічого, що називається спред або розмиття, або щось подібне (єдиним елементом управління було просто тіньOpacity) Як можна керувати такими речами, як розмиття та поширення?

Редагувати:

Ось мої налаштування в ескізі: І ось я хочу, щоб моя тінь виглядала так:Налаштування тіні ескізу


Тінь хотіла




І ось як це виглядає на даний момент: Зауважте, ви повинні натиснути на картинку, щоб насправді побачити тінь.

Поточна тінь

Мій код такий:

func setupLayer(){
    view.layer.cornerRadius = 2
    view.layer.shadowColor = Colors.Shadow.CGColor
    view.layer.shadowOffset = CGSize(width: 0, height: 1)
    view.layer.shadowOpacity = 0.9
    view.layer.shadowRadius = 5
}

Теги ios (платформа), дизайн (використання програмного Sketch) та Core-Graphics (можна використовувати UIBezierPath для малювання тіні, що може бути актуальним) є всіма релевантними, я не розумію, чому вони повинні бути видаленим.
Quantaliinuxite

ви хочете, щоб тінь для цього білого виду була правильною?
О-мкар

5
Схоже, рамки Sketch та CoreAnimation мають різні показники, оскільки це завжди виглядає по-різному в iOS та в Sketch з тими ж парамами.
Тимур Бернікович

Ага. Радість роботи з дизайнерами, які використовують інструменти, майже не схожі на спосіб iOS. Якщо ви перейдете на щось на зразок PaintCode замість Sketch, він не буде працювати лише як iOS, але і надасть вам код, який вам також потрібен. :-)
Fogmeister

Що робити, якщо ви встановили і радіус, і розмиття в ескізі?
Володимир

Відповіді:


257

Ось як застосувати всі 6 властивостей тіні Sketch до шару UIView з майже ідеальною точністю:

extension CALayer {
  func applySketchShadow(
    color: UIColor = .black,
    alpha: Float = 0.5,
    x: CGFloat = 0,
    y: CGFloat = 2,
    blur: CGFloat = 4,
    spread: CGFloat = 0)
  {
    shadowColor = color.cgColor
    shadowOpacity = alpha
    shadowOffset = CGSize(width: x, height: y)
    shadowRadius = blur / 2.0
    if spread == 0 {
      shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

Скажімо, ми хочемо представити наступне:

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

Ви можете легко зробити це за допомогою:

myView.layer.applySketchShadow(
  color: .black, 
  alpha: 0.5, 
  x: 0, 
  y: 0, 
  blur: 4, 
  spread: 0)

або більш лаконічно:

myView.layer.applySketchShadow(y: 0)

Приклад:

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

Зліва: скріншот iPhone 8 UIView; праворуч: Ескіз прямокутник.

Примітка:

  • При використанні ненульового значення spreadвін жорстко boundsкодує шлях, заснований на CALayer. Якщо межі шару колись змінюються, ви хочете знову зателефонувати за applySketchShadow()методом.

Гм. У програмі "Ескіз", якщо ви застосовуєте спред, він "переміщується", де починається розмиття / градієнт тіні. Що означає - він залишається на постійному тіньовому кольорі, поки не почне розмиття. Але якщо ми перемістимо тінь шару навколо, тінь почалася б лише з цього шару, правда? (Скажімо, поширення 10, тінь розпочнеться лише після зміщення 10, де, як і в ескізі, розмиття / градієнт розпочнеться після зміщення 10). Тож вони насправді не відповідають, правда?
Стефан

1
@Senseful Мені цікаво знати, як ви досягли цієї формули ?!
Хашем Абоонамі

2
Ви забули встановити maskToBounds = false.
ScottyBlades

1
Як вже було сказано ScottyBlades, без цього не обійдеться maskToBounds = false. Приклад, коли це не вдасться, - це застосування цієї тіні до UIButton.
Хосе

5
Чи повинно це бути shadowRadius = blur / UIScreen.main.scaleна відміну від жорсткого кодування blur / 2.0? Або є інша причина ділити на 2? @matchifang Ви це зрозуміли?
JWK

59

Ви можете спробувати це .... Ви можете грати зі значеннями. У shadowRadiusдиктує ступінь розмиття. shadowOffsetдиктує, куди йде тінь.

Швидкий 2.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height))
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.blackColor().CGColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.CGPath

Swift 3.0

let radius: CGFloat = demoView.frame.width / 2.0 //change it to .height if you need spread for height 
let shadowPath = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 2.1 * radius, height: demoView.frame.height)) 
//Change 2.1 to amount of spread you need and for height replace the code for height

demoView.layer.cornerRadius = 2
demoView.layer.shadowColor = UIColor.black.cgColor
demoView.layer.shadowOffset = CGSize(width: 0.5, height: 0.4)  //Here you control x and y
demoView.layer.shadowOpacity = 0.5
demoView.layer.shadowRadius = 5.0 //Here your control your blur
demoView.layer.masksToBounds =  false
demoView.layer.shadowPath = shadowPath.cgPath

Приклад із поширенням

Приклад із поширенням

Щоб створити основну тінь

    demoView.layer.cornerRadius = 2
    demoView.layer.shadowColor = UIColor.blackColor().CGColor
    demoView.layer.shadowOffset = CGSizeMake(0.5, 4.0); //Here your control your spread
    demoView.layer.shadowOpacity = 0.5 
    demoView.layer.shadowRadius = 5.0 //Here your control your blur

Основний приклад тіней у Swift 2.0

ВИХІД


2
Інформація неправильна, зсув - це не контроль розповсюдження, це контроль x і y.
Quantaliinuxite

@Quantaliinuxite я випадково змінив коментарі, я оновив відповіді
O-mkar

Правильно, і тепер виникає суть мого питання: як я можу контролювати поширення?
Quantaliinuxite

@Quantaliinuxite Оновлений код тепер змінити 2,1 на необхідну кількість розвороту
O-mkar

Дякую за відповідь. Тут я не в змозі зрозуміти shadowOpacity (чому його використовують).
Суніта

11

Ескіз Тінь за допомогою IBDesignable та IBInspectable у Swift 4

ЕЛЕКТРИЧНИЙ СКЕТ І ХРИСТОВИЙ БОК

Тіньовий приклад

КОД

@IBDesignable class ShadowView: UIView {

    @IBInspectable var shadowColor: UIColor? {
        get {
            if let color = layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
        set {
            if let color = newValue {
                layer.shadowColor = color.cgColor
            } else {
                layer.shadowColor = nil
            }
        }
    }

    @IBInspectable var shadowOpacity: Float {
        get {
            return layer.shadowOpacity
        }
        set {
            layer.shadowOpacity = newValue
        }
    }

    @IBInspectable var shadowOffset: CGPoint {
        get {
            return CGPoint(x: layer.shadowOffset.width, y:layer.shadowOffset.height)
        }
        set {
            layer.shadowOffset = CGSize(width: newValue.x, height: newValue.y)
        }

     }

    @IBInspectable var shadowBlur: CGFloat {
        get {
            return layer.shadowRadius
        }
        set {
            layer.shadowRadius = newValue / 2.0
        }
    }

    @IBInspectable var shadowSpread: CGFloat = 0 {
        didSet {
            if shadowSpread == 0 {
                layer.shadowPath = nil
            } else {
                let dx = -shadowSpread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                layer.shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
    }
}

ВИХІД

DEMO вихід

ЯК ВИКОРИСТОВАТИ ЇЇ

DEMO


Якщо ви не повернетесь layer.shadowRadius * 2до тіні для отримання
тіней

7

Цей код працював для мене дуже добре:

yourView.layer.shadowOpacity = 0.2 // opacity, 20%
yourView.layer.shadowColor = UIColor.black.cgColor
yourView.layer.shadowRadius = 2 // HALF of blur
yourView.layer.shadowOffset = CGSize(width: 0, height: 2) // Spread x, y
yourView.layer.masksToBounds = false

Значення розмиття в Sketch не відповідає радіусу тіні Core Animation.
CIFilter

Що в кінцевому підсумку працювало досить близько для нас, то вдвічі зменшилося значення розмиття у Sketch для тіньового радіусу Core Animation.
CIFilter

2

Для тих, хто намагається застосувати тінь до заздалегідь визначеного контуру (наприклад, для кругового перегляду, наприклад), ось що я закінчив:

extension CALayer {
    func applyShadow(color: UIColor = .black,
                     alpha: Float = 0.5,
                     x: CGFloat = 0,
                     y: CGFloat = 2,
                     blur: CGFloat = 4,
                     spread: CGFloat = 0,
                     path: UIBezierPath? = nil) {
        shadowColor = color.cgColor
        shadowOpacity = alpha
        shadowRadius = blur / 2
        if let path = path {
            if spread == 0 {
                shadowOffset = CGSize(width: x, height: y)
            } else {
                let scaleX = (path.bounds.width + (spread * 2)) / path.bounds.width
                let scaleY = (path.bounds.height + (spread * 2)) / path.bounds.height

                path.apply(CGAffineTransform(translationX: x + -spread, y: y + -spread).scaledBy(x: scaleX, y: scaleY))
                shadowPath = path.cgPath
            }
        } else {
            shadowOffset = CGSize(width: x, height: y)
            if spread == 0 {
                shadowPath = nil
            } else {
                let dx = -spread
                let rect = bounds.insetBy(dx: dx, dy: dx)
                shadowPath = UIBezierPath(rect: rect).cgPath
            }
        }
        shouldRasterize = true
        rasterizationScale = UIScreen.main.scale
    }
}

Я опублікую кілька прикладів пізніше, але це спрацювало місце для кругових поглядів для мене.


Це "майже" працює, але спред невірний, коли ви використовуєте спред 1з xі y 0 shapeLayer.applyShadow(color: .black, alpha: 1, x: 0, y: 0, blur: 0, spread: 1, path: path). У цьому випадку тінь має зміщення, тоді як вона не повинна. В даному випадку трикутник повинен мати тінь поширення 1px в усіх напрямках ibb.co/YjwRb9B
Ян

1

Моє рішення на основі відповідей на цю публікацію: (Swift 3)

let shadowPath = UIBezierPath(rect: CGRect(x: -1,
                                           y: -2,
                                           width: target.frame.width + 2,
                                           height: target.frame.height + 2))

target.layer.shadowColor = UIColor(hexString: shadowColor).cgColor
target.layer.shadowOffset = CGSize(width: CGFloat(shadowOffsetX), height: CGFloat(shadowOffsetY))
target.layer.masksToBounds = false
target.layer.shadowOpacity = Float(shadowOpacity)
target.layer.shadowPath = shadowPath.cgPath

0

Мені дуже подобається відповідь, розміщена тут, та пропозиції в коментарях. Ось як я змінив це рішення:

extension UIView {
  func applyShadow(color: UIColor, alpha: Float, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat) {
    layer.masksToBounds = false
    layer.shadowColor = color.cgColor
    layer.shadowOpacity = alpha
    layer.shadowOffset = CGSize(width: x, height: y)
    layer.shadowRadius = blur / UIScreen.main.scale
    if spread == 0 {
      layer.shadowPath = nil
    } else {
      let dx = -spread
      let rect = bounds.insetBy(dx: dx, dy: dx)
      layer.shadowPath = UIBezierPath(rect: rect).cgPath
    }
  }
}

ВИКОРИСТАННЯ:

myButton.applyShadow(color: .black, alpha: 0.2, x: 0, y: 1, blur: 2, spread: 0)

-1

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

Отже, здається, що метод абсолютно не відображає параметри ескізу. Якісь підказки?


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