KINTO Tech Blog
SwiftUI

【SwiftUI】独自のStyleを定義してスタイリッシュなコードを書こう

Cover Image for 【SwiftUI】独自のStyleを定義してスタイリッシュなコードを書こう

この記事は KINTOテクノロジーズアドベントカレンダー2024 の11日目の記事です🎅🎄


メリークリスマス✌️🎅
KINTOテクノロジーズで my route(iOS) を開発しているRyommです。
本記事ではカスタムスタイルの紹介をします。

はじめに

私がこの書き方を知ったのはApp Dev Tutorialsがきっかけです。

https://developer.apple.com/tutorials/app-dev-training/creating-a-card-view#Customize-the-label-style

なんてスタイリッシュなんだ…!

カスタムスタイルを使うことで、SwiftUIのコードが格段に読みやすく、洗練されたコードになる…!私もスタイリッシュなコードを書きたい!

そんな衝動に駆られて使い始めましたが、実際かなり便利で読みやすいのでおすすめです。
特に、用途別に乱立した構造体名を覚えていなくても ~~Style() にドットで候補を探せるところが気に入っています。

候補がサジェストされる様子

カスタムスタイルのつくりかた

例えばLabelのカスタムスタイルを作成する場合、 LabelStyle を継承した構造体を作成し、プロトコルに準拠したメソッド(ここでは makeBody(configuration:) )内にスタイルを定義します。

configurationに含まれる値はものによって異なるので都度調べる必要がありますが、LabelStyleConfigurationに関してはTextとImageのViewが入っています。

/// 文字+アイコン のラベルスタイル
struct TrailingIconLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.title
            configuration.icon
        }
    }
}

さらに LabelStyle を拡張して、作成したカスタムスタイルを静的プロパティとして追加すると、呼び出し時に .labelStyle(.trailingIcon) のように呼び出すことができて可読性が向上します。ン〜スタイリッシュ!

extension LabelStyle where Self == TrailingIconLabelStyle {
    static var trailingIcon: Self { Self() }
}

もし「spaceを指定したい」など、引数を持たせたい場合はカスタムスタイルにメンバプロパティを追加することで実現できます。

/// 文字+アイコン のラベルスタイル
struct TrailingIconLabelStyle: LabelStyle {
    // デフォルト値を設定しておくとドット始まりの呼び出し方法もキープできる
    var spacing: CGFloat = 4

    func makeBody(configuration: Configuration) -> some View {
        HStack(spacing: spacing) {
            configuration.title
            configuration.icon
        }
    }
}
// 呼び出し
Label().labelStyle(.trailingIcon) // spaceにはデフォルト値が使われる
Label().labelStyle(TrailingIconLabelStyle(spacing: 2)) // spaceを2に指定

使いどころ

アプリ全体で広く使う共通デザインや、上記の TrailingIconLabelStyle のように普遍的なカスタムスタイルに使うと良いでしょう。

たとえば、my routeではProgressViewで使っています。
ProgressView自体のスタイル設定もですが、ProgressViewを表示中に背景をグレーっぽくするのもスタイルに含めることができます。

struct CommonProgressViewStyle: ProgressViewStyle {
    func makeBody(configuration: Configuration) -> some View {
        ZStack {
            ProgressView(configuration)
                .tint(Color(.gray))
                .controlSize(.large)
            Color(.loadingBackground)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
}

extension ProgressViewStyle where Self == CommonProgressViewStyle {
    static var common: Self { Self() }
}

ちなみに、ProgressViewに background() を指定するとProgressViewに必要なサイズのみしか描画されないので、ZStackでColorをProgressViewの下に敷き、背景色が与えられたサイズ全体に広がるようにしています。

このようにスタイルを作成することで、以下のように簡潔でスタイリッシュに書けるようになりました。

struct SomeView: View {
    @State var loadingStatus: LoadingStatus

    var body: some View {
        SomeContentView
        .overlay {
            if loadingStatus == .loading {
                ProgressView()
                    .progressViewStyle(.common)
            }
        }
        .disabled(loadingStatus == .loading)
    }
}

おわりに

カスタムスタイルの紹介でした!

以下のページにあるものはカスタムスタイルを作成できます。

https://developer.apple.com/documentation/swiftui/view-styles

よりスタイリッシュなコードを目指して一歩前進 🦌 🎄

Facebook

関連記事 | Related Posts

We are hiring!

【iOSエンジニア】モバイルアプリ開発G/東京

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【iOSエンジニア】モバイルアプリ開発G/大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。