SwiftUI在MacOS上的按钮问题

24

我正在使用SwiftUI在MACOS上

如果我这样做:

      Button(action: { } ) {
        Text("Press")
        .padding()
        .background(Color.blue)
      }

我收到了这个:

enter image description here

两个灰色区域是可点击按钮的端点。 但我希望按钮的形状是蓝色区域。 有什么办法可以让整个蓝色区域都可点击。

(我尝试使用.onTapGesture,但这样无法使按钮动画化,以便您知道已经点击它。)


这是SwiftUI常见的问题。我在使用Image时也遇到了同样的问题。 - kchopda
对于只想要一个默认按钮的人,请尝试这个:https://dev59.com/E1MI5IYBdhLWcg3wBGuC#62727585 - Manabu Nakazawa
7个回答

25

使用ButtonStyle,然后根据传递的配置值指定颜色和其他样式属性,即可实现您想要的外观。

如果存在一种可以继承默认按钮半径、基于文本长度自动调整宽度和其他属性的折中方案,那将是很好的,但至少有能力指定所有属性并获得所需的外观。

希望这有所帮助!

import SwiftUI

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

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello World")
                .frame(maxWidth: .infinity, maxHeight: .infinity)

            Button(action: {
            }) {
                Text("Press")
                    .frame(maxWidth: 100, maxHeight: 24)
            }
            .buttonStyle(BlueButtonStyle())
        }
    }
}


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

感谢@Gene Z. Ragan - 真的很有效 :-) 很想看看苹果是否会回应我的反馈。 - Cortado-J
如果他们回复了你,请发表一条评论。很高兴这对你有用! - Gene Z. Ragan
我从你的优秀代码基础上做了进一步的改进,使其更加简洁和灵活。我会发布为单独的答案。 - Cortado-J
2
同样的方法也在以下链接中讨论过:https://alejandromp.com/blog/2019/06/22/swiftui-reusable-button-style/ - Cortado-J
谢谢大家!这个问题深深地埋在专家们的脑海中...是否有任何关于SwiftUI在MacOS上的限制的文档或论坛,因为大多数教程都是关于iOS的,我浪费了时间追踪这个问题,最终发现是一个macOS的问题。 - mobibob
@mobibob,我仍然认为这真的是一个bug。请看下面我与苹果的对话。我不知道有任何论坛或文档可以解决这个问题。 - Cortado-J

15

受 @Gene Z. Ragan 巨佬的回答启发,我在此基础上做了更进一步的改动:

将 ButtonStyle 更加灵活化:

struct NiceButtonStyle: ButtonStyle {
  var foregroundColor: Color
  var backgroundColor: Color
  var pressedColor: Color

  func makeBody(configuration: Self.Configuration) -> some View {
    configuration.label
      .font(.headline)
      .padding(10)
      .foregroundColor(foregroundColor)
      .background(configuration.isPressed ? pressedColor : backgroundColor)
      .cornerRadius(5)
  }
}

然后在调用该方法时添加一些语法糖以使其更加清晰:

extension View {
  func niceButton(
    foregroundColor: Color = .white,
    backgroundColor: Color = .gray,
    pressedColor: Color = .accentColor
  ) -> some View {
    self.buttonStyle(
      NiceButtonStyle(
        foregroundColor: foregroundColor,
        backgroundColor: backgroundColor,
        pressedColor: pressedColor
      )
    )
  }
}

这意味着我们可以使用默认的颜色设置:白色前景、灰色背景以及强调颜色和按下颜色。

  Button(action: { } ) {
    Text("Button A")
  }
  .niceButton()

或者我们可以自定义颜色:

  Button(action: { } ) {
    Text("Button B has a long description")
  }
  .niceButton(
    foregroundColor: .blue,
    backgroundColor: .yellow,
    pressedColor: .orange
  )

我们得到:

这里输入图片描述

再次感谢Gene。


同样的方法也在以下链接中讨论过:https://alejandromp.com/blog/2019/06/22/swiftui-reusable-button-style/ - Cortado-J

5
我向苹果的反馈助手提出了这个问题,看看他们是否有任何有用的想法。
对话如下:
我说:
如果在MACOS上使用SwiftUI,我这样做:
  Button(action: { } ) {
    Text("Press")
    .padding()
    .background(Color.blue)
  }

我看到一个蓝色的盒子,两个灰色的东西从两侧伸出。图片已附上。这两个灰色区域是可点击按钮的端点。但我期望按钮的形状应该是蓝色区域的形状。
在iOS上相同的代码可以正常工作。
(我尝试使用.onTapGesture,但这不会使按钮动画化,以便您知道已经点击它。)
苹果公司说:
iOS和macOS具有不同的默认ButtonStyles-iOS是无边框的,而macOS具有标准的边框效果。
听起来你正在尝试创建自定义ButtonStyle(因此没有任何系统提供的chrome)。所以你需要创建它,可以将蓝色背景应用于按钮的标签,然后将其应用于你的简单按钮与文本。
Button("Press"), action {}).buttonStyle(BluePaddingButtonStyle()

这将确保它在您运行它的每个平台上具有相同的外观。
我说:
嗨,谢谢你的解释。 我明白你的意思,我对我想出的方法感到满意。
仍然不太对劲的是:
  Button(action: { } ) {
    Text("Press")
    .padding()
    .background(Color.blue)
  }

“应该产生如此奇怪的外观。我不明白为什么那段代码不能只生成一个蓝色按钮。”
“就像我说的那样-这不是一个问题,因为我已经解决了它,但目前它似乎并不直观。”
“苹果公司说:”
“ViewBuilder内提供的内容用作按钮的标签:而不是整个按钮。按钮仍将带有周围的背景、边框、前景样式等,如其ButtonStyle所述。因此,如果您的按钮需要具有非常特定的外观,则需要自定义该样式:要么使用BorderlessButtonStyle(尽管请注意,它仍具有特定的前景外观样式),要么使用自定义ButtonStyle。”
“我的想法:”
“这确实帮助我理解为什么它显示为它的样子,但从直觉上看,它仍然似乎是错的!!!”

这是有益的见解。谢谢分享。 - mobibob

4
您可以通过添加 .buttonStyle(.borderless) 来强制 macOS 上实现类似 iOS 的行为。
Button(action: { } ) {
   Text("Press")
      .padding()
      .background(Color.blue)
}
.buttonStyle(.borderless)

1
在Swift 5中,可以通过以下简单的代码实现:
        Button("First Button") {
            print("Hello World")
        }
        .padding(10)
        .accentColor(.yellow)
        .background(Color.blue)
        .cornerRadius(10)

Screenshot of the preview


1
我不认为这是可能的,但你可以尝试使用this
支持SPM,适用于Swift 5.1且精简。

谢谢@krjw - 非常感谢。CustomButton看起来不错,但我不知道如何将其转换为View以在SwiftUI中使用? - Cortado-J
1
我也认为我在问题中提供的代码片段是合理的 - 你认为这实际上是一个 bug 吗? - Cortado-J
说实话,我只在iOS上使用过SwiftUI,并在新项目中尝试了您的代码,以找出是否有助于将修饰符应用于按钮本身...我稍微玩了一下,认为这不是正确的行为。我不会说这是一个错误,因为我不知道预期的行为,但对我来说至少看起来是错误的。 - krjw
我之前在iOS中使用Button,效果很好。但是我很惊讶的是,在MacOS上它并没有相同或至少类似的结果,因为SwiftUI的一个理念就是你可以用相同的代码在不同平台间切换。 - Cortado-J
你知道我该如何让CustomButton与SwiftUI一起工作吗?我想我需要一种符合View协议的包装器? - Cortado-J
我已经通过苹果的反馈助手发送了“反馈”。很有趣看看他们会说什么... - Cortado-J

1
忽略自从发现这个问题以来已经过去了将近4年,到2023年7月,它仍然存在于MacOS上。MacOS上的按钮根本不支持标签(LABEL)。 任何位于“label”内部的视图都会被忽略,而按钮的最小高度为约16像素。这就是为什么。
    Text("Press")
        .padding()
        .background(Color.blue)

覆盖了“按钮”的“灰色背景”。

我的建议是,不要浪费时间 - 只需使用带有手势捕捉器的“标签”作为“按钮”。

    Text("Press")
        .padding()
        .background(Color.blue)
        .onTapGesture {
             // reaction here
        }

这在MacOS上可以运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接