1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUI Tutorialsから学ぶSwiftUI〜その1〜

Last updated at Posted at 2021-07-26

はじめに

本記事は、Apple公式のSwiftUI Tutorialsを参考として内容をまとめた記事です。
英語が苦手な方や、概要をさっと理解したい方を対象に本記事を執筆しております。

注釈コメントを入れたプロジェクトファイルはこちら(GitHub)


Creating and Combining Views

Creating and Combining Views

3つのコードエディタ

SwiftUIで記述するプログラムは、「ソースエディタ」「キャンバス」「インスペクタ」の3箇所から編集することができる。

ソースエディタ(source editor)

ソースエディタ.png

キャンバス(canvas)

キャンバス.png

インスペクタ(inspector)

インスペクタは、ソースエディタまたはキャンバスで、編集対象を⌘(command) + ⌥(control)を押したままクリックすると表示することができる。
インスペクタ.png

ビュー(view)

ビューは、アプリケーション画面を構成する「画面部品」を指し、Viewプロトコルに準拠する構造体の内部で記述する。
また、ビュー毎に用意された**修飾子(modifier)**を付与することで、自由自在に変更することができる。

ただし、ビューがもつbodyプロパティは一つのビューしか返せないため、複数ビューをもたせる場合は後述するスタックでグループ化する必要がある。

基本的なビューと、その修飾子を以下に表す。

ビュー(内容) 修飾子 内容
共通 foregroundColor
frame サイズ
padding パディング
Text(ラベル) font フォント
lineLimit 表示行数
truncationMode 省略位置
fixedSize 水平・垂直方向のサイズ固定
Image(画像) resizable サイズの可変化
aspectRatio アスペクト比
renderingMode 色の可変化
clipShape フレームの図形
overlay 上乗せするビュー
shadow 影の半径
Button(ボタン) border 枠線
Path(2次元図形) stroke 線色
fill 塗りつぶし色
Spacer(スペーサー)
List(リスト)
Section(セクション)
NavigationView(ナビゲーションビュー) navigationBarTitle ナビゲーションバーのタイトル
NavigationLink(遷移先)
TabView(タブビュー) tabItem タブ
ScrollView(スクロールビュー)
TextField(テキストフィールド) textFieldStyle テキストフィールドのスタイル

スタック(Stack)

スタックは、複数のビューをx, y, z方向にグループ化する。

主なスタックは、以下の通り。

スタック 方向 内容
HStack x 全てのビューを水平方向にグループ化して表示
LazyHStack x 画面内のビューを水平方向にグループ化して表示
VStack y 全てのビューを垂直方向にグループ化して表示
LazyVStack y 画面内のビューを垂直方向にグループ化して表示
ZStack z 全てのビューを垂直方向にグループ化して表示

MapKit

MapKitは、ビューにマップを埋め込むのに用いるフレームワークであり、マップを表すMapビューと、マップに関連する各構造体を提供する。

座標(coordinate)

MapKitにおける座標は、座標を表すCLLocationCoordinate2Dと、縮尺を表すMKCoordinateSpanで構成されるMKCoordinateRegionで表現する。

値型データのバインド($)

値型データを代入する場合、通常は変数のが代入されるが、参照する値型データに$を付与することで、変数として代入できる。


Building Lists and Navigation

Building Lists and Navigation

ビュー(view), モデル(model), リソース(resource)

プロジェクトファイルの管理において、ファイルの用途・機能によって以下のように分類するのが望ましい。

分類 用途・機能
ビュー ビューを表すswiftファイル
モデル ビューに反映するデータ構造を記述するswiftファイル
リソース データを格納・記述するファイル(jsonXMLなど)

JSON解析

JSON解析は、以下の手順で行う。

  1. Bundleからファイルパス(=URLオブジェクト)を取得
  2. Data型オブジェクトにJSONデータを格納
  3. JSON解析を行うJSONDecoderクラスを用いたデコード処理の実施

ここで、JSONデータの解析結果を格納する構造体は、
DecodableEncodableの2つの構造体を包含するCodableに準拠させることで、
JSONデータのキーと同名である構造体のプロパティに値を格納することができる。

また、JSONデータを格納した構造体リストを走査する場合は、HashableIdentifiableプロトコルに準拠させる。

定義

// 1. Bundleからファイルパスを取得
Bundle.main.url(
    forResource name: String?, 
    withExtension ext: String?
) -> URL?
// パラメータ
// name: リソースファイル名
// ext: ファイル拡張子(通常はnil)
// -> extをnilに設定した場合は、拡張子も含めてファイル名を記述

// 2. Data型オブジェクトの生成・格納
// -> 全項目イニシャライザ(memberwise initializer)によって、optionsパラメータの記述は不要
Data(contentsOf url: URL, options: Data.ReadingOptions = []) throws
// パラメータ
// url: ファイルパスを表すURLオブジェクト

// 3. JSONDecoderを用いたデコード処理
JSONDecoder#decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable
// パラメータ
// type: 返却型
// -> 通常は"<返却型>.self"と記述し、<返却型>自身の型を指定
// data: JSONデータを格納するData型オブジェクト

サンプルコード(定型文)

JSON解析
// JSON解析処理
func load<T: Decodable>(_ filename: String) -> T {
    let data: Data

    // ファイルパスの取得
    // -> プロジェクトファイルはBundleに格納される
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }

    // JSONファイル -> Data型 への変換
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }

    // Data型 -> Codable型に準拠した構造体 への変換(JSON解析)
    // -> <返却型>リストの各プロパティに一対一で対応するJSONデータが代入
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

表示デバイスに応じた動的プレビュー

画面サイズの異なるデバイスに応じた動的プレビューを行う場合、ビューを描画するループ処理を行うForEach構造体と、
キーパス(key path)の基点となるインスタンス自身を表すアイデンティティキーパス(\.self)を用いて、デバイス毎にビューを生成する。

サンプルコード

デバイスに応じた動的プレビュー
struct LandmarkList: View { ... }

struct LandmarkList_Previews: PreviewProvider {
    static var previews: some View {
        // デバイスに応じた動的プレビュー
        // -> Identifiableに準拠していないリストを走査する場合は、キーパス式で記述
        // <= リスト・セット・辞書などのコレクションを走査する場合、各要素は"id"プロパティをもつ必要がある
        //    Identifiableに準拠しない場合は、アイデンティティキーパス(\.self)を用いてidを指定
        ForEach(["iPhone SE (2nd generation)", "iPhone XS Max"], id: \.self) { deviceName in
            LandmarkList()
                // プレビューするデバイスの指定
                .previewDevice(PreviewDevice(rawValue: deviceName))
                // プレビュー名の指定
                .previewDisplayName(deviceName)
        }
    }
}

Handling User Input

Handling User Input

Combine

Combineは、値の変更を監視するPublisher値を更新するOperator更新後の値を取得するSubscriberを提供する
フレームワークであり、「値の変更」を非同期イベントとして監視し、値の変更に伴う更新処理を自動で行う。

値の変更を監視するデータには@Publishedプロパティラッパを付与し、
監視データを保持するクラスをfinalクラスとしてObservableObjectプロトコルに準拠させる。

プロパティラッパ(property wrapper)

プロパティラッパを用いることで、値型データとして扱われる構造体(ビュー)の内部でプロパティの値にアクセスすることができる。

SwiftUIで用意されているプロパティラッパは、以下の通り。

プロパティラッパ データ型 読み書き 管理ビュー
プロパティ 値型 読み込み -
@State 値型 読み書き ビュー自身
@AppStorage 値型 読み書き アプリケーション自身
@SceneStorage 値型 読み書き シーン自身
@Binding 値型 読み書き 外部ビュー
@StateObject 参照型 - ビュー自身
@ObservedObject 参照型 - 親ビュー
@EnvironmentObject 参照型 - ビュー自身
外部ビュー
@Environment 値型・参照型(環境値) 読み込み ビュー自身
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?