【问题标题】:How to set custom highlighted state of SwiftUI Button如何设置 SwiftUI 按钮的自定义突出显示状态
【发布时间】:2019-10-23 20:46:12
【问题描述】:

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

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

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    为 SwiftUI beta 5 更新

    SwiftUI 实际上为此做了expose an APIButtonStyle

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

    【讨论】:

    • 我收到了这个:dyld: Symbol not found: _$s7SwiftUI11ButtonStyleP4body13configuration9isPressed4BodyQzAA0C0VyAA0cD5LabelVG_SbtFTq
    • 这不配置Button的标签而不是Button本身?我看到的行为暗示 configuration.label 影响子视图,比如是否要在样式中设置角半径,但在 .buttonStyle(...) 调用之后的 Button 上设置背景
    • 有什么方法可以自定义禁用样式吗?用.disabled(true)申请的那个?
    • @PavelAlexeev 请参阅stackoverflow.com/a/59210692/1007303 以获取解决问题的方法。
    • 这仅适用于未选中的按下状态,这只是一个瞬时状态:“一个布尔值,指示用户当前是否正在按下按钮。” developer.apple.com/documentation/swiftui/…
    【解决方案2】:

    据我所知,目前还没有官方支持的方式来做到这一点。这是您可以使用的一个小解决方法。这会产生与 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 })
            }    
        }
    }
    

    【讨论】:

    • 不错的发现!也可以使用.longPressAction(minimumDuration: 0, maximumDistance: .infinity, {}) { pressed in self.pressed = pressed } } 来完成。
    • 但是,这不会动画,是吗? :hmm:
    • @thisIsTheFoxe 使用withAnimation{}
    • 如果按钮位于滚动视图中的某个位置,这是否有效?
    • 不,这不是@DavidAnderson,因此我切换回了普通按钮
    【解决方案3】:

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

    我创建了一个特殊的 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 个按钮的水平堆栈,这些按钮在选中时显示为蓝色,在取消选中时显示为红色。

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 您能举一个使用CustomButton的例子吗?
      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-14
        • 2020-11-18
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 2020-05-07
        • 2013-05-08
        • 1970-01-01
        相关资源
        最近更新 更多