はじめに
SwiftUIで読みやすい padding
を付けるコツを紹介します。
環境
- OS:macOS Ventura 13.3.1
- Xcode:14.3 (14E222b)
- Swift:5.8
リファクタリング前のコード
以下のコードを読みやすくなるようリファクタリングします。
import SwiftUI
struct FooView: View {
var body: some View {
VStack(spacing: 0) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
.padding([.leading, .trailing], 24)
Text("This is Swift. Swift is very beautiful programming language.")
.padding(EdgeInsets(top: 8, leading: 24, bottom: 32, trailing: 24))
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
.padding([.leading, .trailing], 24)
Text("This is gear.")
.padding(.top, 8)
.padding([.leading, .trailing], 24)
}
}
}
リファクタリング
.horizontal
や .vertical
を使う
.leading
と .trailing
をまとめて .horizontal
で表せます。
同様に .top
と .bottom
はまとめて .vertical
と表せます。
記述が簡潔になるのでオススメです。
import SwiftUI
struct FooView: View {
var body: some View {
VStack(spacing: 0) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
- .padding([.leading, .trailing], 24)
+ .padding(.horizontal, 24)
Text("This is Swift. Swift is very beautiful programming language.")
.padding(EdgeInsets(top: 8, leading: 24, bottom: 32, trailing: 24))
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
- .padding([.leading, .trailing], 24)
+ .padding(.horizontal, 24)
Text("This is gear.")
.padding(.top, 8)
- .padding([.leading, .trailing], 24)
+ .padding(.horizontal, 24)
}
}
}
今回は使っていませんが、上下左右の padding
は .all
で表わせ、デフォルトは .all
なので省略できます。
私は省略したほうが簡潔で読みやすいと思っています。
// 以下はすべて同義
// 下に行くほど読みやすいと思っている
.padding([.top, .leading, .bottom, .trailing], 24)
.padding(.all, 24)
.padding(24)
EdgeInset
を使わない
EdgeInset
を使うことで、1行で上下左右の padding
を設定できます。
しかし4方向すべてを指定するイニシャライザしか存在せず、 .horizontal
のようにまとめたり、 一部分のみ省略したりできません。
好みもありますが、私は EdgeInset
を使わず .padding
を複数付けて表現します。
import SwiftUI
struct FooView: View {
var body: some View {
VStack(spacing: 0) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
.padding(.horizontal, 24)
Text("This is Swift. Swift is very beautiful programming language.")
- .padding(EdgeInsets(top: 8, leading: 24, bottom: 32, trailing: 24))
+ .padding(.top, 8)
+ .padding(.bottom, 32)
+ .padding(.horizontal, 24)
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
.padding(.horizontal, 24)
Text("This is gear.")
.padding(.top, 8)
.padding(.horizontal, 24)
}
}
}
要素同士の間隔には spacing
を使う
こちらの記事 で説明している通り、一般的に padding
は「親要素と子要素の間隔」を表します 。
SwiftUIで 「要素同士の間隔」は spacing
で表す ので、そうなるように修正します。
VStack
の spacing
を 0
にして、 padding
で調整するのは望ましくありません。
VStack
や HStack
はグルーピングの役割も果たします。
Image
と Text
間の spacing
は 8
、 swift
と gear
のグループ間の spacing
は 32
とわかるので、 VStack(spacing:)
を使って表現します。
import SwiftUI
struct FooView: View {
var body: some View {
- VStack(spacing: 0) {
+ VStack(spacing: 32) {
+ VStack(spacing: 8) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
.padding(.horizontal, 24)
Text("This is Swift. Swift is very beautiful programming language.")
- .padding(.top, 8)
- .padding(.bottom, 32)
.padding(.horizontal, 24)
+ VStack(spacing: 8) {
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
.padding(.horizontal, 24)
Text("This is gear.")
- .padding(.top, 8)
.padding(.horizontal, 24)
}
}
}
グルーピングと spacing
により、要素間の間隔が読みやすくなりました。
できる限り親Viewに巻き上げる
padding
はできる限り親Viewに巻き上げて付けたほうが読みやすいです。
今回は行っていませんが、 Image
+ Text
を部品化した際に、内部に padding
があると外から値が確認しづらくなるためです。
また padding
を内部に付けると部品を使い回しにくくなります。
今回は水平方向の padding
がすべて 24
なので、まとめて一番外側の VStack
に付けます。
import SwiftUI
struct FooView: View {
var body: some View {
VStack(spacing: 32) {
VStack(spacing: 8) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
- .padding(.horizontal, 24)
Text("This is Swift. Swift is very beautiful programming language.")
- .padding(.horizontal, 24)
}
VStack(spacing: 8) {
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
- .padding(.horizontal, 24)
Text("This is gear.")
- .padding(.horizontal, 24)
}
}
+ .padding(.horizontal, 24)
}
}
リファクタリング後のコード
リファクタリング後のコードです。
リファクタリング前より padding
と spacing
の使い分けができており、読みやすいと思います。
import SwiftUI
struct FooView: View {
var body: some View {
VStack(spacing: 32) {
VStack(spacing: 8) {
Image(systemName: "swift")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.red)
Text("This is Swift. Swift is very beautiful programming language.")
}
VStack(spacing: 8) {
Image(systemName: "gear")
.resizable()
.scaledToFit()
.frame(width: 44, height: 44)
.foregroundColor(.gray)
Text("This is gear.")
}
}
.padding(.horizontal, 24)
}
}
最終的に padding
の指定が1つのみとなり、本当に必要なのは「 FooView
全体に水平方向へ 24
の padding
を設ける」とわかりました。
FooView
を使い回す場合は内部から .padding(.horizontal, 24)
を外し、呼び出し時に padding
を付けます。
FooView()
.padding(.horizontal, 24)
おまけ: padding
は frame
に影響する
padding
を付けるとその分 frame
が変わります。
border
を付けるとわかりやすいです。
Before | After |
---|---|
![]() |
![]() |
要素間の間隔は frame
に影響させたくないことが多いので、その意味でも今回のリファクタリングは有用です。
また frame
が異なるのでリファクタリングと呼べるか怪しいですが、見た目は変わっていないため便宜上リファクタリングと呼んでいます。
逆に言えば、 frame
を変えたい場合は padding
を使う必要があります。
おわりに
読みやすい padding
を付けるコツの紹介でした。
誤っている箇所やほかにコツがあれば、コメントなどで教えていただけると嬉しいです