SwiftUI 構造体・メソッド・モディファイア完全攻略

EntryPane().tabItem { Label("記入", systemImage: "square.and.pencil") } を、見た瞬間に分解して理解できる自分へ」

このガイドは、Swift/SwiftUI のコードを “品詞分解” する感覚を身につけるための、実務直結のチートシート+サンプル大全です。型(構造体)、関数/メソッド、プロパティ、モディファイア、クロージャ、トレーリングクロージャ、some Viewextension ViewViewModifier まで、一気に腹落ちさせます。


目次

  1. ぱっと見で判別!チートシート
  2. Swift 基礎:型/構造体/関数/プロパティ/イニシャライザ
  3. クロージャ&関数型&引数ラベル&トレーリングクロージャ
  4. SwiftUI の正体:View と「モディファイア=Viewを返すメソッド」
  5. tabItemLabel を完全分解
  6. applyTwice/calc/framedSquare の完全分解
  7. 実行用ミニデモ(コピペOK)
  8. 10本ノック:即時判別ドリル(解答付き)
  9. 実務で効く“見極めコツ”
  10. 用語ミニ辞典(必要最小限を濃く)
  11. 次の一歩

1) ぱっと見で判別!チートシート

  • 構造体(struct)struct Name { ... } で宣言。型名は大文字始まり。初期化は Name(...)
  • メソッド(method):型の中の func name(...)小文字始まり。呼び出しは instance.name(...)
  • プロパティvar/let。計算型は { ... } を持つ。
  • モディファイア(modifier)extension View に定義された “View を返すメソッド”。見た目上は view.modifierName(...)ドット連結
  • イニシャライザ(init)init(...) / TypeName(...)
  • クロージャ{ ... }。最後の引数がクロージャなら trailing closure() の外に { } を置ける。
  • 大文字/小文字ルール型・プロトコルは大文字値・関数は小文字。まずはこれで8割判別。

2) Swift 基礎:型/構造体/関数/プロパティ/イニシャライザ

2.1 構造体+プロトコル+イニシャライザ

protocol Greetable {
    func greet() -> String
}

struct Person: Greetable {       // 構造体(型名は大文字)
    let name: String             // ストアドプロパティ
    init(name: String) {         // イニシャライザ
        self.name = name
    }
    func greet() -> String {     // メソッド(小文字始まり)
        "Hello, \(name)"
    }
}

let p = Person(name: "Taro")   // 構造体の初期化(TypeName(...))
print(p.greet())                 // メソッド呼び出し

2.2 extension と “既存型へメソッド追加”

extension String {
    func boxed() -> String {     // 既存型 String にメソッド追加
        "[\(self)]"
    }
}

"hi".boxed() // => "[hi]"

2.3 計算型プロパティ vs メソッド

struct Rect {
    var width: Double
    var height: Double

    var area: Double {           // 計算型プロパティ(()で呼ばない)
        width * height
    }

    func area() -> Double {      // メソッド(()が必要)
        width * height
    }
}

指針:**“値そのもの”ならプロパティ、“処理”**を強調したいならメソッド。


3) クロージャ&関数型&引数ラベル&トレーリングクロージャ

3.1 applyTwice を一文字ずつ分解

func applyTwice(_ f: (Int) -> Int, to x: Int) -> Int { f(f(x)) }
  • func:関数宣言。
  • applyTwice:関数名。
  • (_ f: (Int) -> Int, to x: Int)2引数
    • _ f: (Int) -> Int:外部名なし_/内部名f関数型(Int→Int)を値として受け取る。
    • to x: Int:外部名to/内部名x/型Int
  • -> Int:戻り値は Int
  • 本体 { f(f(x)) }xf を適用 → さらに f を適用。

💲

💲 $0 の意味(自動引数名)

の意味(自動引数名)

クロージャの引数名を省略すると、最初の引数は $0、2番めは $1 … で参照できます。

let doubled = applyTwice({ $0 * 2 }, to: 3)
// 1回目: { $0 * 2 }(3) == 6
// 2回目: { $0 * 2 }(6) == 12  → doubled == 12

3.2 calc と trailing closure

func calc(_ x: Int, operation: (Int) -> Int) -> Int { operation(x) }

let y = calc(10) { v in v + 5 }  // trailing closure(最後の引数がクロージャ)
// 等価: calc(10, operation: { v in v + 5 })

合図最後の引数がクロージャなら { ... }() の外へ出せる。


4) SwiftUI の正体:View と「モディファイア=Viewを返すメソッド」

4.1 モディファイアは“ただのメソッド”

extension View {
    func framedSquare(_ size: CGFloat) -> some View {  // 自作モディファイア相当
        self
            .frame(width: size, height: size)  // Viewメソッド(モディファイア)
            .border(.secondary)                // 同上:新しい View を返す
    }
}

Text("Hi").framedSquare(80)
  • モディファイアの正体extension View に定義された “View を返すメソッド”
  • 連結は メソッドチェーン。毎回 新しい View を返す(元の View は不変)。
  • some ViewOpaque Result Type。複雑化した具体型を呼び出し側から隠す

4.2 ViewModifier パターン

struct Badge: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding(8)
            .overlay(alignment: .topTrailing) {
                Circle().frame(width: 8, height: 8)
            }
    }
}

extension View {
    func badgeDot() -> some View { modifier(Badge()) }
}

Image(systemName: "bell").badgeDot()

modifier(_:)View のメソッド。メソッド=モディファイアという感覚でOK。


5) tabItem と Label を完全分解

TabView {
    EntryPane()                             // 構造体の初期化(some View)
        .tabItem {                          // View拡張メソッド(モディファイア)
            Label("記入", systemImage: "square.and.pencil") // Label構造体の初期化
        }

    Text("日記")
        .tabItem {
            Label("日記", systemImage: "book.closed")
        }
}
  • EntryPane()EntryPane のイニシャライザ
  • .tabItem { ... }View に生えたメソッド=モディファイア
  • { Label(...) }クロージャ(戻り値は Label 構造体のインスタンス)。

6) 完全分解 3 連発

6.1 applyTwice

func applyTwice(_ f: (Int) -> Int, to x: Int) -> Int { f(f(x)) }
// func …… 関数宣言
// applyTwice …… 関数名
// (_ f: (Int) -> Int, to x: Int) …… 2引数(外部名の有無と型を注視)
// -> Int …… 戻り値の型
// { f(f(x)) } …… 本体(x に f を2回適用)

6.2 calc & trailing closure

func calc(_ x: Int, operation: (Int) -> Int) -> Int { operation(x) }
let y = calc(10) { v in v + 5 }
// 最後の引数がクロージャ → () の外に { } を出せる
// 等価: calc(10, operation: { v in v + 5 })

6.3 自作モディファイア framedSquare

extension View {
    func framedSquare(_ size: CGFloat) -> some View {
        self                              // レシーバ(元の View)
            .frame(width: size, height: size) // Viewメソッド(モディファイア)
            .border(.secondary)               // さらにモディファイア
    }
}
  • _ size外部名なしで呼べる(view.framedSquare(80))。
  • CGFloat:UI座標/サイズで使う実数型。
  • some View:具体型を隠す。

7) 実行用ミニデモ(コピペOK)

import SwiftUI

// ① 関数とクロージャ(基礎)
func applyTwice(_ f: (Int) -> Int, to x: Int) -> Int { f(f(x)) }
func calc(_ x: Int, operation: (Int) -> Int) -> Int { operation(x) }

// ② View拡張(自作モディファイア)
extension View {
    func framedSquare(_ size: CGFloat) -> some View {
        self
            .frame(width: size, height: size)
            .border(.secondary)
    }
}

struct DemoView: View {
    var body: some View {
        VStack(spacing: 16) {
            Text("applyTwice → \(applyTwice({ $0 * 2 }, to: 3))") // 12
            Text("calc → \(calc(10) { $0 + 5 })")                 // 15
            Text("Hi").framedSquare(80)                           // 自作モディファイア
        }
        .padding()
    }
}

#Preview { DemoView() }

8) 10本ノック:即時判別ドリル(解答付き)

Q1

struct Chip: View { // ?
    var text: String // ?
    var body: some View { // ?
        Text(text) // ?
            .padding(.horizontal, 10) // ?
            .padding(.vertical, 6) // ?
            .background(.thinMaterial) // ?
            .clipShape(Capsule()) // ?
    }
}

Q2

extension View { // ?
    func capsuleBackground() -> some View { // ?
        self
            .padding(8) // ?
            .background(.ultraThinMaterial) // ?
            .clipShape(Capsule()) // ?
    }
}

Text("Hi").capsuleBackground() // ?

Q3

func makeLabel(_ title: String, systemImage: String) -> some View { // ?
    Label(title, systemImage: systemImage) // ?
}

TabView {
    Text("Home")
        .tabItem { makeLabel("ホーム", systemImage: "house") } // ?(2つ)
}

解答

  • Q1: 構造体 / ストアドプロパティ / 計算型プロパティ / Textのイニシャライザ / モディファイア / モディファイア / モディファイア / Capsuleのイニシャライザ(モディファイア引数)
  • Q2: extension / メソッド(View返す→モディファイア相当) / モディファイア / モディファイア / Capsuleイニシャライザ(モディファイア引数) / メソッド呼び出し(=モディファイア適用)
  • Q3: 関数 / Labelイニシャライザ / .tabItemはモディファイア、makeLabel(...)は関数呼び出し(中身は Label を返すクロージャ)

9) 実務で効く“見極めコツ”

  1. 大文字は型/小文字は値・関数TypeName(...) はイニシャライザ。
  2. ドット連鎖の小文字はだいたいメソッド。SwiftUI なら ほぼモディファイア
  3. { ... } を見たらクロージャを疑う。最後の引数なら trailing closure かも。
  4. “値っぽいか/動作っぽいか” でプロパティとメソッドを仕分け。
  5. Option-Click(⌥クリック)で宣言確認structfuncinit か一撃で分かる。

10) 用語ミニ辞典

  • 外部引数名/内部引数名func f(ex external: Type)。呼び出しは ex:、中では external
  • 関数型 (A,B)->C:関数という“値”の型。クロージャはこの型のインスタンス。
  • クロージャ:無名関数。{ (x: Int) -> Int in x + 1 }/省略で { $0 + 1 }
  • trailing closure:最後の引数がクロージャの時、() の外に {} を置ける。
  • some View:Opaque Result Type。具体型を隠して「ある View」とだけ約束。
  • CGFloat:UI座標/サイズで使う浮動小数点。
  • モディファイアView に生えたメソッド。毎回新しい View を返す(関数型スタイル)。

コメント

タイトルとURLをコピーしました