状態変化を監視する変数は body View の上で宣言する
body内で宣言すると状態変化しない(状態変化する時にViewを表示し直すため)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct test: View { @State var aaa = 0 //OK var body: some View { @State var bbb = 0 //NG Button(action: { aaa += 1 bbb += 1 }) { Text("\(String(aaa))") } Text("\(String(bbb)") } } |
.fullScreenCover()等のシート修飾子の位置
SwiftUIの.sheet(), .fullScreenCover(), .popover() などのシート修飾子は、原則としてトリガーとなるView(ここではButton)と同じ階層か、その親Viewに適用するのが一般的です。
シート修飾子を**Buttonの直下に適用するのは良い方法**です。これにより、どのボタンがどのシートを表示するのかがコード上で明確になります。
SwiftUIの.fullScreenCover()などのシート修飾子の配置は、慣れるまで混乱しやすいポイントの一つです。
「表示のトリガーとなるビュー(Button)に直接つける」、あるいは**「そのビューを囲むコンテナ(VStackなど)の直後につける」**と覚えておくと、今後もスムーズに開発を進められると思います。
適用場所のベストプラクティス
基本的なルールは、シートの表示を制御する状態変数 (@State var isShowingSheet: Bool) が定義されているViewの直下(そのbodyのトップレベルの子Viewの直後)に修飾子を付けるのが最も見通しが良く、推奨されます。
1. Button直下に適用する例(推奨される方法)
トリガーとなるButtonに直接修飾子を適用します。これが一番シンプルでわかりやすいパターンです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
struct ContentView: View { @State private var isShowingCover = false var body: some View { VStack { Text("メインコンテンツ") // ✅ Buttonに直接 .fullScreenCover() を適用 (OK) Button("フルスクリーン表示") { isShowingCover = true } .fullScreenCover(isPresented: $isShowingCover) { FullScreenView() } // 他のViewがあれば続く Spacer() } } } |
2. コンテナView(VStack, ZStackなど)の直下に適用する例
ボタンがコンテナ内にあり、修飾子をコンテナ全体に適用しても動作します。
⚠️ 避けるべき場所
修飾子をVStackやHStackなどのコンテナのコンテンツ内(VStack { ... } の中括弧内)に適用しようとすると、複数のViewに適用しようとしていると見なされてエラーになるため注意してください。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
struct ContentView: View { @State private var isShowingCover = false var body: some View { VStack { Text("メインコンテンツ") Button("フルスクリーン表示") { isShowingCover = true } // ❌ ここに .fullScreenCover() を適用するとエラーになる // .fullScreenCover(isPresented: $isShowingCover) { ... } } } } |
まとめ
階層が深くなっても、Buttonと対応する@State変数が存在するViewで、トリガーとなるView(Button)の直後に修飾子を付けるのが、最もシンプルで推奨される方法です。迷ったら「表示させるボタンの直後」と覚えておくと良いでしょう。👍
id: \.self
ForEachでコレクションを一つずつ取り出して処理する時にviewのidに第一引数と同じものを与える?
このForEachの\.selfで動的に複数のListViewを作った場合挙動がおかしくなる。その場合はUUIDで構成したIDを割り当てる。
参考記事: https://zenn.dev/kntk/articles/1f1b40da6fe181
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
let aaa: [String] = ["a", "b", "c"] ForEach(aaa, id: \.self){ bbb in Text("\(bbb)") } //テキストが a b c で作成されると同時にtextのidもa b cに振られる? //動的にList等のViewを作成する時は下のようなUUID構造体でIDに割り振らないと動作がおかしくなる struct aaa: Identifiable{ var id = UUID() //変数名は id のみ有効 var bbb: String } struct ContentView: View{ @State var ccc: [aaa] = [aaa(bbb: "あああ"), aaa(bbb: "いいい"), bbb(ccc: "ううう")] var body: some View { List{ ForEach (ccc) { a in Button(action: { }){ Text(a.bbb) } } } } } |
Data型
Swift の Data は
- バイト列(UInt8 の配列)
- つまり
[UInt8]のようなもの
例)
|
1 |
var d = Data([0x00, 0xFF, 0x12]) |
添え字アクセスも 1バイトだけ に使える:
|
1 |
d[0] = 0x10 // OK(1バイトの数値) |
つまり Data の添え字には
入れられるのは「0〜255 の UInt8 だけ」
構造体、クラスの違い
構造体はインスタンス化せず使える値型
クラスはインスタンス化しないと使えない参照型
クラス、構造体の使い分け
🔹クラス vs 構造体(API取得コードの場合)
| 用途 | 向いている | 理由 |
|---|---|---|
| API通信やネットワーク管理 | ✅ クラス(class) | 状態を保持(例: セッション、リトライ回数、認証トークンなど)しやすい。参照型なので複数箇所で共有できる。 |
| データモデル(受信したJSON構造など) | ✅ 構造体(struct) | 値型なので安全で軽い。Codable対応もしやすく、スレッド間の共有にも安全。 |
- class:状態や継続的な管理(APIClient、TokenManagerなど)
- struct:一時的なデータやJSONモデル
- enum:APIの種類・エラー種別の定義などに便利
クラスの初期化順メモ
viewのカスタマイズは三項演算子で行う
SwiftUIのif文はオブジェクトを一度インスタンス化してから分けるのでその分処理が無駄になる
プロトコルとは
C++とかの抽象クラス的な機能
待機処理
Task{}を使いTask.sleep()
|
1 2 3 4 5 6 |
Button("1秒後に実行") { Task { try? await Task.sleep(for: .seconds(1)) print("1秒後に実行された!") } } |
❌ やってはいけない方法
sleep(1)
→ UIフリーズしてアプリ固まる
DispatchQueue.main.asyncAfter(…)
→ Swift Concurrency 時代だとあまり推奨されず
→ 同期保証が弱くなりバグることがある
(ただし完全にNGではなく、従来コード互換で動く)
非同期
Task{} を作る → すぐにバックグラウンドで開始
関数で async にすればTask{}使わず await でおk
Listの背景色modifierについて
List内の背景色はすこし特殊で本体部と個別Viewを設置する【行】の背景色とで別れていて記述も通常と少し違う
文字色は通常通りの設定で可能
|
1 2 3 4 5 6 |
List { Text("あああ") .listRowBackground(Color.blue) //Textの行の背景色 } .scrollContentBackground(.hidden) //一度本体部を透明にする .background(Color.gray) //透明にしてから色を設定する |

Viewに変数を渡した時の#Previewの書き方について
初期化時にプロパティを渡す子ビューのプレビューを作成するには、#Previewマクロの中で、そのビューのイニシャライザ(init)に必要な引数を指定します。
基本的な書き方
たとえば、TestViewがmessage: Stringというプロパティを持っている場合、プレビューは以下のように書きます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import SwiftUI struct TestView: View { let message: String // 初期化時に必須のプロパティ var body: some View { Text(message) } } #Preview { // プレビューの中で、TestViewの初期化子に必要な引数を指定する TestView(message: "プレビュー用のメッセージ") } |
#Previewマクロのブロックの中は、ビューを返すクロージャとして扱われます。このため、TestView()のように引数を省略して呼び出すことはできず、TestView(message: "...")のように正しい引数を渡す必要があります。
複数のプレビューを作成する場合
さまざまな状態をテストするために、複数のプレビューを定義することもできます。
|
1 2 3 4 5 6 7 |
#Preview("通常") { TestView(message: "通常メッセージ") } #Preview("長いメッセージ") { TestView(message: "これは非常に長いメッセージです。テキストの折り返しを確認します。") } |
プレビューでダミーデータを使用する場合
プレビュー専用のダミーデータ(MockDataなど)を作成しておくと、より柔軟なプレビューが可能になります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import SwiftUI struct User { var name: String } struct UserView: View { let user: User var body: some View { VStack(alignment: .leading) { Text("ユーザー名:") Text(user.name).font(.headline) } } } // プレビュー用のダミーデータ let mockUser = User(name: "テストユーザー") #Preview { UserView(user: mockUser) } |
つまりプレビュー用に変数名の右は何でも良いから書いておけばおk
if文の後に【 , 】で && の代わりになる
Swift特有の文法 guard let でよく使われるらしい。
|
1 2 3 4 5 6 7 |
if let data = try KeychainHelper.read(service: service, account: account), let saved = try? JSONDecoder().decode(SavedToken.self, from: data), saved.expiry > Date() { self.token = saved.token self.expiry = saved.expiry return saved.token } |

コメント