はじめに
SwiftUIを使用し、アプリを作成するとき、画面の処理の仕方がどうなるかを調査しました。
作成例
例
チェックをONにしたら、その項目の取り消し線を引くという例です。
SwiftUIにはHTMLのチェックボタンずばりはありませんでした。
ToggleというUIコンポーネントが今回使用できそうなので代わりに使用しました。
上記画面のソース
ソース例を記載します。
import SwiftUI
// Task構造体: タスクのデータモデルを定義。IdentifiableとEquatableプロトコルに準拠しています。
struct Task: Identifiable, Equatable {
let id: Int // タスクの一意識別子
var title: String // タスクのタイトル
var checked: Bool // タスクが完了したかどうかの状態
}
// ContentView: アプリのメインビューを定義する構造体。
struct ContentView: View {
@State private var tasks = [ // タスクの配列を管理する状態変数。@Stateにより、UIが自動的に更新されます。
Task(id: 1, title: "散歩", checked: true),
Task(id: 2, title: "料理", checked: false),
Task(id: 3, title: "筋トレ", checked: true)
]
var body: some View {
List { // リストビューを使用して、タスクの一覧を表示。
ForEach($tasks) { $task in // $tasksを使用して、タスク配列の各要素に対するバインディングを生成し、ListRowに渡す。
ListRow(taskTitle: $task.title, isChecked: $task.checked) // ListRowビューを使用して、各タスクを表示。
}
}
}
}
// ListRow: リストの各行を定義するビュー。タスクのタイトルとチェック状態を表示する。
struct ListRow: View {
@Binding var taskTitle: String // タスクタイトルのバインディング。外部からの変更を受け入れる。
@Binding var isChecked: Bool // チェック状態のバインディング。外部からの変更を受け入れる。
var body: some View {
Toggle(isOn: $isChecked) { // トグルスイッチ。isCheckedの状態に基づいてON/OFFを切り替える。
if isChecked {
Text(taskTitle) // タスクがチェックされている場合、タイトルに取り消し線を表示。
.strikethrough()
.fontWeight(.ultraLight)
} else {
Text(taskTitle) // タスクがチェックされていない場合、通常のタイトルを表示。
}
}
}
}
#Preview {
ContentView()
}
上記の動き
SwiftUIのアプローチは以下のようになりました。
-
ON/OFFの切り替えと取り消し線の表示:
@Binding
を使用してListRow
ビュー内のisChecked
状態を監視し、この状態に基づいてToggle
スイッチのON/OFFを反映し、条件に応じてText
に取り消し線を表示します。これはデータバインディングと宣言的UI記述の例です。 -
データの表示:
@State
プロパティラッパーを使用してContentView
内のtasks
配列の状態を管理し、ForEach
を使って配列の各要素に対してループ処理を行い、データをビューにバインドします。これは状態管理と宣言的UI記述の例です。 -
画面構成:
ContentView
とListRow
ビューを定義して、アプリのUIを構成します。ContentView
がアプリのメインビューを表し、ListRow
がリストの各行を表すコンポーネントとして機能します。これはコンポーネント化の例です。
SwiftUIのアプローチ
SwiftUIで対応した特徴として、宣言的UI記述、状態管理、コンポーネント化、そしてデータバインディングがありました。WebのフロントのフレームワークのReactやVueに似ています。これらの概念を組み合わせることで、データの変更がUIに即座に反映され、ユーザーインタラクションを効率的に扱うことができます。
今回のアプリに当てはめると以下になります。
-
宣言的UI記述:
ContentView
とListRow
のビュー定義により、UIの構造と表示ロジックが明確に宣言されています。 -
状態管理:
@State
プロパティラッパーにより、UIの状態が内部的に追跡され、状態の変更に応じてUIが自動的に更新されます。 -
コンポーネント化:
ListRow
ビューにより、再利用可能なUIコンポーネントが作成され、UIの再利用性と保守性が向上します。 -
データバインディング:
@Binding
プロパティラッパーにより、親ビューから子ビューへのデータの双方向バインディングが実現され、UIコンポーネント間でデータが効率的に同期されます。
SwiftUIの設計思想は、宣言的UIプログラミングパラダイムに根ざしています。このアプローチは、UIの状態と表示を密接に連携させ、状態の変更が直接UIの更新に反映されるようにすることを目指しています。
-
状態の一元管理:
@State
,@Binding
,@ObservedObject
などのプロパティラッパーを使用することで、アプリケーションの状態を一元管理しやすくなります。これにより、状態の変更がUIのどの部分に影響を与えるかをシステムが把握し、必要な部分のみを効率的に更新できます。 -
データフローの明確化: データのバインディングと状態管理の仕組みにより、データがアプリケーションを通じてどのように流れるかが明確になります。これにより、バグの発生を減らし、コードの可読性と保守性を向上させることができます。
-
UIとロジックの密接な結合: UIの構造と表示ロジックを宣言的に記述することで、UIの見た目と動作を一箇所で管理できます。これにより、開発プロセスが簡潔になり、迅速なイテレーションが可能になります。
おわりに
Swiftの構造体、オプショナル型というSwiftに特徴的な型を踏まえた記載等も追加したかったですが、内容が広範囲になりまとまりもなくなるため割愛しました。SwiftとSwiftUIに詳しくない方々にも、これらの技術がどのように動作し、どのようなメリットを提供するのかざっくりわかれば幸いです。
参考記事