SwiftUI の List を onMove 付きで macOS で使う時に発生する問題点と回避策を説明します。
Sponsor Link
環境&対象
以下の環境で動作確認を行なっています。
- macOS Big Sur 11.1
- Xcode 12.3
SwiftUI の List と onMove, onDelete
SwiftUI の List を ForEach + onMove, onDelete と組み合わせて使うと非常に便利です。
List 使用例 (iOS)
struct ContentView: View {
@State private var data = ["Item1", "Item2", "Item3", "Item4"]
var body: some View {
VStack {
#if os(iOS)
EditButton()
#endif
List {
ForEach(data, id:\.self) { title in
HStack {
Text("Title: \(title)")
}
}
.onDelete(perform: { indexSet in
data.remove(at: indexSet.first!)
})
.onMove(perform: { indices, newOffset in
data.move(fromOffsets: indices, toOffset: newOffset)
})
}
}
}
}
上記のコードで、以下のような 要素の移動や削除をできる UI を持つリストを作ることができます。
macOS 上での List と onMove, onDelete
List という View も ForEach の onMove, onDelete いずれも、macOS 用にも用意されています。
先のコードは、macOS にも使えるので、動かしてみると以下のような動作になります。
削除はうまくできるのですが、要素を移動すると、行の高さがおかしくなります。
macOS 上での List/ForEach と onMove の問題点
上の動画で確認できる通り、「要素移動させると、行の高さがおかしくなる」が問題です。
当面の回避策
修正を待っていてもしょうがないので、回避策をさがしてみました。
.frame で適正な高さを設定
そのままですが、.frame で height に値を設定してしまうのが、確実です。
ただ、その値を計算するのが手間で、システム側で適切に設定して欲しいというケースが多いです。
なので、.frame で height を計算することを避けたいので、他を探してみました。
.frame で 幅を指定しても OK
理由は不明ですが、.frame で width に値を設定しても期待通りの動きになります。
width 指定したコード
struct ContentView: View {
@State private var data = ["Item1", "Item2", "Item3", "Item4"]
var body: some View {
VStack {
#if os(iOS)
EditButton()
#endif
List {
ForEach(data, id:\.self) { title in
HStack {
Text("Title: \(title)")
.frame(width: 300) // 追加!
}
}
.onDelete(perform: { indexSet in
data.remove(at: indexSet.first!)
})
.onMove(perform: { indices, newOffset in
data.move(fromOffsets: indices, toOffset: newOffset)
})
}
}
}
}
なぜ width を指定することで、height が正しく調整されるのかは不明ですが、高さも元の高さを保って移動されます。
まとめ:macOS の List/ForEach の onMove の行高さ不具合を回避する方法
macOS の List/ForEach の onMove の行高さ不具合を回避する方法
- .frame で height を指定する
- .frame で width を指定しても高さを保持して、移動される
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
Sponsor Link