🍎

SwiftUIとUIKitのコードを比較して「宣言的UI」と「命令的UI」を理解する。

に公開

SwiftUIを学習を始めて、だいたい1年が経ちました。

学習を始めた当初、UIKitを少しだけ触ったことはあったのですが、すぐにSwiftUIへ移行しました。それ以来、ドキュメントや記事で何度も目にしてきた言葉があります。

  • 「SwiftUIは宣言的UIである」
  • 「UIKitは命令的UIである」

正直なところ、これまでこの言葉の意味を雰囲気でしか理解していませんでした。
また「SwiftUIの方がコードが短く書ける」という効率面の違いだと聞いたこともあります。

先日、「SwiftUIの宣言的UIってどういうこと?」と聞かれた時に答えることができませんでした。
「コード量が短くなる。宣言的UIはどうあるべきかの状態を宣言する。」と受け売りで説明していることに気がつきました。

実際に「ボタンを押すとカウントアップし、10を超えると赤文字になる」 という全く同じ機能を、UIKitとSwiftUIそれぞれで記述したコードを比較すれば、自分の中での理解も深まるのではないかと思いました。

比較したコード

2つのコードを見てください。

1. UIKit(命令的UI)

import UIKit
import SwiftUI //プレビューインポート用のフレームワーク呼び込み

class ViewController: UIViewController {

    var count = 0
    let countLabel = UILabel()
    let countButton = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        
        setupUI()
    }
    
    func setupUI() {

        countLabel.text = "0"
        countLabel.font = .systemFont(ofSize: 40)
        countLabel.textAlignment = .center
        
        //位置とサイズをピクセル単位で命令。 座標(0,200)に、幅は画面いっぱい、高さは50で配置。
        //この実装では0~ の数字
        countLabel.frame = CGRect(x: 0, y: 200, width: view.frame.width, height: 50)
        
        //countLabelを親画面に追加するように命令
        view.addSubview(countLabel)
        
        //この実装では増やす ボタン
        countButton.setTitle("増やす", for: .normal)
        countButton.titleLabel?.font = .systemFont(ofSize: 24)

        countButton.frame = CGRect(x: 0, y: 300, width: view.frame.width, height: 50)

        countButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)

        view.addSubview(countButton)
    }
    
    @objc func buttonTapped() {
        
        count += 1
        
        //データの変更は、画面に自動反映されないので下記で新しい数字に置き換えるよう命令。
        countLabel.text = "\(count)"
        
        // 色変えも手動。もし10を超えていたら、色を赤に塗り替えろ!と命令。
        if count > 10 {
            countLabel.textColor = .red
        }
    }
}

#Preview {
    ViewController()
}

2. SwiftUI(宣言的UI)

import SwiftUI

struct ContentView: View {
    // 「@State」をつけて変数を監視
    @State var count = 0

    var body: some View {
        VStack {
            // ここが宣言的
            // ラベルには count の値を表示すると宣言しておく
            Text("\(count)")
                .font(.system(size: 40))
                .foregroundColor(count > 10 ? .red : .black)

            Button("増やす") {
                count += 1
                // ここで「画面を更新しろ」という命令は不要。
                // システムが勝手に再描画してくれる。
            }
            .font(.title)
            .padding()
        }
    }
}

#Preview {
    ContentView()
}

1. それぞのコードで作成したUI

UIKitの実装
https://x.com/Perk_sh/status/1993603725164970051?s=20

SwiftUIの実装
https://x.com/Perk_sh/status/1993603821315019090?s=20


気づき1:「手順の指示」か、「状態の定義」か

UIKitのコードを書いて感じたのは、「プログラマーがすべてのプロセスを掌握している」 という感覚です。

buttonTapped メソッドの中を見ると:

 @objc func buttonTapped() {
        
        count += 1
        
        //データの変更は、画面に自動反映されないので下記で新しい数字に置き換えるよう命令。
        countLabel.text = "\(count)"
        
        // 色変えも手動。もし10を超えていたら、色を赤に塗り替えろ!と命令。
        if count > 10 {
            countLabel.textColor = .red
        }
    }
  1. 変数を増やす
  2. ラベルのテキストを書き換える
  3. 色を変えるか判定して適用する

これらすべてを、プログラマーが明示的に指示(命令)しています。「次は何をするか」という手順の主導権は完全にプログラマー側にあります。これが 「命令的」 と言われる所以のようです。

一方、SwiftUIではボタンの中に「画面を更新しろ」というコードはありません。
代わりに、Viewを作る段階で 「このテキストの色は、countが10を超えたら赤になるものである」 という ルール(定義) を記述しています。

「こうしなさい」と手順を細かく制御するのがUIKitで、「こうあるべき」と定義してあとはフレームワークに任せるのがSwiftUI。
どちらが良い悪いではなく、「制御の粒度」と「任せ方」が違う。

気づき2:データの同期に対する責任の所在

UIKitのアプローチは、データと見た目の連携を プログラマーが責任を持って管理する スタイルです。

count += 1
countLabel.text = "\(count)" // ここで同期させる

変数が変わったタイミングで、どのUIパーツをどう更新するか、すべて自由に決められます。例えば「内部データは更新するけど、あえて画面は更新しない」といった細かい制御も可能です。その自由度がある反面、更新の指示を書き忘れると画面が変わらないため、実装には緻密さが求められます。

対してSwiftUIは、@State を使うことで、データと見た目の同期(Binding)をシステムに委譲しています。
「データが変われば、画面も変わる」という仕組み自体をフレームワークが引き受けてくれるため、プログラマーは「どう同期させるか」を考える必要がなくなり、「どんな状態であるべきか」に集中できる。

管理が難しいというよりは、「手動で細かく操縦する(UIKit)」か「自動操縦システムに設定を入力する(SwiftUI)」かの違い に近い感覚を覚えました。

気づき3:座標への意識

レイアウトに関しても思想の違いが明確でした。
これはSwiftUIを書いていて一番最初に宣言的UIを感じるポイントです。

  • UIKit: CGRect(x: 0, y: 200, ...)
  • SwiftUI: VStack { ... }

UIKitでは、X座標、Y座標、幅、高さといった具体的な数値を指定して配置します。これは「ピクセル単位での厳密な配置」が可能であることを意味します。
一方、SwiftUIの VStack は構造を宣言するだけで、具体的な座標計算はフレームワークが行います。

これも、詳細な配置を自分でコントロールしたい(UIKit)か、構造だけ決めてレスポンシブな配置は任せたい(SwiftUI)か、というスタンスの違いが現れています。

まとめ

今回の比較を通して、UIKitとSwiftUIの違いはコードの長さだけではないことが分かりました。
意識することのなかった宣言的UIも命令的UIと比較することで少しだけ理解することができました。

  • UIKit(命令的):
    「どう処理するか(How)」をプログラマーが逐一記述する。
    具体的な手順と制御を重視するアプローチ。

  • SwiftUI(宣言的):
    「何を表示するか(What)」というルールを記述する。
    状態と構造の定義を重視するアプローチ。

SwiftUIを学ぶ上で、「なぜSwiftUIはこういう書き方をするのか」という疑問の答えが、UIKitのアプローチを知ることでより鮮明になりました。
「自動(SwiftUI)」の便利さを享受しつつ、「手動(UIKit)」で何が行われていたのかを知っておくことは、より深い理解につながると感じました。

Discussion