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

SwiftUI透明按钮如何仅在外部添加阴影?

解决SwiftUI透明按钮阴影渗透内部的问题

要实现透明背景、圆角边框且仅外部显示阴影的按钮,核心是通过遮罩(mask)裁剪掉阴影的内部部分,只保留外部区域。以下是两种可行的实现方案:

方案一:遮罩裁剪阴影内部

直接在边框的阴影层添加遮罩,仅保留外部阴影:

import SwiftUI

#Preview("Transparent Button Shadow Fix") {
    let cornerRadius: CGFloat = 15

    Button {} label: {
        Text("Transparent button")
            .font(.system(size: 14, weight: .semibold))
            .foregroundStyle(.black)
            .padding(.horizontal, 16)
            .padding(.vertical, 8)
            .frame(height: 40, alignment: .center)
    }
    .background(.clear) // 保持背景透明
    .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
    .overlay {
        ZStack {
            // 带阴影的边框
            RoundedRectangle(cornerRadius: cornerRadius)
                .stroke(.red, lineWidth: 1)
                .shadow(color: .blue, radius: 2, x: 0, y: 0)
                // 用放大的圆角矩形做遮罩,只保留外部阴影
                .mask(
                    RoundedRectangle(cornerRadius: cornerRadius)
                        .inset(by: -2) // 负数inset扩大遮罩范围,刚好覆盖外部阴影
                        .fill(Color.black)
                )
        }
    }
    .padding(48)
    .background(Color.white)
}

原理说明

  • .inset(by: -2) 将遮罩的圆角矩形范围向外扩大2pt(对应阴影的radius值),这样阴影的内部部分会被遮罩裁剪掉,只保留边框外侧的阴影。
  • 保持按钮背景为.clear,确保透明效果不受影响。

方案二:分层叠加阴影与边框

通过分层绘制阴影和边框,用遮罩精准控制阴影显示区域:

import SwiftUI

#Preview("Transparent Button Shadow Fix") {
    let cornerRadius: CGFloat = 15
    let shadowRadius: CGFloat = 2
    let borderWidth: CGFloat = 1

    Button {} label: {
        Text("Transparent button")
            .font(.system(size: 14, weight: .semibold))
            .foregroundStyle(.black)
            .padding(.horizontal, 16)
            .padding(.vertical, 8)
            .frame(height: 40, alignment: .center)
    }
    .background(.clear)
    .clipShape(RoundedRectangle(cornerRadius: cornerRadius))
    .overlay(alignment: .center) {
        // 第一层:绘制阴影,用遮罩仅保留外部部分
        RoundedRectangle(cornerRadius: cornerRadius)
            .fill(Color.clear)
            .shadow(color: .blue, radius: shadowRadius, x: 0, y: 0)
            .mask(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .stroke(Color.black, lineWidth: borderWidth + 2 * shadowRadius)
            )
        // 第二层:叠加红色边框
        RoundedRectangle(cornerRadius: cornerRadius)
            .stroke(.red, lineWidth: borderWidth)
    }
    .padding(48)
    .background(Color.white)
}

原理说明

  • 先绘制透明填充的圆角矩形并添加阴影,再用一个宽度为边框宽度+2*阴影半径的描边遮罩,只保留阴影的外侧部分。
  • 最后叠加红色边框,保证边框的正常显示,同时阴影不会渗透到按钮内部。

这两种方案都能满足你的需求:保持背景透明、保留圆角边框、仅外部显示阴影,且适配不同的背景色。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 17:14:52