はじめに
Appleのサンプルコードを参考に、LabelStyleを自作する方法を紹介します。
環境
- OS:macOS Monterey 12.4
- Xcode:13.4.1 (13F100)
- Swift:5.6.1
既存コードのリファクタリング
以下のコードをリファクタリングしながら、LabelStyleの実装を紹介します。
HStack(spacing: 32) {
Image(iconName) // var iconName: String
.resizable()
.scaledToFit()
.frame(width: 68, height: 68)
Text(name) // var name: String
.font(.title)
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(16)
LabelStyleを作成する
まずLabelStyleを実装します。
LabelStyle
に準拠した構造体を作成し、 makeBody(configuration:)
メソッドで既存コードの HStack
をほぼそのまま返せばOKです。
変更点は以下です。
-
Image(iconName)
をconfiguration.icon
、Text(name)
をconfiguration.title
へ置き換える -
Image
に付いている.resizable()
を外す- 理由はわからないが、付けるとビルドエラーになる
+ import SwiftUI
+
+ struct MonsterCellLabelStyle: LabelStyle {
+ func makeBody(configuration: Configuration) -> some View {
+ HStack(spacing: 32) {
+ configuration.icon
+ .scaledToFit()
+ .frame(width: 68, height: 68)
+ configuration.title
+ .font(.title)
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+ }
+ }
LabelStyleのプレビューを作成する(任意)
必要に応じてLabelStyleのプレビューを作成します。
import SwiftUI
struct MonsterCellLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(spacing: 32) {
configuration.icon
.scaledToFit()
.frame(width: 68, height: 68)
configuration.title
.font(.title)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
+
+ struct MonsterCellLabelStyle_Previews: PreviewProvider {
+ static var previews: some View {
+ VStack {
+ Label {
+ Text("uhooi")
+ } icon: {
+ Image("uhooi")
+ .resizable()
+ }
+ Label {
+ Text("とてつもなく長い名前のモンスター")
+ } icon: {
+ Image("uhooi")
+ .resizable()
+ }
+ }
+ .labelStyle(MonsterCellLabelStyle())
+ }
+ }
Label
の最初のクロージャーに Text
、 次のクロージャーに Image
を渡します。
Image
には先ほど外した .resizable()
を付けます。
.labelStyle(MonsterCellLabelStyle())
でLabelStyleを適用します。
既存のViewをLabelに置き換える
LabelStyleを作成したら、既存のViewを Label
に置き換えます。
- HStack(spacing: 32) {
- Image(iconName)
- .resizable()
- .scaledToFit()
- .frame(width: 68, height: 68)
- Text(name)
- .font(.title)
- .frame(maxWidth: .infinity, alignment: .leading)
+ Label {
+ Text(name)
+ } icon: {
+ Image(iconName)
+ .resizable()
}
+ .labelStyle(MonsterCellLabelStyle())
.padding(16)
これでLabelStyleの実装は完成です。
LabelStyleの指定をスマートにする(任意)
Appleのサンプルコードに面白いエクステンションがあったので、LabelStyleの指定をスマートにしたい場合に実装します。
import SwiftUI
struct MonsterCellLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(spacing: 32) {
configuration.icon
.scaledToFit()
.frame(width: 68, height: 68)
configuration.title
.font(.title)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
+ extension LabelStyle where Self == MonsterCellLabelStyle {
+ static var monsterCell: Self { .init() }
+ }
+
struct MonsterCellLabelStyle_Previews: PreviewProvider {
static var previews: some View {
VStack {
Label {
Text("uhooi")
} icon: {
Image("uhooi")
.resizable()
}
Label {
Text("とてつもなく長い名前のモンスター")
} icon: {
Image("uhooi")
.resizable()
}
}
.labelStyle(MonsterCellLabelStyle())
}
}
このエクステンションにより、ドット始まりでLabelStyleを指定できるようになります。
Label {
Text(name)
} icon: {
Image(iconName)
.resizable()
}
- .labelStyle(MonsterCellLabelStyle())
+ .labelStyle(.monsterCell)
.padding(16)
Swiftっぽくなっていい感じです。
おわりに
これでLabelStyleを自作できるようになりました。
Labelを使うことで画像と文字列に関連があることを示せ、LabelStyleを使うことで構造と装飾を分離できます。
LabelとLabelStyleを使い、Viewの可読性を高めていきましょう
参考リンク
- Refactor MonsterCellView · uhooi/UhooiPicBook-SwiftUI-Playground@35f7015
- Refactor MonsterCellLabelStyle · uhooi/UhooiPicBook-SwiftUI-Playground@f57e83d
- Refactor ButtonStyle by uhooi · Pull Request #139 · uhooi/Loki
- https://twitter.com/_rockname/status/1624931190947061760
- How to make a custom button style supports leading dot syntax in SwiftUI | Sarunw