如何为任意SwiftUI View添加内阴影?解决非Shape视图实现难题
为任意SwiftUI View实现内阴影效果
SwiftUI中,Shape子类(如RoundedRectangle)可以直接通过.fill(.shadow(.inner(...)))实现内阴影,但对于Text这类非Shape视图,这种方法并不适用。下面提供一个通用方案,可给任意SwiftUI View添加内阴影:
自定义ViewModifier实现通用内阴影
通过自定义ViewModifier,利用视图复制、遮罩和混合模式,模拟出内阴影效果,且不会遮挡原视图内容:
struct InnerShadowModifier: ViewModifier { let color: Color let radius: CGFloat let offsetX: CGFloat let offsetY: CGFloat // 简化初始化,默认水平偏移为0 init(color: Color, radius: CGFloat, offsetX: CGFloat = 0, offsetY: CGFloat) { self.color = color self.radius = radius self.offsetX = offsetX self.offsetY = offsetY } func body(content: Content) -> some View { content .overlay( content .offset(x: offsetX, y: offsetY) .shadow(color: color, radius: radius) .mask(content) .blendMode(.destinationOut) ) .compositingGroup() } } // 给View添加扩展,方便链式调用 extension View { func innerShadow(color: Color, radius: CGFloat, offsetX: CGFloat = 0, offsetY: CGFloat) -> some View { self.modifier(InnerShadowModifier(color: color, radius: radius, offsetX: offsetX, offsetY: offsetY)) } }
使用示例
给Text添加内阴影+外投影
#Preview { VStack { Text("Hello Inner Shadow") .font(.largeTitle) .fontWeight(.bold) .background(.blue) .frame(width: .infinity, height: 100) .innerShadow(color: .white, radius: 1, offsetY: -1) .shadow(radius: 10) // 叠加外投影 Spacer() } .background(.yellow) .padding() }
给Shape复用该方法
效果和原生.fill(.shadow(.inner(...)))一致,且统一调用逻辑:
#Preview { VStack { RoundedRectangle(cornerRadius: 10) .fill(.blue) .frame(width: .infinity, height: 100) .innerShadow(color: .white, radius: 1, offsetY: -1) .shadow(radius: 10) Spacer() } .background(.yellow) .padding() }
方案原理说明
- 复制原视图并偏移:让阴影出现在目标方向(比如向上偏移则顶部生成内阴影)
- 用原视图作为遮罩:限制阴影只在原视图的范围内显示
.blendMode(.destinationOut):让阴影区域“挖掉”原视图对应位置的像素,模拟内阴影的凹陷效果.compositingGroup():确保混合模式和阴影效果正确作用于整个视图组,避免影响外部视图
这个方案的核心优势是不依赖固定Shape,适用于任意SwiftUI View(包括Text、Image、容器视图等),且不会遮挡原视图的内容。
内容的提问来源于stack exchange,提问作者fingia
相关产品推荐
相关产品推荐

