🔥

[SwiftUI]固定Listで選択状態から非選択状態に変える

1 min read

固定Listでの選択状態の切り替えがうまくいかない

設定画面のような、固定Listで画面遷移も行う画面をSwiftUIで実装していたところ、選択状態が解除されないという事象がありました。


struct ContentView: View {

  @State var selection: Int?

  var body: some View {
    NavigationView {
      List(selection: $selection) {
        NavigationLink("Item 1", destination: Text("Item 1")).id(1)
        NavigationLink("Item 2", destination: Text("Item 2")).id(2)
      }.onAppear {
        self.selection = nil
      }      
    }
  }
}

このコードでは、selectionに選択状態を保存していますが、画面遷移した時にnilを代入することで選択状態が解除されると考えていましたが、そうはなりませんでした。

StackOverflowでも、同様に悩んでいる人がいました。

https://stackoverflow.com/questions/60139184/swiftui-navigationlink-macos-default-selected-state

環境

この事象は以前よりあるようですが、現在でも発生しています。

  • Xcode 12.5.1
  • iOS 14.5

解決方法

以下の記事を参考にしましたが、固定Listの場合はうまくいきませんでした。

https://stackoverflow.com/questions/63998759/swiftui-list-items-are-not-deselected

この記事では、それぞれに.id(UUID())を追加することで、画面を更新した際に選択状態が解除されるようにしています。
固定Listでうまくいかない原因としては、画面が更新されないことだと考えられます。最初のソースコードのように@Stateではうまく画面が更新されないようです。

色々試した結果、ObservableObject, @Published.id(UUID())を使えばうまくいきました。


class ViewModel: ObservableObject {
  @Published var value: Int = 0
}

struct ContentView: View {

  @ObservedObject var viewModel = ViewModel()

  var body: some View {
    NavigationView {
      List {
        NavigationLink("Item 1", destination: Text("Item 1")).id(UUID())
        NavigationLink("Item 2", destination: Text("Item 2")).id(UUID())
      }.onAppear {
        self.viewModel.value = 0
      }      
    }        
  }

}

なんとも気持ちが悪い挙動になっています。@Published var value: IntはViewでは使っていませんが、更新すると画面全体が再描画されるようで、画面遷移すると選択状態が解除されます。

Discussion

ログインするとコメントできます