[SwiftUI]ButtonStyleでisEnabledの取得

2 min read読了の目安(約1800字

前回の記事で、SwiftUIのButtonStyleを使うことでボタンの見た目を整えることができました。
今回は、ボタンの有効/無効をButtonStyleでどうにかできないか調べました。

やりたいことと問題点

ButtonStyleを使い、ボタンの有効/無効の状態によって色を変えます。
しかし、ButtonStyleの実装を見てみると、isPressedの状態しかありません。なので、ボタンの有効/無効を自前で用意する必要があります。

public protocol ButtonStyle {

    associatedtype Body : View

    @ViewBuilder func makeBody(configuration: Self.Configuration) -> Self.Body

    typealias Configuration = ButtonStyleConfiguration
}

public struct ButtonStyleConfiguration {

    public struct Label : View {
        public typealias Body = Never
    }

    public let label: ButtonStyleConfiguration.Label

    public let isPressed: Bool
}

解決策1

こちらの記事にあるように、ButtonStyleの中でカスタムViewを定義して@Environmentで状態isEnabledを取得する方法です。

https://stackoverflow.com/questions/59169436/swiftui-buttonstyle-how-to-check-if-button-is-disabled-or-enabled

解決策2

解決策1を改善し、以下のようにできました。

import SwiftUI

struct CapsuleButtonStyle: ButtonStyle {
        
    @Environment(\.isEnabled) var isEnabled: Bool

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(self.isEnabled ? Color.blue : Color.gray)
            .foregroundColor(.white)
            .font(.body.bold())
            .clipShape(Capsule())
            .scaleEffect(configuration.isPressed ? 0.95 : 1)
            .animation(.easeOut(duration: 0.2))
    }
}

特にカスタムViewを定義しなくても取得できていました。
気になることとして、ButtonStyleをまとめて適用した時にisEnabledが正しく取れるかどうかですが、調べた限り問題はありませんでした。

Group {
  Button("Rounded 1", action: { }).disabled(!self.enabled)
  Button("Rounded 2", action: { })            
}.buttonStyle(RoundedButtonStyle(cornerRadius: 8.0))

Rounded 1をdisabledにしても、Rounded 2はdisableにはならない

サンプルコード

https://github.com/usk-sample/SwiftUIButtonsSample