You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIKit应用实现Neumorphic新拟态风格时如何为UIView添加内阴影

实现思路
  • UIKit没有原生内阴影属性,核心实现逻辑为外阴影+反向遮罩:创建带外阴影的图层,用遮罩裁剪掉视图边界以外的阴影部分,仅保留视图内部的阴影区域,即可得到内阴影效果。
  • 新拟态凹陷效果需要两层内阴影配合:左上角的深色内阴影模拟凹陷背光面,右下角的浅色内阴影模拟凹陷受光面,和你已实现的外阴影逻辑完全对应。
完整实现代码
class DebossedView: UIView {
    private var outerDarkShadow = CALayer()
    private var outerLightShadow = CALayer()
    // 新增内阴影图层
    private var innerDarkShadow = CALayer()
    private var innerLightShadow = CALayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupLayers()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupLayers()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        // 避免重复添加图层
        if outerDarkShadow.superlayer == nil {
            layer.borderColor = UIColor.white.cgColor
            layer.borderWidth = 3
            layer.cornerRadius = 16
            layer.insertSublayer(outerDarkShadow, at: 0)
            layer.insertSublayer(outerLightShadow, at: 0)
            layer.addSublayer(innerDarkShadow)
            layer.addSublayer(innerLightShadow)
        }
        // 视图尺寸变化时更新所有图层frame和遮罩
        outerDarkShadow.frame = bounds
        outerLightShadow.frame = bounds
        innerDarkShadow.frame = bounds
        innerLightShadow.frame = bounds
        
        let maskPath = UIBezierPath(rect: bounds.insetBy(dx: -100, dy: -100))
        let cutoutPath = UIBezierPath(roundedRect: bounds, cornerRadius: 16).reversing()
        maskPath.append(cutoutPath)
        (innerDarkShadow.mask as? CAShapeLayer)?.path = maskPath.cgPath
        (innerLightShadow.mask as? CAShapeLayer)?.path = maskPath.cgPath
    }
    
    // 原有外阴影生成方法
    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
    }
    
    // 新增内阴影生成方法
    private func innerShadowLayer(color: UIColor, shadowOffset: CGSize, shadowRadius: CGFloat, shadowOpacity: Float) -> CALayer {
        let layer = CALayer()
        layer.shadowColor = color.cgColor
        layer.shadowOffset = shadowOffset
        layer.shadowRadius = shadowRadius
        layer.shadowOpacity = shadowOpacity
        layer.masksToBounds = true
        layer.cornerRadius = 16
        
        // 反向遮罩:大矩形挖去中间视图区域,仅保留内部阴影
        let maskLayer = CAShapeLayer()
        let maskPath = UIBezierPath(rect: bounds.insetBy(dx: -100, dy: -100))
        let cutoutPath = UIBezierPath(roundedRect: bounds, cornerRadius: 16).reversing()
        maskPath.append(cutoutPath)
        maskLayer.path = maskPath.cgPath
        layer.mask = maskLayer
        
        return layer
    }
    
    private func setupLayers() {
        backgroundColor = .gray
        outerDarkShadow = shadowLayer(color: .black, shadowOffset: 10, shadowRadius: 12)
        outerLightShadow = shadowLayer(color: .white, shadowOffset: -10, shadowRadius: 10)
        // 内阴影参数可根据设计需求调整
        innerDarkShadow = innerShadowLayer(color: .black, shadowOffset: CGSize(width: 6, height: 6), shadowRadius: 8, shadowOpacity: 0.4)
        innerLightShadow = innerShadowLayer(color: .white, shadowOffset: CGSize(width: -6, height: -6), shadowRadius: 8, shadowOpacity: 0.4)
    }
}
效果调整说明
  • 内阴影的偏移量、半径、透明度参数可根据设计需求微调,透明度建议控制在0.3~0.6区间,避免效果过重。
  • 所有圆角参数需保持统一,避免遮罩边缘错位。
  • 图层布局逻辑移到了layoutSubviews中,避免draw方法多次调用导致重复添加图层,同时适配视图尺寸变化的场景。

内容的提问来源于stack exchange,提问作者Flavius Dolha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 20:27:04