如何仅将 SwiftUI 不透明度应用于父视图?

How to apply SwiftUI opacity to the parent View only?

将不透明度应用于 SwiftUI 中的视图,将其向下传递到层次结构中,看起来它有点像环境值。当每个子视图独立地受到不透明度的影响时,它会导致行为。不管我们把黄色圆圈放在叠加层、背景还是ZStack里面。

我们如何才能使不透明度只影响父视图,就像它在 UIKit 中的行为一样,这样整个视图及其所有内容将变成半透明的单个实体对象?

struct OpacityTest: View {
    var body: some View {
        HStack(spacing: 30) {
            buttonContent
                .opacity(0.5)

            Button(action: {}) {
                buttonContent
            }
        }
    }
    
    var buttonContent: some View {
        Image(systemName: "calendar.circle")
            .resizable()
            .frame(width: 65, height: 65)
            .overlay(
                Circle()
                    .fill(Color.yellow)
                    .frame(width: 40, height: 40)
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
            )
    }
}

struct OpacityTest_Preview: PreviewProvider {
    static var previews: some View {
        OpacityTest()
    }
}

更新: 正如 ChrisR 回答的那样,.drawingGroup() 完成了工作并部分解决了问题,但直到一切都在父视图范围内。一旦我们向叠加层内的视图添加偏移量,它就会被裁剪。有机会避免这种情况吗?

.overlay(
    Circle()
        .fill(Color.yellow)
        .frame(width: 40, height: 40)
        .offset(x: 10, y: -10) //This modifier brakes the drawingGroup
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
    )

添加.drawingGroup()。这会将所有进一步的修饰符应用于整个 object/view:

    var buttonContent: some View {
        Image(systemName: "calendar.circle")
            .resizable()
            .frame(width: 65, height: 65)
            .overlay(
                Circle()
                    .fill(Color.yellow)
                    .frame(width: 40, height: 40)
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
            )
        .drawingGroup() // here
    }

关于 OP 更新的编辑:

.offset 不会更改视图的 size/bounds,只会移动渲染,这会导致不必要的裁剪。您可以使用 geometryReader,但在固定大小的示例中,手动放大父框架应该足以避免剪裁:

    var buttonContent: some View {
        ZStack(alignment: .topTrailing) {
            Image(systemName: "calendar.circle")
                .resizable()
                .frame(width: 65, height: 65)
            Circle()
                .fill(Color.yellow)
                .frame(width: 40, height: 40)
                .offset(x: 10, y: -10)
        }
        .frame(width: 95, height: 95) // here
        .drawingGroup()
    }

如果我们对父视图应用任何修饰符,它也会对子视图产生影响。

buttonContent 中删除 .opacity(0.5) 并将其添加到 .overlay modifier

之前

添加 .compositingGroup() 就可以了。它使外部修饰符仅在顶层起作用,而不会通过层次结构传递。也不裁剪任何带偏移的子视图,并保留原始布局框架。

var buttonContent: some View {
    Image(systemName: "calendar.circle")
        .resizable()
        .frame(width: 65, height: 65)
        .overlay(
            Circle()
                .fill(Color.yellow)
                .frame(width: 40, height: 40)
                .offset(x: 10, y: -10)
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
        )
        .compositingGroup() //here
}