4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

(SwiftUI).sheetの使い分けが難しかった。

Last updated at Posted at 2023-07-27

2種類の.sheet

始めにみなさんご存知かと思われますが、swiftUIでモーダル遷移させるsheetには2種類あります。それが下の2つです。
1...sheet(isPresented:onDismiss:content:)

2...sheet(item:onDismiss:content:)

大きな違いは引数が.sheet(isPresented:..)になっているか、それとも.sheet(item:..)になっているかの違いです。「isPresented」はバインディングされたBool値がtrueになることでシートを表示させ、「item」はIdentifiableプロトコルに適合した任意の型が代入されることでシートを表示させます。

つまずき

自分の理想とする挙動は、動画1のように選択された項目の値を次のViewへ渡し、TextField内で表示させることです。
しかし、.sheet(isPresented:..)のシートを使用すると、動画2のように、どの項目をタップしたとしても、最初に表示されるのは、fruitIndexの初期値である「0」番目の「りんご」が表示されてしまいます。しかし、その後、他の項目をタップすると、ちゃんと動いてくれます。

動画1
iPhone2.gif

動画2
iPhone.gif

問題のコード

struct IsPresentedSheet: View {
    
    @State private var fruits = [
        Fruit(name: "りんご"),
        Fruit(name: "もも"),
        Fruit(name: "レモン"),
        Fruit(name: "ぶどう")
    ]
    @State private var isSheetPresented = false
    @State private var fruitsIndex =  0
    
    var body: some View {
        NavigationStack{
            List(fruits.indices, id: \.self) { index in
                HStack{
                    Text(fruits[index].name)
                    Spacer()
                    Button {
                        fruitsIndex = index
                        isSheetPresented = true
                    } label: {
                        Image(systemName: "info.circle.fill")
                    }
                }
            }
            .sheet(isPresented: $isSheetPresented) {
                SheetView(
                    fruitNewItem: fruits[fruitsIndex].name
                )
            }
        }
    }
}

struct SheetView: View {
    
    @Environment(\.dismiss) var dismiss
    @State var fruitNewItem: String
    
    var body: some View {
        NavigationStack{
            HStack{
                Text("名前")
                TextField("", text: $fruitNewItem)
                    .frame(width: 210,height: 50)
                    .border(Color.black)
                    .padding()
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Cancel"){
                        dismiss()
                    }
                }
            }
        }
    }
}

解決方法

1.sheet(item:onDismiss:content:)を使用する。
「item」を引数に持つsheetを使用すると、こういった挙動は改善されます。しかし、なぜこのようなな違いが出てくるのかまでは突き止めることができませんでした。理由をご存知の方がいましたら、是非ご鞭撻頂けたらと思います。

ちなみに

var body: some View直下にlet _ = print(fruitsIndex)でアクセスするか、Listのコードブロックの最後に.onChange内でfruitsIndexをprintすると、ちゃんと表示してくれることが確認できました。最初にViewが描画される時に、プロパティにアクセスすると、この謎挙動が修正されるのでしょうか?

4
3
0

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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?