Как контролировать распространение и размытие теней?


113

Я спроектировал элементы пользовательского интерфейса в эскизе, и один из них имеет тень с размытием 1 и растяжением 0. Я просмотрел документ для свойства слоя представлений, и у слоя нет ничего с именем spread или blur или чего-либо подобного (единственным элементом управления был просто shadowOpacity) Как можно управлять такими вещами, как размытие и распространение?

РЕДАКТИРОВАТЬ:

Вот мои настройки в Sketch: А вот как я хочу, чтобы моя тень выглядела:Настройки тени эскиза


Тень хотел




А вот как это выглядит на данный момент: Обратите внимание, вам нужно щелкнуть по картинке, чтобы увидеть тень.

Текущая тень

Мой код выглядит следующим образом:

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

вам нужна тень только для этого белого вида, верно?
O-mkar

5
Похоже, фреймворк Sketch и CoreAnimation имеют разные метрики, потому что он всегда выглядит по-разному в iOS и в Sketch с одинаковыми параметрами.
Тимур Берникович

Ах. Удовольствие от работы с дизайнерами, использующими инструменты, которые почти или совсем не похожи на то, как работает iOS. Если вы перейдете к чему-то вроде PaintCode вместо Sketch, он не только будет работать, как работает iOS, но и предоставит вам нужный код. :-)
Fogmeister 02

Что, если вы задали в скетче и радиус, и размытие?
Владимир

Ответы:


261

Вот как с почти идеальной точностью применить все 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()метод.

Хм. В Sketch, если вы применяете распространение, он «выходит наружу», где начинается размытие / градиент тени. Это означает, что цвет тени остается неизменным, пока не начнется размытие. Но если мы перемещаем shadowLayer, тень будет начинаться только с этого слоя, верно? (Скажем, spread равен 10, тень начнется только после смещения 10, тогда как, как и в Sketch, размытие / градиент начнется после смещения 10). Так что они действительно не совпадают, правда?
Стефан

1
@Senseful Мне интересно узнать, как вы достигли этой формулы ?!
Hashem Aboonajmi

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

1
Как упоминалось выше ScottyBlades, без этого не получится maskToBounds = false. Пример неудачи - применение этой тени к файлу UIButton.
Хосе

5
Следует ли это shadowRadius = blur / UIScreen.main.scaleпротивопоставить жесткому кодированию blur / 2.0? Или есть еще одна причина делить на 2? @matchifang Ты понял это?
JWK

58

Вы можете попробовать это .... вы можете поиграть со значениями. Параметр shadowRadiusопределяет степень размытия. shadowOffsetдиктует, куда идет тень.

Swift 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 (почему он используется).
Sunita

12

Sketch Shadow с использованием IBDesignable и IBInspectable в Swift 4

ЭСКИЗ И XCODE СТОРОНА ЗА СТОРОНОЙ

Теневой пример

КОД

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

ВЫВОД

ДЕМО ВЫХОД

КАК ЭТО ИСПОЛЬЗОВАТЬ

ДЕМО


layer.shadowRadius * 2Разве ты не должен вернуться за получателем shadowBlur
Berkuqo

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 for 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.