SwiftUI中如何为形状的描边添加内阴影效果
问题原因
原有内阴影实现使用完整的实心形状作为阴影层的遮罩,模糊后的阴影会铺满整个形状的内部区域,因此会溢出描边范围覆盖中间空心区域,无法实现仅附着在描边上的效果。
兼容iOS16以下版本的实现方案
核心调整点:
- 先裁剪阴影层,去掉形状外侧溢出的阴影,仅保留向内扩散的内阴影方向内容
- 使用奇偶填充规则生成环形遮罩,挖掉中间空心区域的阴影,让阴影仅出现在描边对应的环形范围内
完整代码
struct ContentView: View { var body: some View { VStack { // 对照:普通无阴影描边 PolygonShape(sides: 7) .stroke(Color.white, lineWidth: 10) // 带描边内阴影的效果 PolygonShape(sides: 7) .strokeWithInnerShadow( strokeColor: .white, lineWidth: 10, shadowColor: .red, shadowRadius: 4 ) } .padding(32) .background(Color.black.ignoresSafeArea()) } } extension Shape { func strokeWithInnerShadow( strokeColor: Color, lineWidth: CGFloat, shadowColor: Color, shadowRadius: CGFloat ) -> some View { // 基础描边层 self.stroke(strokeColor, lineWidth: lineWidth) .overlay( self .stroke(shadowColor, lineWidth: lineWidth) .blur(radius: shadowRadius) // 裁掉形状外部的阴影,仅保留向内扩散的内阴影 .clipShape(self) .mask( GeometryReader { proxy in let size = proxy.size // 内缩距离为描边宽度的1/2,对齐描边的内沿 let inset = lineWidth / 2 Path { path in // 外边界:原形状完整路径 path.addPath(self.path(in: CGRect(origin: .zero, size: size))) // 内边界:向内缩进后的形状路径 let innerRect = CGRect(origin: .zero, size: size) .insetBy(dx: inset, dy: inset) path.addPath(self.path(in: innerRect)) } // 奇偶填充生成环形遮罩,挖空中间区域 .fill(style: FillStyle(eoFill: true, antialiased: true)) } ) ) } } struct PolygonShape: Shape { let sides: Int func path(in rect: CGRect) -> Path { let c = CGPoint(x: rect.width/2.0, y: rect.height/2.0) let r = Double(min(rect.width,rect.height)) / 2.0 var vertices:[CGPoint] = [] for i in 0...sides { let angle = (2.0 * Double.pi * Double(i)/Double(sides)) let pt = CGPoint(x: r * cos(angle), y: r * sin(angle)) vertices.append(CGPoint(x: pt.x + c.x, y: pt.y + c.y)) } var path = Path() for (n, pt) in vertices.enumerated() { n == 0 ? path.move(to: pt) : path.addLine(to: pt) } path.closeSubpath() return path } } struct StackedElementsView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
说明
- 该实现兼容iOS13及以上版本,无iOS16专属API依赖
- 对于系统自带Shape(
Circle、RoundedRectangle、Capsule等)和所有自动在传入rect内居中绘制的自定义Shape,该方法都可以正常生效 - 可通过调整
shadowRadius参数控制阴影模糊范围,需要更真实的阴影效果可以给阴影层添加小幅内偏移,例如在.blur后调用.offset(x: 0, y: 1)模拟光照方向
内容的提问来源于stack exchange,提问作者TruMan1
相关产品推荐
相关产品推荐

