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

时间:2019-06-08 19:48:44

标签: swiftui

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

enter image description here

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

5 个答案:

答案 0 :(得分:4)

我一直在寻找类似的功能,所以我通过以下方式做到了。

我创建了一个特殊的View结构,以所需的样式返回一个Button,在此结构中,我添加了一个State属性。我有一个名为'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个按钮的水平堆栈,这些按钮在选中时为蓝色,在取消选中时为红色。

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

答案 1 :(得分:3)

据我所知,目前尚无官方支持的方法。您可以使用以下解决方法。这样产生的行为与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 })
        }    
    }
}

答案 2 :(得分:2)

SwiftUI实际上为此expose an APIButtonStyle

struct MyButtonStyle : ButtonStyle {
    func body(configuration: Button<Self.Label>, isPressed: Bool) -> some View {
        configuration
            .overlay(configuration) // Works around SwiftUI automatically changing opacity.
            .padding()
            .foregroundColor(.white)
            .background(isPressed ? Color.red : Color.blue)
            .cornerRadius(8.0)
    }
}

extension StaticMember where Base: ButtonStyle {
    static var myStyle: StaticMember<MyButtonStyle> {
        return .init(MyButtonStyle())
    }
}

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

正如我在.overlay(...)之后的注释中所指出的那样,当前(测试版3)SwiftUI始终会自动更改按钮内容的不透明度。我已经对此提出了反馈,因为使用自定义按钮样式似乎应该完全覆盖这种行为。对我来说似乎是个虫子。

答案 3 :(得分:1)

这适用于对上述解决方案不满意的人,因为他们提出了其他问题,例如手势重叠(例如,现在很难在滚动视图中使用此解决方案)。另一个关键是创建这样的自定义按钮样式

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

因此,我们应该只传递闭包,闭包将返回按钮的状态并基于此参数创建按钮。它将像这样使用:

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")")
            }))
   }
} 

答案 4 :(得分:0)

好吧,让我再次清除一切。这是确切的解决方案

  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)
        
    }))