如何设置 SwiftUI 按钮的自定义突出显示状态

How to set custom highlighted state of SwiftUI Button

我有一个按钮。我想为突出显示的状态设置自定义背景颜色。我怎样才能在 SwiftUI 中做到这一点?

Button(action: signIn) {
    Text("Sign In")
}
.padding(.all)
.background(Color.red)
.cornerRadius(16)
.foregroundColor(.white)
.font(Font.body.bold())

据我所知,目前还没有官方支持的方法来做到这一点。这是您可以使用的一些解决方法。这会产生与在 UIKit 中相同的行为,在 UIKit 中点击按钮并将手指拖离它会使按钮保持突出显示。

struct HoverButton<Label>: View where Label: View {

    private let action: () -> ()

    private let label: () -> Label

    init(action: @escaping () -> (), label: @escaping () -> Label) {
        self.action = action
        self.label = label
    }

    @State private var pressed: Bool = false

    var body: some View {
        Button(action: action) {
            label()
                .foregroundColor(pressed ? .red : .blue)
                .gesture(DragGesture(minimumDistance: 0.0)
                    .onChanged { _ in self.pressed = true }
                    .onEnded { _ in self.pressed = false })
        }    
    }
}

针对 SwiftUI beta 5 进行了更新

SwiftUI 实际上 expose an API 为此:ButtonStyle.

struct MyButtonStyle: ButtonStyle {

  func makeBody(configuration: Self.Configuration) -> some View {
    configuration.label
      .padding()
      .foregroundColor(.white)
      .background(configuration.isPressed ? Color.red : Color.blue)
      .cornerRadius(8.0)
  }

}


// To use it
Button(action: {}) {
  Text("Hello World")
}
.buttonStyle(MyButtonStyle())

我一直在寻找类似的功能,我是通过以下方式实现的。

我创建了一个特殊的 View 结构,以我需要的样式返回一个 Button,在这个结构中我添加了一个 State 属性 selected。我有一个名为 'table' 的变量,它是一个 Int,因为我的按钮是一个带有数字的圆形按钮

struct TableButton: View {
    @State private var selected = false

    var table: Int

    var body: some View {
        Button("\(table)") {
            self.selected.toggle()
        }
        .frame(width: 50, height: 50)
        .background(selected ? Color.blue : Color.red)
        .foregroundColor(.white)
        .clipShape(Circle())
    }
}

然后我在我的内容中使用查看代码

HStack(spacing: 10) {
  ForEach((1...6), id: \.self) { table in
    TableButton(table: table)
  }
}

这将创建一个水平堆栈,其中包含 6 个按钮,选中时颜色为蓝色,取消选中时颜色为红色。

我不是经验丰富的开发人员,只是尝试了所有可能的方法,直到我发现这对我有用,希望它对其他人也有用。

这是针对那些对上述解决方案不满意的人,因为他们提出了其他问题,例如重叠手势(例如,现在在 scrollview 中很难使用此解决方案)。另一个拐杖是创建一个像这样的自定义按钮样式

struct CustomButtonStyle<Content>: ButtonStyle where Content: View {
    
    var change: (Bool) -> Content
    
    func makeBody(configuration: Self.Configuration) -> some View {
        return change(configuration.isPressed)
    }
}

所以,我们应该只传递将 return 按钮状态的闭包,并根据此参数创建按钮。它将像这样使用:

struct CustomButton<Content>: View where Content: View {
    var content:  Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    var body: some View {
        Button(action: { }, label: {
            EmptyView()
        })
            .buttonStyle(CustomButtonStyle(change: { bool in
                Text("\(bool ? "yo" : "yo2")")
            }))
   }
} 

好的,让我再次清除所有内容。这是确切的解决方案

  1. 创建下面的按钮修改器。
    struct StateableButton<Content>: ButtonStyle where Content: View {
        var change: (Bool) -> Content
        
        func makeBody(configuration: Configuration) -> some View {
            return change(configuration.isPressed)
        }
    }
  1. 然后像下面一样使用它
    Button(action: {
        print("Do something")
    }, label: {

        // Don't create your button view in here
        EmptyView()
    })
    .buttonStyle(StateableButton(change: { state in

        // Create your button view in here
        return HStack {
            Image(systemName: "clock.arrow.circlepath")
            Text(item)
            Spacer()
            Image(systemName: "arrow.up.backward")
        }
        .padding(.horizontal)
        .frame(height: 50)
        .background(state ? Color.black : Color.clear)
        
    }))

您需要定义一个自定义样式,用于为正常状态和高亮状态提供两种背景:

Button(action: {
     print("action")
}, label: {
     Text("My Button").padding()
})
.buttonStyle(HighlightableButtonStyle(normal: { Color.red },
                                      highlighted: { Color.green }))

// Custom button style
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
struct HighlightableButtonStyle<N, H>: ButtonStyle where N: View, H: View {
    
    private let alignment: Alignment
    private let normal: () -> N
    private let highlighted: () -> H
    
    init(alignment: Alignment = .center, @ViewBuilder normal: @escaping () -> N, @ViewBuilder highlighted: @escaping () -> H) {
        self.alignment = alignment
        self.normal = normal
        self.highlighted = highlighted
    }
    
    func makeBody(configuration: Configuration) -> some View {
        return ZStack {
            if configuration.isPressed {
                configuration.label.background(alignment: alignment, content: normal)
            }
            else {
                configuration.label.background(alignment: alignment, content: highlighted)
            }
        }
    }
}