如何根据可选的 Binding<Image> 在此 SwiftUI @ViewBuilder 中初始化 Bool?

How do I initialize a Bool in this SwiftUI @ViewBuilder based on an optional Binding<Image>?

我的目标是使用三元运算符为图像添加阴影,但如果视图使用默认图像,不会添加阴影。

这是我的代码:

import SwiftUI

struct ImageDisplay: View {
    @State private var defaultImage: Image = Image(systemName: "person.crop.circle.fill")
    private var defaultText: String = "Add picture"
    private var hasShadow: Bool = true
    private var text: String?
    private var image: Binding<Image>?
    
    public init(_ text: String, image: Binding<Image>) {
        self.init(
            text: .some(text),
            image: image
        )
    }
    
    public init(image: Binding<Image>) {
        self.init(
            text: nil,
            image: image
        )
    }
    
    public init() {
        self.init(
            text: nil,
            image: nil
        )
    }
    
    private init(text: String?, image: Binding<Image>?) {
        self.text = text
        self.image = image
    }
    
    private struct InternalImageDisplay: View {
        var text: String
        var hasShadow: Bool  /// NOT CERTAIN WHERE THIS BELONGS, OR IF IT GOES HERE ...
        @Binding var image: Image
        
        @ViewBuilder
        var body: some View {
                        
            VStack {
                image
                    .imageDisplayStyle()
                    .shadow(radius: hasShadow ? 4.0 : 0.0) /// HERE IS WHERE I'LL USE IT...
                
                Button(action: {
                    
                    //TODO: - Code to present image picker.
                    
                }, label: {
                    Text(text)
                })
            }
        }
    }
    
    var body: some View {
        InternalImageDisplay(
            text: text ?? defaultText,
            hasShadow: false,   /// THIS IS NOT WORKING IN ANY IMPLEMENTATION ...
            image: image ?? $defaultImage
        )
    }
}

我也有 Image 的扩展:

import SwiftUI

extension Image {
    func imageDisplayStyle() -> some View {
        return self
            .resizable()
            .scaledToFill()
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
            .aspectRatio(contentMode: .fit)
            .clipShape(Circle())
            .foregroundColor(.gray)
   }
}

我正在尝试让看起来像这样的代码能够很好地工作并且它很棒,除了阴影布尔:

struct ContentView: View {
    
    @State private var image = Image("img1")
    
    @State private var defaultImage: Bool = true
    
    var body: some View {
        VStack (spacing: 48) {
            // no binding
            ImageDisplay()
            // binding
            ImageDisplay("Select image", image: $image)
            ImageDisplay(image: $image)
        }
        .frame(width: 190)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


在您的初始化程序中,您可以根据是否传入图像参数来设置hasShadow

然后,您可以将其传递给您的 InternalImageDisplay

出于测试目的,我让阴影更明显一些:


struct ImageDisplay: View {
    @State private var defaultImage: Image = Image(systemName: "person.crop.circle.fill")
    private var defaultText: String = "Add picture"
    private var hasShadow: Bool = true
    private var text: String?
    private var image: Binding<Image>?
    
    public init(_ text: String, image: Binding<Image>) {
        self.init(
            text: text,
            image: image,
            hasShadow: true //<-- Here
        )
    }
    
    public init(image: Binding<Image>) {
        self.init(
            text: nil,
            image: image,
            hasShadow: true //<-- Here
        )
    }
    
    public init() {
        self.init(
            text: nil,
            image: nil,
            hasShadow: false //<-- Here
        )
    }
    
    private init(text: String?, image: Binding<Image>?, hasShadow : Bool) {  //<-- Here
        self.text = text
        self.image = image
        self.hasShadow = hasShadow  //<-- Here
    }
    
    private struct InternalImageDisplay: View {
        var text: String
        var hasShadow: Bool   //<-- This gets passed in as a prop
        @Binding var image: Image
        
        @ViewBuilder
        var body: some View {
                        
            VStack {
                image
                    .imageDisplayStyle()
                    .shadow(color: Color.green, radius: hasShadow ? 10.0 : 0)  //<-- Here (now it's green for testing)
                
                Button(action: {
                    
                    //TODO: - Code to present image picker.
                    
                }, label: {
                    Text(text)
                })
            }
        }
    }
    
    var body: some View {
        InternalImageDisplay(
            text: text ?? defaultText,
            hasShadow: hasShadow,  //<-- Here
            image: image ?? $defaultImage
        )
    }
}