[SwiftUI] SwiftUIのList内にBinding変数を渡す方法

SwiftUI2021

SwiftUI の List 内に、配列要素の Binding を渡す方法を説明します(2021 秋リリース予定の iOS15, macOS12 等が必要です)

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Big Sur 11.4
  • Xcode 13.0 beta
  • iOS 15.0 beta

配列を Binding で渡す

#以前の記事でも書きましたが、WWDC21 での発表に合わせたアップデートです。

SwiftUI を使っていると List, ForEach 等のループ内に、配列の Binding を渡したいケースが発生します。

以下は、以前の記事です。
SwiftUI[SwiftUI] SwiftUIのForEach内でBinding変数を渡したい

StackOverflow 等でもよく質問が出ていて、色々な方法が説明されていましたが、どれも 直接的に記述している感じがしないものでした。(declarative なはずなのに)

SwiftUI の次のバージョンでは、解決策が実装されそうなので、その内容を説明します。

以下では、iOS15 で対応する予定の SwiftUI を "次期バージョンの SwiftUI"、iOS14 までの SwiftUI を "これまでの SwiftUI" と書いています。

なお、次期バージョンの SwiftUI は、現在ベータ版ですので、リリース時には変更が入っているかもしれません。

前提

複数の要素を配列で持ち、その内容を TextField や Slider で編集したいとします。以下のような例です。

MultipleSliders
MultipleSliders

iOS14 以前のバージョンの SwiftUI での実装

以前は、以下のように実装していました。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/17
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct SliderItem: Identifiable {
    public var id: UUID
    public var doubleValue: Double
    init() {
        id = UUID()
        doubleValue = Double.random(in: 0...10)
    }
}

struct ContentView: View {
    @State var items:[SliderItem] = [SliderItem(), SliderItem(), SliderItem(), SliderItem()]
    var body: some View {
        VStack {
            List(items.indices) { index in
                Text("\(items[index].doubleValue)")
                Slider(value: $items[index].doubleValue, in: 0...10)
            }
        }
        .padding()
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

配列の indices を使って、ループさせます。ループの中では、index を使用して配列から要素のデータや Binding を取得しています。

WWDC21 のビデオでも言及していますが、この方法は、お勧めしないと説明されています。

MEMO
これまでは、indices を使った実装しかできなかったのですが、次期バージョンの SwiftUI では この indices を使った実装はお勧めしないということだと思います。

# ちなみに、identifier をきちんと制御しなさいということも言われていますので、この記事の例では、Double を保持するためにも ID を振ってサンプルを作ってみました。

次期バージョンの SwiftUI での実装

見た目は同じなので省略です。

以下のコードのように、List に対して、配列の Binding を渡すことができるようになります。


//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2021/06/17
//  © 2021  SmallDeskSoftware
//

import SwiftUI

struct SliderItem: Identifiable {
    public var id: UUID
    public var doubleValue: Double
    init() {
        id = UUID()
        doubleValue = Double.random(in: 0...10)
    }
}

struct ContentView: View {
    @State var items:[SliderItem] = [SliderItem(), SliderItem(), SliderItem(), SliderItem()]
    var body: some View {
        VStack {
            //  (1)         (2)
            List($items) { $item in
                Text("\(item.doubleValue)")  // (3)
                Slider(value: $item.doubleValue, in: 0...10) // (4)
            }
        }
        .padding()
    }
}
コード解説
  1. List に対して、配列の Binding を渡すことができます
  2. 渡された 配列の要素を、Binding で受けることができます
  3. 値を使用したい時は変数として使えます
  4. Binding として使いたい時は、(これまでと同様に) $ をつけて使います
注意
上記では、List をコード例に使っていますが、ForEach に対しても同様に渡すことができます。

まとめ:SwiftUI の List/ForEach に Binding を渡す方法

以前からも indices を使えばできることでしたが、Binding を List に渡すように記述することで、その List で行いたいことが明確に記述されるようになったと思います。

SwiftUI の List に Binding を渡す方法
  • List に Binding を渡すことできる
  • closure 内には、配列要素の Binding が渡される
  • closure 内での扱いは、これまで通り

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です