英文:
Is there other alternative other than extending Button for ButtonStyle?
问题
我的目标是拥有条件按钮样式。但是,.buttonStyle(selected : .bordered : .borderedProminent) 无法使用。
struct DepartureArrivalView: View {
    @Binding var value: String
    @Binding var selected: Bool
    
    var body: some View {
        VStack(spacing: 0) {
            Button {
                
            } label: {
                Text(value)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.title)
            }
            .customButtonStyle(selected)
            .padding(16)
        }
    }
}
extension Button {
    @ViewBuilder
    func customButtonStyle(_ selected: Bool) -> some View {
        switch selected {
        case true:
            self.buttonStyle(.bordered)
        case false:
            self.buttonStyle(.borderedProminent)
        }
    }
}
#if DEBUG
struct DepartureArrivalView_Previews: PreviewProvider {
    static var previews: some View {
        DepartureArrivalView(value: .constant("A"), selected: .constant(true))
    }
}
#endif
英文:
My goal is to have conditional button style. However, .buttonStyle(selected : .bordered : .borderedProminent) can't be used.
struct DepartureArrivalView: View {
    @Binding var value: String
    @Binding var selected: Bool
    
    var body: some View {
        VStack(spacing: 0) {
            Button {
                
            } label: {
                Text(value)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.title)
            }
            .customButtonStyle(selected)
            .padding(16)
        }
    }
}
extension Button {
    @ViewBuilder
    func customButtonStyle(_ selected: Bool) -> some View {
        switch selected {
        case true:
            self.buttonStyle(.bordered)
        case false:
            self.buttonStyle(.borderedProminent)
        }
    }
}
#if DEBUG
struct DepartureArrivalView_Previews: PreviewProvider {
    static var previews: some View {
        DepartureArrivalView(value: .constant("A"), selected: .constant(true))
    }
}
#endif
答案1
得分: 3
注意,.bordered 和 .borderedProminent 不符合 ButtonStyle。它们符合 PrimitiveButtonStyle。
因此,解决这个问题的一种方法是引入一个 PrimitiveButtonStyle,它包装了你想要选择的两种样式。为了以一种通用、可重用的方式实现它,可以将它定义为对这两种样式的泛型包装:
enum Either<Left, Right> {
    case left(Left)
    case right(Right)
}
extension Either: PrimitiveButtonStyle where Left: PrimitiveButtonStyle, Right: PrimitiveButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        switch self {
        case .left(let left):
            left.makeBody(configuration: configuration)
        case .right(let right):
            right.makeBody(configuration: configuration)
        }
    }
}
然后像这样使用它:
struct DepartureArrivalView: View {
    @Binding var value: String
    @Binding var selected: Bool
    private var style: some PrimitiveButtonStyle {
        return selected ? Either.left(.bordered) : .right(.borderedProminent)
    }
    var body: some View {
        VStack(spacing: 0) {
            Button {
            } label: {
                Text(value)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.title)
            }
            .buttonStyle(style)
            .padding(16)
        }
    }
}
英文:
Note that .bordered and .borderedProminent don't conform to ButtonStyle. They conform to PrimitiveButtonStyle.
So, one way to solve this is by introducing a PrimitiveButtonStyle that wraps the two styles you want to choose from. To do it in a generic, reusable way, make it generic over the two styles:
enum Either<Left, Right> {
    case left(Left)
    case right(Right)
}
extension Either: PrimitiveButtonStyle where Left: PrimitiveButtonStyle, Right: PrimitiveButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        switch self {
        case .left(let left):
            left.makeBody(configuration: configuration)
        case .right(let right):
            right.makeBody(configuration: configuration)
        }
    }
}
Then use it like this:
struct DepartureArrivalView: View {
    @Binding var value: String
    @Binding var selected: Bool
    private var style: some PrimitiveButtonStyle {
        return selected ? Either.left(.bordered) : .right(.borderedProminent)
    }
    var body: some View {
        VStack(spacing: 0) {
            Button {
                
            } label: {
                Text(value)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .font(.title)
            }
            .buttonStyle(style)
            .padding(16)
        }
    }
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论