在 UIView 中创建内部阴影以复制 Neumorphic Style
Creating inner shadows in UIView to replicate Neumorphic Style
我的任务是在我的 UIKit 应用程序中实现 neumorphic 设计风格。我已经成功地实现了双外部阴影(黑暗和光明),但不知何故,我无法弄清楚如何在视图内实现内部阴影。我已经尝试过像 CAGradientLayer
这样的方法,从 .black
到 .clear
再到 .white
,但它看起来并不像它应该的那样。我在互联网上搜索了各种解决方案,但似乎找不到合适的解决方案。
我应该如何创建内阴影?我应该使用什么方法?我可以在 Swift 或 Objective-C.
中找到解决方案
Current state of the view
The state that I am trying to reach
当前状态的简化版本(外部阴影):
class DebossedView: UIView {
private var outerDarkShadow = CALayer()
private var outerLightShadow = CALayer()
override func draw(_ rect: CGRect) {
outerDarkShadow = shadowLayer(color: UIColor.black, shadowOffset: 10, shadowRadius: 12)
outerLightShadow = shadowLayer(color: UIColor.white, shadowOffset: -10, shadowRadius: 10)
layer.borderColor = UIColor.white.cgColor
layer.borderWidth = 3
layer.insertSublayer(outerDarkShadow, at: 0)
layer.insertSublayer(outerLightShadow, at: 0)
}
private func shadowLayer(color: UIColor, shadowOffset: CGFloat, shadowRadius: CGFloat) -> CALayer {
let shadowLayer = CALayer()
shadowLayer.frame = bounds
shadowLayer.backgroundColor = UIColor.gray.cgColor
shadowLayer.shadowColor = color.cgColor
shadowLayer.cornerRadius = 16
shadowLayer.shadowOffset = CGSize(width: shadowOffset, height: shadowOffset)
shadowLayer.shadowOpacity = 1
shadowLayer.shadowRadius = shadowRadius
return shadowLayer
}
}
如果我没记错的话,您必须创建一个包含模糊形状的叠加层,并剪裁到该形状。
剪裁后,模糊的外部部分将消失,您只会看到内部模糊作为阴影。
您的“目标”图片与您为“Neumorphism informations”发布的 link 中的示例不匹配,因此不确定这是否会给您想要的确切结果。
然而,一些注意事项...
draw()
绝对不是 您要创建和 adding/inserting 子层的地方。
- 您可以使用
CAGradientLayer
获得“内部”外观(基于您的目标图像)
- 您可以添加一个
UIImageView
作为子视图来保存图像。
所以,这是一个例子 UIView
subclass:
class NeuView: UIView {
public var image: UIImage? {
didSet {
imgView.image = image
}
}
private let imgView = UIImageView()
private let darkShadow = CALayer()
private let lightShadow = CALayer()
private let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
// add sublayers
self.layer.addSublayer(darkShadow)
self.layer.addSublayer(lightShadow)
self.layer.addSublayer(gradientLayer)
darkShadow.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
darkShadow.shadowOffset = CGSize(width: 5, height: 5)
darkShadow.shadowOpacity = 1
darkShadow.shadowRadius = 10
lightShadow.shadowColor = UIColor.white.withAlphaComponent(0.9).cgColor
lightShadow.shadowOffset = CGSize(width: -5, height: -5)
lightShadow.shadowOpacity = 1
lightShadow.shadowRadius = 10
// 45-degree gradient layer
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
self.layer.borderColor = UIColor.white.withAlphaComponent(0.5).cgColor
layer.borderWidth = 3
// very light gray background color
let bkgColor = UIColor(white: 0.95, alpha: 1.0)
darkShadow.backgroundColor = bkgColor.cgColor
lightShadow.backgroundColor = bkgColor.cgColor
// set gradient colors from
// slightly darker than background to
// slightly lighter than background
let c1 = UIColor(white: 0.92, alpha: 1.0)
let c2 = UIColor(white: 0.97, alpha: 1.0)
gradientLayer.colors = [c1.cgColor, c2.cgColor]
// image view properties
imgView.contentMode = .scaleAspectFit
imgView.translatesAutoresizingMaskIntoConstraints = false
//imgView.layer.masksToBounds = true
addSubview(imgView)
NSLayoutConstraint.activate([
// let's make the image view 60% of self
imgView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
imgView.centerXAnchor.constraint(equalTo: centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
// set all layers' frames to bounds
darkShadow.frame = bounds
lightShadow.frame = bounds
gradientLayer.frame = bounds
// set all layers' cornerRadius to one-half height
let cr = bounds.height * 0.5
darkShadow.cornerRadius = cr
lightShadow.cornerRadius = cr
gradientLayer.cornerRadius = cr
layer.cornerRadius = cr
}
}
和一个示例视图控制器:
class NeuTestVC: UIViewController {
let neuView = NeuView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
guard let img = UIImage(named: "neu01") else {
print("Could not load image!")
return
}
neuView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(neuView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
neuView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
neuView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
neuView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
neuView.heightAnchor.constraint(equalTo: neuView.widthAnchor),
])
// set the image
neuView.image = img
}
}
使用此图片(透明背景):
这是结果:
通过调整颜色、阴影属性等,这可能会给您想要的结果。
编辑
这里是 NewView
class.
的修改版本
我们不使用渐变层,而是添加一个带有“孔”切口的 CAShapeLayer
,并使用该层来投射“内阴影”:
class NeuView: UIView {
public var image: UIImage? {
didSet {
imgView.image = image
}
}
private let imgView = UIImageView()
// "outer" shadows
private let darkShadow = CALayer()
private let lightShadow = CALayer()
// "inner" shadow
private let innerShadowLayer = CAShapeLayer()
private let innerShadowMaskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
// add sublayers
self.layer.addSublayer(darkShadow)
self.layer.addSublayer(lightShadow)
self.layer.addSublayer(innerShadowLayer)
darkShadow.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
darkShadow.shadowOffset = CGSize(width: 5, height: 5)
darkShadow.shadowOpacity = 1
darkShadow.shadowRadius = 10
lightShadow.shadowColor = UIColor.white.withAlphaComponent(0.9).cgColor
lightShadow.shadowOffset = CGSize(width: -5, height: -5)
lightShadow.shadowOpacity = 1
lightShadow.shadowRadius = 10
self.layer.borderColor = UIColor.white.withAlphaComponent(0.5).cgColor
//layer.borderWidth = 3
// very light gray background color
let bkgColor = UIColor(red: 0.94, green: 0.95, blue: 0.99, alpha: 1.0) // UIColor(white: 0.95, alpha: 1.0)
darkShadow.backgroundColor = bkgColor.cgColor
lightShadow.backgroundColor = bkgColor.cgColor // UIColor(white: 0.98, alpha: 1.0).cgColor
// image view properties
imgView.contentMode = .scaleAspectFit
imgView.translatesAutoresizingMaskIntoConstraints = false
//imgView.layer.masksToBounds = true
addSubview(imgView)
NSLayoutConstraint.activate([
// let's make the image view 60% of self
imgView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
imgView.centerXAnchor.constraint(equalTo: centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
// set dark and light shadow layers' frames to bounds
darkShadow.frame = bounds
lightShadow.frame = bounds
// set self.layer and dark and light shadow layers' cornerRadius to one-half height
let cr = bounds.height * 0.5
darkShadow.cornerRadius = cr
lightShadow.cornerRadius = cr
self.layer.cornerRadius = cr
// for the "inner" shadow,
// rectangle path needs to be larger than
// bounds + shadow offset + shadow raidus
// so the shadow doesn't "bleed" from all sides
let path = UIBezierPath(rect: bounds.insetBy(dx: -40, dy: -40))
// create a path for the "hole" in the layer
let circularHolePath = UIBezierPath(ovalIn: bounds)
// this "cuts a hole" in the path
path.append(circularHolePath)
path.usesEvenOddFillRule = true
innerShadowLayer.path = path.cgPath
innerShadowLayer.fillRule = .evenOdd
// fillColor doesn't matter - just needs to be opaque
innerShadowLayer.fillColor = UIColor.white.cgColor
// mask the layer, so we only "see through the hole"
innerShadowMaskLayer.path = circularHolePath.cgPath
innerShadowLayer.mask = innerShadowMaskLayer
// adjust properties as desired
innerShadowLayer.shadowOffset = CGSize(width: 15, height: 15)
innerShadowLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
innerShadowLayer.shadowRadius = 5
// setting .shadowOpacity to a very small value (such as 0.025)
// results in very light shadow
// set .shadowOpacity to 1.0 to clearly see
// what the shadow is doing
innerShadowLayer.shadowOpacity = 0.025
}
}
示例视图控制器:
class NeuTestVC: UIViewController {
let neuView = NeuView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.94, green: 0.95, blue: 0.99, alpha: 1.0)
guard let img = UIImage(named: "neu01") else {
print("Could not load image!")
return
}
neuView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(neuView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
neuView.topAnchor.constraint(equalTo: g.topAnchor, constant: 60.0),
neuView.widthAnchor.constraint(equalToConstant: 125.0),
neuView.heightAnchor.constraint(equalTo: neuView.widthAnchor),
neuView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
// set the image
neuView.image = img
}
}
结果 - 顶部实例的“内阴影”不透明度设置为 0.9
(以使其清晰可见)...底部实例设置为 0.025
:
我的任务是在我的 UIKit 应用程序中实现 neumorphic 设计风格。我已经成功地实现了双外部阴影(黑暗和光明),但不知何故,我无法弄清楚如何在视图内实现内部阴影。我已经尝试过像 CAGradientLayer
这样的方法,从 .black
到 .clear
再到 .white
,但它看起来并不像它应该的那样。我在互联网上搜索了各种解决方案,但似乎找不到合适的解决方案。
我应该如何创建内阴影?我应该使用什么方法?我可以在 Swift 或 Objective-C.
中找到解决方案Current state of the view
The state that I am trying to reach
当前状态的简化版本(外部阴影):
class DebossedView: UIView {
private var outerDarkShadow = CALayer()
private var outerLightShadow = CALayer()
override func draw(_ rect: CGRect) {
outerDarkShadow = shadowLayer(color: UIColor.black, shadowOffset: 10, shadowRadius: 12)
outerLightShadow = shadowLayer(color: UIColor.white, shadowOffset: -10, shadowRadius: 10)
layer.borderColor = UIColor.white.cgColor
layer.borderWidth = 3
layer.insertSublayer(outerDarkShadow, at: 0)
layer.insertSublayer(outerLightShadow, at: 0)
}
private func shadowLayer(color: UIColor, shadowOffset: CGFloat, shadowRadius: CGFloat) -> CALayer {
let shadowLayer = CALayer()
shadowLayer.frame = bounds
shadowLayer.backgroundColor = UIColor.gray.cgColor
shadowLayer.shadowColor = color.cgColor
shadowLayer.cornerRadius = 16
shadowLayer.shadowOffset = CGSize(width: shadowOffset, height: shadowOffset)
shadowLayer.shadowOpacity = 1
shadowLayer.shadowRadius = shadowRadius
return shadowLayer
}
}
如果我没记错的话,您必须创建一个包含模糊形状的叠加层,并剪裁到该形状。 剪裁后,模糊的外部部分将消失,您只会看到内部模糊作为阴影。
您的“目标”图片与您为“Neumorphism informations”发布的 link 中的示例不匹配,因此不确定这是否会给您想要的确切结果。
然而,一些注意事项...
draw()
绝对不是 您要创建和 adding/inserting 子层的地方。- 您可以使用
CAGradientLayer
获得“内部”外观(基于您的目标图像) - 您可以添加一个
UIImageView
作为子视图来保存图像。
所以,这是一个例子 UIView
subclass:
class NeuView: UIView {
public var image: UIImage? {
didSet {
imgView.image = image
}
}
private let imgView = UIImageView()
private let darkShadow = CALayer()
private let lightShadow = CALayer()
private let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
// add sublayers
self.layer.addSublayer(darkShadow)
self.layer.addSublayer(lightShadow)
self.layer.addSublayer(gradientLayer)
darkShadow.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
darkShadow.shadowOffset = CGSize(width: 5, height: 5)
darkShadow.shadowOpacity = 1
darkShadow.shadowRadius = 10
lightShadow.shadowColor = UIColor.white.withAlphaComponent(0.9).cgColor
lightShadow.shadowOffset = CGSize(width: -5, height: -5)
lightShadow.shadowOpacity = 1
lightShadow.shadowRadius = 10
// 45-degree gradient layer
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
self.layer.borderColor = UIColor.white.withAlphaComponent(0.5).cgColor
layer.borderWidth = 3
// very light gray background color
let bkgColor = UIColor(white: 0.95, alpha: 1.0)
darkShadow.backgroundColor = bkgColor.cgColor
lightShadow.backgroundColor = bkgColor.cgColor
// set gradient colors from
// slightly darker than background to
// slightly lighter than background
let c1 = UIColor(white: 0.92, alpha: 1.0)
let c2 = UIColor(white: 0.97, alpha: 1.0)
gradientLayer.colors = [c1.cgColor, c2.cgColor]
// image view properties
imgView.contentMode = .scaleAspectFit
imgView.translatesAutoresizingMaskIntoConstraints = false
//imgView.layer.masksToBounds = true
addSubview(imgView)
NSLayoutConstraint.activate([
// let's make the image view 60% of self
imgView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
imgView.centerXAnchor.constraint(equalTo: centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
// set all layers' frames to bounds
darkShadow.frame = bounds
lightShadow.frame = bounds
gradientLayer.frame = bounds
// set all layers' cornerRadius to one-half height
let cr = bounds.height * 0.5
darkShadow.cornerRadius = cr
lightShadow.cornerRadius = cr
gradientLayer.cornerRadius = cr
layer.cornerRadius = cr
}
}
和一个示例视图控制器:
class NeuTestVC: UIViewController {
let neuView = NeuView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
guard let img = UIImage(named: "neu01") else {
print("Could not load image!")
return
}
neuView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(neuView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
neuView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
neuView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
neuView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
neuView.heightAnchor.constraint(equalTo: neuView.widthAnchor),
])
// set the image
neuView.image = img
}
}
使用此图片(透明背景):
这是结果:
通过调整颜色、阴影属性等,这可能会给您想要的结果。
编辑
这里是 NewView
class.
我们不使用渐变层,而是添加一个带有“孔”切口的 CAShapeLayer
,并使用该层来投射“内阴影”:
class NeuView: UIView {
public var image: UIImage? {
didSet {
imgView.image = image
}
}
private let imgView = UIImageView()
// "outer" shadows
private let darkShadow = CALayer()
private let lightShadow = CALayer()
// "inner" shadow
private let innerShadowLayer = CAShapeLayer()
private let innerShadowMaskLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
// add sublayers
self.layer.addSublayer(darkShadow)
self.layer.addSublayer(lightShadow)
self.layer.addSublayer(innerShadowLayer)
darkShadow.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
darkShadow.shadowOffset = CGSize(width: 5, height: 5)
darkShadow.shadowOpacity = 1
darkShadow.shadowRadius = 10
lightShadow.shadowColor = UIColor.white.withAlphaComponent(0.9).cgColor
lightShadow.shadowOffset = CGSize(width: -5, height: -5)
lightShadow.shadowOpacity = 1
lightShadow.shadowRadius = 10
self.layer.borderColor = UIColor.white.withAlphaComponent(0.5).cgColor
//layer.borderWidth = 3
// very light gray background color
let bkgColor = UIColor(red: 0.94, green: 0.95, blue: 0.99, alpha: 1.0) // UIColor(white: 0.95, alpha: 1.0)
darkShadow.backgroundColor = bkgColor.cgColor
lightShadow.backgroundColor = bkgColor.cgColor // UIColor(white: 0.98, alpha: 1.0).cgColor
// image view properties
imgView.contentMode = .scaleAspectFit
imgView.translatesAutoresizingMaskIntoConstraints = false
//imgView.layer.masksToBounds = true
addSubview(imgView)
NSLayoutConstraint.activate([
// let's make the image view 60% of self
imgView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
imgView.centerXAnchor.constraint(equalTo: centerXAnchor),
imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
])
}
override func layoutSubviews() {
super.layoutSubviews()
// set dark and light shadow layers' frames to bounds
darkShadow.frame = bounds
lightShadow.frame = bounds
// set self.layer and dark and light shadow layers' cornerRadius to one-half height
let cr = bounds.height * 0.5
darkShadow.cornerRadius = cr
lightShadow.cornerRadius = cr
self.layer.cornerRadius = cr
// for the "inner" shadow,
// rectangle path needs to be larger than
// bounds + shadow offset + shadow raidus
// so the shadow doesn't "bleed" from all sides
let path = UIBezierPath(rect: bounds.insetBy(dx: -40, dy: -40))
// create a path for the "hole" in the layer
let circularHolePath = UIBezierPath(ovalIn: bounds)
// this "cuts a hole" in the path
path.append(circularHolePath)
path.usesEvenOddFillRule = true
innerShadowLayer.path = path.cgPath
innerShadowLayer.fillRule = .evenOdd
// fillColor doesn't matter - just needs to be opaque
innerShadowLayer.fillColor = UIColor.white.cgColor
// mask the layer, so we only "see through the hole"
innerShadowMaskLayer.path = circularHolePath.cgPath
innerShadowLayer.mask = innerShadowMaskLayer
// adjust properties as desired
innerShadowLayer.shadowOffset = CGSize(width: 15, height: 15)
innerShadowLayer.shadowColor = UIColor(white: 0.0, alpha: 1.0).cgColor
innerShadowLayer.shadowRadius = 5
// setting .shadowOpacity to a very small value (such as 0.025)
// results in very light shadow
// set .shadowOpacity to 1.0 to clearly see
// what the shadow is doing
innerShadowLayer.shadowOpacity = 0.025
}
}
示例视图控制器:
class NeuTestVC: UIViewController {
let neuView = NeuView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.94, green: 0.95, blue: 0.99, alpha: 1.0)
guard let img = UIImage(named: "neu01") else {
print("Could not load image!")
return
}
neuView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(neuView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
neuView.topAnchor.constraint(equalTo: g.topAnchor, constant: 60.0),
neuView.widthAnchor.constraint(equalToConstant: 125.0),
neuView.heightAnchor.constraint(equalTo: neuView.widthAnchor),
neuView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
// set the image
neuView.image = img
}
}
结果 - 顶部实例的“内阴影”不透明度设置为 0.9
(以使其清晰可见)...底部实例设置为 0.025
: