追記: NavigationView の中だと上手くいかない場合がありました。
SwiftUI で List の高さを中身に合わせて変える方法です。
下の画像の様に、 List の高さを中身と同じにします。(グレーの部分は List の背景です。)
↓
List と同じ様に表示される(高さが同じになる) VStack を作成して、その高さを List に設定しています。
import SwiftUI
import Combine
struct Item: Identifiable {
var id = UUID()
var name:String
}
class ViewModel: ObservableObject {
var items:[Item] = { (0..<2).map { i in Item(name: "item\(i)") } }()
@Published var listHidden = true
var listHeight:CGFloat = 0
var publisher = PassthroughSubject<Bool, Never>()
func setHeight(_ height: CGFloat) {
listHeight = height
// 5. List を表示
listHidden = false
}
}
struct MyView: View {
@ObservedObject var viewModel = ViewModel()
init() {
// 分かりやすい用に List の背景色を付けます
UITableView.appearance().backgroundColor = UIColor.lightGray
}
var body: some View {
// 1. VStack の方が表示される
if viewModel.listHidden {
ZStack {
VStack(spacing: 0) {
ForEach(viewModel.items) { item in
Row(item: item)
}
}
.background(
GeometryReader { geo in
Color.white
// 4. viewModel の listHeight に高さをセット
.onReceive(viewModel.publisher) { _ in
viewModel.setHeight(geo.size.height)
}
}
)
// 2. VStack が見えないようにする
Color.white
}
// 3. 表示したら publisher から通知
.onAppear { viewModel.publisher.send(true) }
// 6. 表示される
} else {
List {
ForEach(viewModel.items) { item in
Row(item: item)
}
}
// 7. 高さを設定
.frame(height: viewModel.listHeight)
}
}
}
struct Row: View {
var item:Item
var body: some View {
Text(item.name)
.listRowInsets(.init()) // 余白を消しておく
.padding()
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.background(Color.orange)
}
}
struct MyView_Previews: PreviewProvider {
static var previews: some View {
MyView()
}
}
バージョン
Swift 5.4