#ios #swift #uiview #calayer #shadow
#iOS #swift #uiview #calayer #тень
Вопрос:
Я пытаюсь создать UIButton со скругленными углами, который имеет 2 цветных тени. Почему красный (а на данный момент еще и синий) слой «тени» покрывает кнопку? Как получить тени под холстом кнопки). Я думал, что это помогает вставлять подслои, а не просто добавлять их.
Я создал игровую площадку, иллюстрирующую проблему
import UIKit
import PlaygroundSupport
Это кнопка, которую я пытаюсь реализовать
class PrimaryButton: UIButton {
required init(text: String = "Test 1", hasShadow: Bool = true) {
super.init(frame: .zero)
setTitle(text, for: .normal)
backgroundColor = UIColor.blue
layer.cornerRadius = 48 / 2
layer.masksToBounds = false
if hasShadow {
insertShadow()
}
}
fileprivate func insertShadow() {
let layer2 = CALayer(layer: layer), layer3 = CALayer(layer: layer)
layer2.applySketchShadow(color: UIColor.red, alpha: 0.5, x: 0, y: 15, blur: 35, spread: -10)
layer3.applySketchShadow(color: UIColor.blue, alpha: 0.5, x: 0, y: 10, blur: 21, spread: -9)
layer.insertSublayer(layer2, at: 0)
layer.insertSublayer(layer3, at: 0)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
layer.sublayers?.forEach { (sublayer) in
sublayer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
}
}
Это расширение, которое помогает добавлять тень из спецификации Sketch:
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
}
masksToBounds = false
}
}
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let button = PrimaryButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 48)
view.addSubview(button)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Ответ №1:
Мне это кажется законным. layer1
amp; layer2
являются подслоями слоя кнопок.
Вы могли бы добавить третий слой, который будет служить фоном. Вот пример, основанный на вашем коде:
class PrimaryButton: UIButton {
let layer1 = CALayer(), layer2 = CALayer(), layer3 = CALayer()
override func layoutSubviews() {
super.layoutSubviews()
layer1.backgroundColor = UIColor.blue.cgColor
layer1.cornerRadius = 48 / 2
[layer1, layer2, layer3].forEach {
$0.masksToBounds = false
$0.frame = layer.bounds
layer.insertSublayer($0, at: 0)
}
layer2.applySketchShadow(color: UIColor.red, alpha: 0.5, x: 0, y: 15, blur: 35, spread: -10)
layer3.applySketchShadow(color: UIColor.blue, alpha: 0.5, x: 0, y: 10, blur: 21, spread: -9)
}
}
Обратите внимание, что я поместил большую часть кода внутрь layoutSubviews
, потому что большинство ваших методов используют фактические границы кнопки.
Комментарии:
1.
layer.masksToBounds = false
устанавливается 3 раза? 0 долларов или раньше forEach?2. Это ненормально, когда вы вызываете
insertSublayer
каждый раз
Ответ №2:
Измените ваши вставки на:
layer.insertSublayer(layer2, at: 1)
layer.insertSublayer(layer3, at: 2)
Это должно сработать.
Комментарии:
1. Это произошло. Почему? Что такого особенного в слое с индексом 0?
2. Я не знаю, почему это работает, потому что все, что я читаю в z-ordering, говорит, что этого не должно быть, и ваш исходный код должен быть правильным. У меня просто возникло ощущение, и я попробовал это 🙂
3. Это не работает. Взгляните на синий фон рядом. Они не совпадают. Вы только что поменяли синий цвет на красный.
4. Также возникли некоторые проблемы при реализации в моем реальном решении. Сегодня я просматривал слишком много теней. Идея наличия фонового слоя и двух теневых слоев отлично сработала.
5. Вы захотите добавить подслои за текущими слоями. Итак, вы могли бы добавить каждый слой позади всех остальных или в начале.
Ответ №3:
Другой способ — добавить двойные кнопки без изменения вашего класса.
let button = PrimaryButton()
button.frame = CGRect(x: 150, y: 200, width: 200, height: 48)
button.backgroundColor = UIColor.clear
view.addSubview(button)
self.view = view
let button1 = PrimaryButton()
button1.frame = CGRect(x: 0, y: 0, width: 200, height: 48)
button.addSubview(button1)
button1.layer.sublayers?.forEach{$0.removeFromSuperlayer()}