第九章 List|GeometryReader|ForEach

75 阅读4分钟

但是我们的服务器地址需要满足用户在我们的支持的环境切换,所以需要一个类似 PopMenuButton的控件,在Mac上我们可以使用 Picker控件,甚至还有 ContextMenu 控件,但是好像都不满足。

那么我们就自己做一个下面这样的控件。

image-20220104103635028

其实 FlutterPopMenuButton的体验要复杂的多,我们只做简单的版本,可以满足用户可以选择切换服务器。

这个组件不止在登陆页面用,以后还用到切换工厂等操作,我们将这个组件放在 Common 同级目录创建一个文件夹 View,新建 PopMenuButton.swift

我们可以看到弹出是可以选择的,数据源是外部的,意思是数据源是不确定的,那么就需要做成滚动,但是也有最大高度限制。

使用 List 将内容可以滚动

既然是滚动的,我们就用到了 List 这个组件。比如下面代码

struct PopMenuButton: View {
    var body: some View {
        List {
            Text("Hello, World!")
            Text("Hello, World!")
            Text("Hello, World!")
        }
    }
}
image-20211117100804725

我们发现List是充满整个页面的。并且在样式上面系统默认做了很多默认边距,可是我们不想要默认产生的间距。

我对于 List 的了解也不是很多,所以也不是很清楚怎么弄,我就用谷歌搜索了关键词。swiftui list remove edge

通过查看相关的资料,我获取了两个关键词一个是设置 listStyle,一个设置 listRowInsets

var body: some View {
    List {
        Text("Hello, World!")
            .listRowInsets(EdgeInsets())
        Text("Hello, World!")
            .listRowInsets(EdgeInsets())
        Text("Hello, World!")
            .listRowInsets(EdgeInsets())
    }
    .listStyle(PlainListStyle())
}
image-20211117104058502

使用 GeometryReader 查看视图大小

从运行结果看,对于每一个Row还是存在一个默认最小的高度44。为了验证我们的猜测,我们用到了一个可以自由布局的组件 GeometryReader

GeometryReader 可以获取容器的大小,从而开始自由的布局,我们将代码修改一下,显示一下布局大小。

GeometryReader { geo in
    return Text("size: \(geo.size.debugDescription)")
}
.listRowInsets(EdgeInsets())
image-20211117104531901

界面上显示当前 Cell 的大小为 390x44,果然最小的高度为44

我们也不想这个 List 充满整个屏幕呀,我们设置 fixedSize。我们发现报错了,List这个组件不支持自身按照内容调节高度。

我们设置一个最大高度试一下,比如最多显示五行半,假设一行就按照44高度计算,那么最大的高度为。

44 * 5.5 = 242

我们给 List 设置一下最大的高度。

image-20211117105352684

List 的大小确实变小了,但是我们只有三行,那么高度岂不是多出来了。这样体验不是很好,因为 List 默认高度是最大的高度,现在设置了最多 242 高度,所以现在计算出 List 的高度为 242 的高度。

通过 .frame(height:) 设置具体高度

如果我们只显示三行的高度,那么就需要去设置 ListFrame 的高度。

/// 44 * 3 = 132
.frame(height: 132)
image-20211117105711556

List 的高度已经变成我们内容的真正的高度,我们将高度设置 500 试一下,看看最大的高度是否还是 242

image-20211117105834809

从结果来看,我们设置最大高度 242 已经失效。为了能够达到我们的体验,如果大于 5 行,那么最大的高度就是 5.5 行,如果小于等于 5 行,就显示真正的高度。

final var listHeight:CGFloat {
	let number = min(5.5,rowNuber)
	return number * 44
}

想要知道 List 的行数,那么就要知道数据源,我们新建一个属性,接受外部传递进来的服务器地址列表。

let items:[String]

使用 ForEach 动态添加子视图

刚才我们是一个个加进 List,但是如果有一组数据,怎么能够循环的添加呢?这个时候我们需要用到 ForEach 的组件。

List {
    ForEach(items, id: \.self) { item in
        Text(item)
            .listRowInsets(EdgeInsets())
    }
}
image-20211117110856507

我们去掉刚才设置的 maxWidth ,将 height 设置为我们计算出来的高度的值。我们将数据源设置为6条,看一下效果。

image-20211117112449410

和我们预想的一样,虽然这样实现了我们的效果,但是我们目前是每一条数据都是固定高度计算出来的,如果每一条的高度不一致,那么对于我们计算高度就会很麻烦。