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

SwiftUI中如何给背景图加暗化遮罩并排除样机透明区域?

封面图
样机图

问题描述

我在SwiftUI视图中展示了一张背景图,上方叠加了带有透明区域的iPhone样机,透明区域可显示背景图。为提升内容可读性,我想添加Color.black.opacity(0.2)的遮罩来暗化背景图,但希望遮罩不覆盖样机的透明区域。

我尝试了以下方法:

Color.black.opacity(0.2)
    .ignoresSafeArea()
    .overlay(
        Image(.mockup)
            .resizable()
            .scaledToFit()
            .blendMode(.destinationOut)
)

但该方法未达到预期效果——暗化遮罩仍在样机的透明区域可见。

以下是完整上下文代码:

import SwiftUI

struct ContentView: View {
    @State var name: String = ""
    
    var body: some View {
        ZStack {
            Image(.cover)
                .resizable()
                .scaledToFill()
                .ignoresSafeArea()
            
            VStack {
                Image(.mockup)
                
                Spacer()
                
                Text("Lucas & Lavínia")
                    .font(.title)
                    .fontWeight(.bold)
                
                Spacer().frame(height: 24)
                
                TextField(
                    "",
                    text: $name,
                    prompt: Text("Nome").foreground(.white)
                )
                .padding(15)
                .background(.white.opacity(0))
                .overlay(
                    RoundedRectangle(
                        cornerRadius: 8
                    )
                    .stroke(
                        .white,
                        lineWidth: 1
                    )
                )
                .clipShape(.rect(cornerRadius: 8))
                .foregroundStyle(.white)
                
                Spacer().frame(height: 24)
                
                Button {
                    
                } label: {
                    HStack {
                        Text("Selecionar fotos da galeria")
                    }
                    .frame(maxWidth: .infinity)
                    .foregroundColor(.white)
                    .padding(.vertical, 15)
                    .background(.red)
                    .cornerRadius(40)
                }
                .frame(maxWidth: .infinity)
                
                Spacer().frame(height: 24)
                
                Button {
                    
                } label: {
                    Text("Capturar foto agora")
                }
                
                Spacer().frame(height: 35)
            }
            .frame(maxWidth: .infinity)
            .foregroundStyle(.white)
            .padding(.all)
            .padding(.top, 25)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
    }
}

#Preview {
    ContentView()
}

请问如何仅对未被样机覆盖的背景图部分应用黑色遮罩?是否可以遮罩或排除样机透明区域内的部分?或者需要采用完全不同的方法实现该效果?

恳请提供相关指导或代码示例!


解决方案

要实现“遮罩不覆盖样机透明区域”的效果,核心是利用混合模式(Blend Mode)或反向遮罩,配合compositingGroup()确保效果正确作用于视图层级。

方法一:用blendMode挖空遮罩

你之前的思路正确,但缺少compositingGroup()打包视图组,导致混合模式无法正确生效。修改后的遮罩代码如下:

Color.black.opacity(0.2)
    .ignoresSafeArea()
    .overlay(Image(.mockup).resizable().scaledToFit())
    .compositingGroup() // 关键:将遮罩和样机打包为一个合成单元
    .blendMode(.destinationOut) // 用样机的不透明区域“挖空”遮罩

将这个遮罩放在背景图和原有VStack之间:

ZStack {
    Image(.cover)
        .resizable()
        .scaledToFill()
        .ignoresSafeArea()
    
    // 添加自定义遮罩
    Color.black.opacity(0.2)
        .ignoresSafeArea()
        .overlay(Image(.mockup).resizable().scaledToFit())
        .compositingGroup()
        .blendMode(.destinationOut)
    
    VStack {
        // 原有VStack内容保持不变
    }
}

方法二:反向遮罩实现

直接给遮罩层添加反向遮罩,只保留样机之外的区域:

Color.black.opacity(0.2)
    .ignoresSafeArea()
    .mask(
        Image(.mockup)
            .resizable()
            .scaledToFit()
            .colorInvert() // 反转颜色,让样机不透明区域变透明
            .compositingGroup()
            .luminanceToAlpha() // 基于亮度转换为透明度,确保遮罩逻辑正确
    )

同样将该遮罩放置在背景图与VStack之间即可。

注意事项

  • 确保Image(.mockup)是带透明通道的PNG格式图片,否则无法识别透明区域。
  • compositingGroup()是核心,它能让混合、遮罩效果作用于整个视图组,而非单个子视图。

内容的提问来源于stack exchange,提问作者Lucas Buchalla Sesti

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:47:25