代码之家  ›  专栏  ›  技术社区  ›  Lucas Buchalla Sesti

如何在SwiftUI中使背景图像变暗,但排除透明模型内的区域?

  •  0
  • Lucas Buchalla Sesti  · 技术社区  · 6 月前

    Cover image

    Mockup image

    我有一个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()
    }
    

    我如何将黑色覆盖层仅应用于背景图像中未被模型覆盖的部分?有没有办法屏蔽或排除模型透明部分内的区域?或者我需要一种完全不同的方法来实现这种效果吗?

    任何指导或代码示例都将不胜感激!

    2 回复  |  直到 6 月前
        1
  •  1
  •   Benzy Neez    6 月前

    首先,我建议在 背景 ZStack ,而不是作为第一层 焦面 这样,从缩放到填充的溢出不会导致 焦面 延伸出屏幕。

    然后,可以添加掩模层作为第一层 焦面 ,这在背景图像上方可见。

    • 掩模由半透明材料制成 Color.black .

    • A. RoundedRectangle 覆盖在半透明背景上。拐角半径应与模型的形状大致匹配。

    • 圆角矩形使用 .blendMode(.destinationOut) 这导致形状从下面的半透明黑色层上切下。

    • 修改器 .compositingGroup() 以防止混合模式更深地燃烧到下层。

    将裁剪的大小和位置与模型图像相匹配 VStack , .matchedGeometryEffect 可以使用。这需要一个命名空间:

    @Namespace private var ns
    

    以下是应用了更新的示例:

    ZStack {
        Color.black
            .opacity(0.5)
            .ignoresSafeArea()
            .overlay {
                RoundedRectangle(cornerRadius: 40)
                    .inset(by: 4)
                    .fill(.black)
                    .matchedGeometryEffect(id: "mockup", in: ns, isSource: false)
                    .blendMode(.destinationOut)
            }
            .compositingGroup()
    
        VStack {
            Image(.mockup)
                .resizable()
                .scaledToFit()
                .matchedGeometryEffect(id: "mockup", in: ns)
    
            // ... other content as before
        }
        // ... modifiers as before
    }
    .background {
        Image(.cover)
            .resizable()
            .scaledToFill()
            .ignoresSafeArea()
    }
    

    Screenshot

        2
  •  -1
  •   Abdul Karim Khan    6 月前

    该代码在背景图像上添加了一个深色的覆盖层,但保持了模型的透明区域清晰。方法如下:

    1. 叠加和蒙版:Color.black.opacity(0.2)叠加会使背景变暗,但我们会对其进行蒙版,这样模型的透明区域就不会变暗。
    2. 混合模式:模型使用.blendMode(.destinationOut)在其透明部分“剪切”暗覆盖。
    3. 组合组:组合所有内容以进行正确渲染。 这使模型保持清晰,同时使其余背景更暗,以提高可读性。只需确保模型图像是透明的PNG!

    您可以用图像替换矩形(“//排除模型区域”)。请尝试以下代码:

    ZStack {
            // Background Image with Overlay
            Image(".cover")
                .resizable()
                .scaledToFill()
                .ignoresSafeArea()
                .overlay(
                    Color.black.opacity(0.2) // Dark overlay
                        .mask(
                            ZStack {
                                Rectangle() // Apply overlay to the entire screen
                                    .fill(Color.black)
                                Rectangle() // Exclude mockup area
                                    .frame(width: 200, height: 300)
                                    .scaledToFit()
                                    .blendMode(.destinationOut) // Cut out the transparent area
                            }
                            .compositingGroup()
                        )
                )
    ...........
    

    enter image description here