mcpField
U
uikit-expert

uikit-expert

github.com/ivan-magda開発8745 回インストール19 週間前に更新
リポジトリ

uikit-expert について

iOS インターフェースを実装、レビュー、改善する開発者向けの UIKit ガイドです。ワークフローと分野別チェックリストで Apple の API 指針を整理し、任意の性能調整より正確性を優先します。

主な機能

  1. 01分野別のレビューチェックリスト
  2. 02モダン API への置き換え指針
  3. 03ライフサイクルと Auto Layout の実践
  4. 04コレクションビューとナビゲーションのパターン
  5. 05メモリと並行処理の指針
  6. 06アクセシビリティと適応型 UI の指針

活用シーン

  1. 01UIKit コードの正確性レビュー
  2. 02非推奨 UIKit API のモダン化
  3. 03コレクションビューと画面遷移の構築
  4. 04アクセシビリティとメモリ管理の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ivan-magda/uikit-expert-skill --skill uikit-expert

インストール後、Claude Code で/uikit-expertを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

.tessl-plugin/
plugin.json
references/
adaptive-appearance.md
animation-patterns.md
auto-layout.md
cell-configuration.md
concurrency-main-thread.md
image-loading.md
keyboard-scroll.md
list-performance.md
memory-management.md
modern-collection-views.md
modern-uikit-apis.md
navigation-patterns.md
uikit-swiftui-interop.md
view-controller-lifecycle.md

詳細

UIKit エキスパートスキル

概要

ネイティブ API と Apple の公式ドキュメントに記載されたガイダンスを優先します。基本方針:アーキテクチャより事実を重視します(MVVM/VIPER/Coordinator を必須とはしませんが、テスト容易性のためにビジネスロジックは分離します)。まず正しさを確保し、その後にパフォーマンスを改善します。「必ず」や「決して」は正しさに関わる場合に限り、任意の最適化には「提案します」や「検討します」を使います。

ワークフロー

以下の 3 つのモードはいずれも、下記のコアガイドラインに基づきます。各トピックから、該当する参照ファイルに移動できます。次から開始点を選びます。

  • レビュー — レビューチェックリストを合否判定の基準として順に確認します。不合格の場合は、対応するコアガイドラインに従って修正します。正しさに関する項目(ライフサイクル、Auto Layout、メモリ、並行処理)をスタイルより優先し、パフォーマンスに関する項目は任意として扱います。
  • 改善 — 各問題に対して、非推奨 → モダンな方法の表にある置き換え方法と、対応するコアガイドラインを適用します。パフォーマンス改善(ダウンサンプリング、プリフェッチ、制約の頻繁な変更の修正)は、必須ではなく任意の最適化として提示します。
  • 新規実装 — 次の順序で実装し、各手順で該当するコアガイドラインを適用します。
    1. データフローの設計 — 所有する状態、注入される依存関係、モデルレイヤー
    2. ライフサイクル — 一度だけのセットアップは viewDidLoad、ジオメトリは viewIsAppearing
    3. Auto Layout — まとめて有効化し、変更を最小限にする
    4. コレクションビュー — DiffableDataSource + CompositionalLayout + CellRegistration
    5. ナビゲーション — 4 つすべての外観スロット、並行遷移のガード
    6. アニメーション、メモリ([weak self]、Task のキャンセル)、並行処理(@MainActor)
    7. 相互運用、画像読み込み、キーボード、Dynamic Type/VoiceOver/ダークモード
    8. iOS 26+ の機能は #available と適切なフォールバックで制御

コアガイドライン

ビューコントローラーのライフサイクル — references/view-controller-lifecycle.md を参照
  • 一度だけのセットアップ(サブビュー、制約、デリゲート)には viewDidLoad を使用します。ジオメトリには使用しません
  • ジオメトリに依存する処理、トレイトに基づくレイアウト、項目へのスクロールには viewIsAppearing(iOS 13+ にバックデプロイ済み)を使用します
  • viewDidLayoutSubviews は複数回呼び出されます。軽量なレイヤーフレームの調整にのみ使用します
  • viewWillAppear の用途は、トランジションコーディネーターによるアニメーションと、対応関係を保った通知の登録に限ります
  • すべてのライフサイクルのオーバーライドで必ず super を呼び出します
  • 子 VC の包含:addChild → addSubview → didMove(toParent:) — 必ずこの順序で行います
  • 開発中は deinit のログで解放を確認します
Auto Layout — references/auto-layout.md を参照
  • プログラムで作成したビューでは、必ず translatesAutoresizingMaskIntoConstraints = false を設定します
  • NSLayoutConstraint.activate([]) を使用し、個別に .isActive = true を設定することは避けます
  • 制約は一度だけ作成し、isActive を切り替えるか .constant を変更します。削除して再作成してはいけません
  • 実行時に優先度を .required(1000)から、または .required に変更してはいけません。999 を使用します
  • 制約をアニメーションする場合は、定数を更新してから、アニメーションブロック内でスーパービューに対して layoutIfNeeded() を呼び出します
  • iOS 26+:.flushUpdates オプションを使用すると、制約のアニメーションを簡略化できます
  • 再利用可能なセル内で、深くネストした UIStackViews は避けます
  • 重要な制約には constraint.identifier を設定し、Unable to simultaneously satisfy constraints のログを読みやすくします
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false   // required on programmatic views
view.addSubview(label)
NSLayoutConstraint.activate([                              // one batch = one solve pass
    label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
コレクションビューとデータソース — references/modern-collection-views.md、references/cell-configuration.md、references/list-performance.md を参照
  • 安定した識別子(完全なモデル構造体ではなく、UUID/データベース ID)を使って UICollectionViewDiffableDataSource を使用します
  • コンテンツの更新には reconfigureItems を使用し、reloadItems はセルの種類が変わる場合にのみ使用します
  • 初期データの読み込みには applySnapshotUsingReloadData を使用します(差分計算を回避します)
  • 単純でないレイアウトには UICollectionViewCompositionalLayout を使用します
  • UICollectionView.CellRegistration を使用します。文字列の識別子や手動の型キャストは使用しません
  • セルのコンテンツには UIContentConfiguration、セルの背景には UIBackgroundConfiguration を使用します
  • 状態に応じたスタイル設定(選択、ハイライト)には configurationUpdateHandler を使用します
// Store the registration once — never create it inside the cell provider (crashes on iOS 15+).
private lazy var cellRegistration = UICollectionView.CellRegistration<PhotoCell, Photo> { cell, _, photo in
    cell.configure(with: photo)
}

// Data source is keyed by the stable ID, so the provider receives an id — look up the model, then configure with it.
dataSource = .init(collectionView: collectionView) { [weak self] cv, indexPath, id in
    guard let self, let photo = self.photo(for: id) else { return nil }
    return cv.dequeueConfiguredReusableCell(using: self.cellRegistration, for: indexPath, item: photo)
}

var snapshot = NSDiffableDataSourceSnapshot<Section, Photo.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(photos.map(\.id))
dataSource.apply(snapshot, animatingDifferences: true)
ナビゲーション — references/navigation-patterns.mdを参照
  • 4つすべてのUINavigationBarAppearanceスロット(standard、scrollEdge、compact、compactScrollEdge)を設定します
  • viewWillAppearでnavigationBarに設定するのではなく、viewDidLoadでnavigationItem(VCごと)に外観を設定します
  • ディープリンクにはsetViewControllers(_:animated:)を使用します。順番にpushを呼び出す方法は使用しません
  • 同時遷移を防ぐため、push/popの前にtransitionCoordinatorを確認します
  • バーのprefersLargeTitlesは一度だけ設定し、VCごとにlargeTitleDisplayModeを使用します
アニメーション — references/animation-patterns.mdを参照
  • UIView.animate — 単純な一度限りのアニメーションに使用し、完了ハンドラーでfinishedを確認します
  • UIViewPropertyAnimator — ジェスチャー駆動で中断可能です。状態遷移(inactive → active → stopped)に従います
  • CABasicAnimation — レイヤー専用のプロパティ(cornerRadius、shadow、3D transforms)に使用し、先にモデル値を設定します
  • iOS 17以降のスプリングAPI:UIView.animate(springDuration:bounce:)はSwiftUIと動作がそろいます
  • 制約のアニメーション:レイアウトを更新 → 定数を変更 → スーパービューでlayoutIfNeeded()をアニメーションします
メモリ管理 — references/memory-management.mdを参照
  • すべてのエスケープクロージャで、デフォルトとして[weak self]を使用します
  • Timer:[weak self]を使用したブロックベースのAPIを使い、viewWillDisappearで無効化します
  • CADisplayLink:弱参照プロキシパターンを使用します(ブロックベースのAPIはありません)
  • NotificationCenter:クロージャで[weak self]を使用し、deinitでオブザーバーを削除します
  • 入れ子のクロージャ:保存する内側のクロージャで[weak self]を再取得します
  • デリゲート:AnyObject制約を付けて、必ずweak var delegate: SomeDelegate?とします
  • deinitで解放を確認します。呼び出されない場合は、強参照循環があります
並行処理 — references/concurrency-main-thread.mdを参照
  • UIViewControllerは@MainActorです。すべてのサブクラスメソッドは暗黙的にメインアクターになります
  • Task参照を保持し、deinitではなくviewDidDisappearでキャンセルします
  • await後にUIを更新する前にTask.isCancelledを確認します
  • Task.detachedはアクター分離を継承しません。UIには明示的なMainActor.runが必要です
  • バックグラウンドからDispatchQueue.main.syncを呼び出してはいけません。await MainActor.runを使用します
UIKitとSwiftUIの相互運用 — references/uikit-swiftui-interop.mdを参照
  • UIHostingController:子VCの完全な包含(addChild → addSubview → didMove)を行い、保存プロパティとして保持します
  • Auto Layoutコンテナーには sizingOptions = .intrinsicContentSize(iOS 16以降)を使用します
  • UIViewRepresentable:変更可能な状態はmakeUIViewではなくupdateUIViewで設定し、更新ループを防ぎます
  • コレクションビューのセル内にSwiftUIコンテンツを表示するには、UIHostingConfiguration(iOS 16以降)を使用します
画像の読み込み — references/image-loading.mdを参照
  • デコード後のビットマップサイズ = 幅 × 高さ × 4バイト(12MPの写真はRAMを約48MB使用します)
  • 表示サイズに合わせてImageIOでダウンサンプリングします。ビットマップ全体を読み込んでからサイズ変更してはいけません
  • iOS 15以降:非同期デコードにはbyPreparingThumbnail(of:)またはprepareForDisplay()を使用します
  • セルの再利用:prepareForReuseでTaskをキャンセルして画像をクリアし、完了時に識別情報を確認します
キーボードとスクロール — references/keyboard-scroll.mdを参照
  • UIKeyboardLayoutGuide(iOS 15以降)を使用し、コンテンツの下端をview.keyboardLayoutGuide.topAnchorに固定します
  • iPad:フローティングキーボードではfollowsUndockedKeyboard = trueを設定します
  • キーボード通知を手動で処理する方法はすべて、レイアウトガイドに置き換えます
アダプティブレイアウトとアクセシビリティ — references/adaptive-appearance.mdを参照
  • 非推奨のtraitCollectionDidChangeではなく、registerForTraitChanges(iOS 17以降)を使用します
  • Dynamic Type:UIFont.preferredFont(forTextStyle:)とadjustsFontForContentSizeCategory = trueを使用します
  • ダークモード:セマンティックカラー(.label、.systemBackground)を使用し、traitの変更時にCGColorを再解決します
  • VoiceOver:カスタムビューにaccessibilityLabel、accessibilityTraits、accessibilityHintを設定します
  • 複雑なリスト項目の操作にはUIAccessibilityCustomActionを使用します

クイックリファレンス — 非推奨 API → 最新 API

非推奨/レガシー最新の代替 API導入時期
traitCollectionDidChangeregisterForTraitChanges(_:handler:)iOS 17
キーボード通知UIKeyboardLayoutGuideiOS 15
cell.textLabel/detailTextLabelUIListContentConfigurationiOS 14
register +文字列による dequeueUICollectionView.CellRegistrationiOS 14
スナップショットでの reloadItemsreconfigureItemsiOS 15
barTintColor/isTranslucentUINavigationBarAppearance(4 つのスロット)iOS 13
(複雑な)UICollectionViewFlowLayoutUICollectionViewCompositionalLayoutiOS 13
アニメーション内での手動 layoutIfNeeded().flushUpdates オプションiOS 26
レガシーなアプリのライフサイクルUIScene + SceneDelegateiOS 26 で必須
ObservableObject +手動での無効化@Observable + UIObservationTrackingEnablediOS 18

レビューチェックリスト

ドメインごとに 1 つのゲートを設けます。確認すべき正確性の項目は、上記の該当するコアガイドラインを参照してください。

  • ライフサイクル — viewDidLoad でジオメトリを扱わない(viewIsAppearing を使用する)。すべてのオーバーライドで super を呼び出す。子 VC は addChild → addSubview → didMove の順に処理する。deinit で解放を確認する
  • Auto Layout — translatesAutoresizingMaskIntoConstraints = false を設定する。NSLayoutConstraint.activate([]) を使用する。頻繁な変更を避ける(isActive/.constant を切り替える)。layoutSubviews 内で setNeedsLayout() を呼び出さない(無限ループになります)
  • コレクションビュー — 差分更新可能で安定した識別子を使用する。reloadItems ではなく reconfigureItems を使用する。重複したスナップショット ID を作らない(BUG_IN_CLIENT クラッシュの原因になります)。文字列による dequeue ではなく CellRegistration を使用する
  • ナビゲーション — 4 つすべての外観スロットを設定する。navigationBar ではなく navigationItem に設定する。同時遷移を防ぐガードを設ける
  • アニメーション — 用途に合った API を使用する。制約のアニメーションでは flush → update → animate の順に処理する。CAAnimation では先にモデル値を設定する
  • メモリ — エスケープクロージャでは [weak self] を使用する。viewDidDisappear で Task をキャンセルする。デリゲートは weak にする。ネストしたクロージャで self を強参照し直さない
  • 並行処理 — await の後に Task.isCancelled を確認する。バックグラウンドから DispatchQueue.main.sync を呼び出さない。VC サブクラスに冗長な @MainActor を付けない
  • 画像 — 表示サイズに合わせてダウンサンプリングする。セルの読み込みは prepareForReuse でキャンセルし、ID を検証する。NSCache のサイズはデコード後のバイト数に基づいて設定する
  • 相互運用 — UIHostingController を保持し、完全な親子関係を構築する。updateUIView で更新ループを防ぐ
  • キーボード — 通知ではなく UIKeyboardLayoutGuide を使用する
  • 適応性/アクセシビリティ — registerForTraitChanges を使用する。Dynamic Type に対応する。トレイト変更時に CGColor を再解決する。カスタムビューに accessibilityLabel/Traits を設定する
  • iOS 26 以降 — フォールバックを備えた #available ガードを設ける。UIScene のライフサイクルを使用する。UIObservationTrackingEnabled の使用を検討する

参考資料

  • references/view-controller-lifecycle.md — ライフサイクルの順序、viewIsAppearing、子 VC の包含
  • references/auto-layout.md — まとめて有効化、制約の頻繁な変更、優先度、アニメーション、デバッグ
  • references/modern-collection-views.md — 差分可能なデータソース、コンポジションレイアウト、CellRegistration
  • references/cell-configuration.md — UIContentConfiguration、UIBackgroundConfiguration、configurationUpdateHandler
  • references/list-performance.md — 先読み、セルの再利用、reconfigureItems、スクロールのパフォーマンス
  • references/navigation-patterns.md — バーの外観、同時遷移、大きなタイトル、ディープリンク
  • references/animation-patterns.md — UIView.animate、UIViewPropertyAnimator、CAAnimation、スプリング
  • references/memory-management.md — 保持サイクル、[weak self]、Timer/CADisplayLink/ネストしたクロージャーの落とし穴
  • references/concurrency-main-thread.md — @MainActor、Task のライフサイクル、Swift 6、GCD からの移行
  • references/uikit-swiftui-interop.md — UIHostingController、UIViewRepresentable、サイズ調整、状態の橋渡し
  • references/image-loading.md — ダウンサンプリング、デコード済みビットマップの計算、セル再利用時の競合状態
  • references/keyboard-scroll.md — UIKeyboardLayoutGuide、スクロールビューのインセット、iPad のフローティングキーボード
  • references/adaptive-appearance.md — トレイトの変更、Dynamic Type、ダークモード、VoiceOver、アクセシビリティ
  • references/modern-uikit-apis.md — Observation フレームワーク、updateProperties()、.flushUpdates、UIScene、Liquid Glass

コメント