【问题标题】:Creating inner shadows in UIView to replicate Neumorphic Style在 UIView 中创建内部阴影以复制 Neumorphic Style
【发布时间】:2021-12-05 14:21:47
【问题描述】:

我的任务是在我的 UIKit 应用程序中实现神经拟态设计风格。我已经成功地实现了双外阴影(黑暗和光明),但不知何故,我无法弄清楚如何在视图内实现内阴影。我试过像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 information

【问题讨论】:

  • 这看起来是您工作的良好起点吗? medium.com/@mail2sajalkaushik/…
  • @DonMag 这确实是一个很好的起点。我现在所处的位置,就是受到那个话题的启发。从这里开始,对我来说是未知的。
  • 好的 - 您需要尝试澄清您遇到的问题。无法添加图片?你的渐变不是你想要的吗?很难说哪里不对。
  • 我正在尝试在圆圈内创建内部阴影。
  • 这就是你的目标吗? i.stack.imgur.com/ZexQW.png(或者,至少,更接近)?

标签: ios swift objective-c core-graphics core-animation


【解决方案1】:

您的“目标”图像与您为“Neumorphism informations”发布的链接中的示例并不真正匹配,因此不确定这是否会为您提供所需的确切结果。

但是,一些注意事项...

  1. draw()绝对不是您要在其中创建和添加/插入子层。
  2. 您可以使用CAGradientLayer 来获得“内在”外观(基于您的目标图像)
  3. 您可以添加UIImageView 作为子视图来保存图像。

所以,这里有一个例子 UIView 子类:

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 类的修改版本。

我们不使用渐变层,而是添加一个带有“孔”切口的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

【讨论】:

  • 谢谢你!这是一个很棒且有据可查的解决方案。但不幸的是,这还不是我想要得到的。我之前用渐变尝试过这个解决方案,但这不是它想要的。再次感谢您的宝贵时间,不胜感激!
  • @FlaviusDolha - 除了非常轻微的颜色差异之外,我真的无法分辨我的结果和您的“目标”图像之间有什么不同。您是否试图沿着这些思路获得“内部阴影”? i.stack.imgur.com/aIFUc.png ...显然,没有那么暗,但这只是为了强调阴影。
  • 没错!非常感谢!
  • @FlaviusDolha - 请参阅我的回答的编辑,了解使用“内阴影”修改的NeuView
【解决方案2】:

如果我没记错的话,您必须创建一个包含模糊形状的叠加层,并剪裁到该形状。 通过剪裁,模糊的外部部分将消失,而您将只将内部模糊作为阴影。

【讨论】:

    猜你喜欢
    • 2012-03-02
    • 1970-01-01
    • 2021-12-16
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 2014-07-30
    • 1970-01-01
    相关资源
    最近更新 更多