1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftUI のシート切り替えで「設定したはずの画面が出ない」問題と対処法

Posted at

よくある状況

たとえば以下のようなコードを書いたとします。

SwiftUI
@State private var currentSheet: SheetType = .name
@State private var isShowSheet: Bool = false

enum SheetType {
    case iconImage, name
    // ... ほかの画面
}

// どこかのボタンアクション
Button("アイコン画像を編集") {
    currentSheet = .iconImage
    isShowSheet = true
}

ところが、実行してみると currentSheet を .iconImage に設定しているのに、なぜか .name の画面が出てしまう という現象が起きることがあります。

SwiftUI では「同じアクション内で順番どおりに書いた State の変更」が 必ずしも意図した順序で反映されるとは限らない ケースがあるためです。
特に、すでにシートが表示されている状態で別のシートに切り替える、などの状況が絡むと、競合が起きやすくなります。

原因: SwiftUI の State 反映タイミング

SwiftUI は「宣言的 UI」であり、View の再描画が「命令した順」に厳密に行われるわけではありません。
通常は「1つのアクション内の State 変更」がまとめて1回の再描画サイクルになり、結果として

SwiftUI
currentSheet = .iconImage
isShowSheet = true

が同時に反映され、.iconImage のシートが開きます。
しかし何らかの事情でシートがまだ閉じきっていなかったり、システム的な割り込みが入ったりすると、
期待通りに「currentSheet を更新 → isShowSheet = true」という順番が適用されないことがあります。

解決策: .onChange(of: someValue) を使って「enum が変わったらシートを開く」

以下のように currentSheet を Optional にしておき、.onChange(of: currentSheet) で「値が入ったらシートを開く」ようにします。

SwiftUI
@State private var currentSheet: SheetType? = nil
@State private var isShowSheet: Bool = false

enum SheetType {
    case iconImage, name, memory, ...
}

var body: some View {
    VStack {
        Button("アイコン画像") {
            currentSheet = .iconImage
        }
    }
    .onChange(of: currentSheet) { newValue in
        // currentSheet が nil でなければシートを開く
        isShowSheet = (newValue != nil)
    }
    .sheet(isPresented: $isShowSheet) {
        if currentSheet == .iconImage {
            IconImageEditView(...)
        } else if currentSheet == .name {
            NameEditView(...)
        }
        // 他のケースは else if ...
    }
}
1
1
1

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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?