【SwiftUI】枠線付き角丸ボタンを簡単に作りたい

しみじみ勉強してきたのに、


なぜか簡単に作れないこういうボタン。

ので、少しやってみました。



 

🧑🏻‍💻 まず思いついた記述

便利な BorderdProminentButtonStyle().overlay() で 角丸枠線をのせます。


// borderProminent + overlay

Button {
} label: {
  VStack {
    Label("BorderedProminent", systemImage: "face.smiling")
    Text(".overlay()")
      .font(.caption)
  }
  .padding()
}
.buttonStyle(.borderedProminent)
.tint(.orange)
#if os(macOS)
.clipShape(.rect(cornerRadius: 16))
#endif
.overlay(RoundedRectangle(cornerRadius: 16).stroke(.gray))

macOS では角がずれる。

ので、clipShape() しました。

気持ち悪いですね。

 

🧑🏻‍💻 よくある記述

この書き方が多いらしいです。

ラベル の View に modifier のチェイン。


// plain + label view modifiers
      
Button {
} label: {
  VStack {
    Label("Plain", systemImage: "face.smiling")
    Text(".background(.orange, in: .rect(cornerRadius: 16))")
      .font(.caption)
    Text(".overlay()")
      .font(.caption)
  }
  .foregroundStyle(.white)
  .padding()
  .background(.orange, in: .rect(cornerRadius: 16))
  .overlay(RoundedRectangle(cornerRadius: 16).stroke(.gray))
}
.buttonStyle(.plain)

ButtonStyle は .plain 一択でしたが、

見た目も、ボタンを押したときの挙動も、

問題ないように見えます。

ただ、コードが長ったらしくなるので、

同様なボタンの数が増えると見通しが悪くなりそうです。

 

🧑🏻‍💻 カスタム ButtonStyle

ビルトインにあっても良さそうなのに。

押したときエフェクトは、BorderdProminentButtonStyle() になるべく似せて ButtonStyle を作ります。


struct StrokeRoundedRectangleButtonStyle: ButtonStyle {
  var cornerRadius: CGFloat

  func makeBody(configuration: Configuration) -> some View {
    configuration.label
      .foregroundStyle(
        .white.opacity(configuration.isPressed ? 0.75 : 1)
      )
      .padding()
      .background(
        .orange.opacity(configuration.isPressed ? 0.75 : 1),
        in: .rect(cornerRadius: cornerRadius)
      )
      .overlay(
        RoundedRectangle(cornerRadius: cornerRadius)
          .stroke(.gray.opacity(configuration.isPressed ? 0.75 : 1))
      )
  }
}

extension ButtonStyle where Self == StrokeRoundedRectangleButtonStyle {
  static var strokeRounded: Self { Self(cornerRadius: 16) }
}

ButtonStyle を作ってしまえば、あとは簡単にボタンに適用できます。


// custom style

Button {
} label: {
  VStack {
    Label("Custom Button Style", systemImage: "face.smiling")
    Text("extension ")
      .font(.caption)
  }
}
.buttonStyle(.strokeRounded)

しかし、コード量は多い。

 

🧑🏻‍💻 まとめ

レイアウトの重なりの考え方として、この場合は、

角を丸くするのは .background() で、

枠線はそれに合わせて .overlay() する、

のが良さげに思えました。

あと、

iOS と macOS のコード共用は、シビアなレイアウトになると厳しいのですね!

👉 Customizing the Appearance and Interaction Behavior of Buttons | Fatbobman's Blog hatena-bookmark