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

其实 Flutter 中 PopMenuButton的体验要复杂的多,我们只做简单的版本,可以满足用户可以选择切换服务器。
这个组件不止在登陆页面用,以后还用到切换工厂等操作,我们将这个组件放在 Common 同级目录创建一个文件夹 View,新建 PopMenuButton.swift。
我们可以看到弹出是可以选择的,数据源是外部的,意思是数据源是不确定的,那么就需要做成滚动,但是也有最大高度限制。
使用 List 将内容可以滚动
既然是滚动的,我们就用到了 List 这个组件。比如下面代码
swift
struct PopMenuButton: View {
var body: some View {
List {
Text("Hello, World!")
Text("Hello, World!")
Text("Hello, World!")
}
}
}

我们发现List是充满整个页面的。并且在样式上面系统默认做了很多默认边距,可是我们不想要默认产生的间距。
我对于 List 的了解也不是很多,所以也不是很清楚怎么弄,我就用谷歌搜索了关键词。swiftui list remove edge
通过查看相关的资料,我获取了两个关键词一个是设置 listStyle,一个设置 listRowInsets。
swift
var body: some View {
List {
Text("Hello, World!")
.listRowInsets(EdgeInsets())
Text("Hello, World!")
.listRowInsets(EdgeInsets())
Text("Hello, World!")
.listRowInsets(EdgeInsets())
}
.listStyle(PlainListStyle())
}

使用 GeometryReader 查看视图大小
从运行结果看,对于每一个Row还是存在一个默认最小的高度44。为了验证我们的猜测,我们用到了一个可以自由布局的组件 GeometryReader。
GeometryReader 可以获取容器的大小,从而开始自由的布局,我们将代码修改一下,显示一下布局大小。
swift
GeometryReader { geo in
return Text("size: \(geo.size.debugDescription)")
}
.listRowInsets(EdgeInsets())

界面上显示当前 Cell 的大小为 390x44,果然最小的高度为44。
我们也不想这个 List 充满整个屏幕呀,我们设置 fixedSize。我们发现报错了,List这个组件不支持自身按照内容调节高度。
我们设置一个最大高度试一下,比如最多显示五行半,假设一行就按照44高度计算,那么最大的高度为。
swift
44 * 5.5 = 242
我们给 List 设置一下最大的高度。

List 的大小确实变小了,但是我们只有三行,那么高度岂不是多出来了。这样体验不是很好,因为 List 默认高度是最大的高度,现在设置了最多 242 高度,所以现在计算出 List 的高度为 242 的高度。
通过 .frame(height:) 设置具体高度
如果我们只显示三行的高度,那么就需要去设置 List 的 Frame 的高度。
swift
/// 44 * 3 = 132
.frame(height: 132)

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

从结果来看,我们设置最大高度 242 已经失效。为了能够达到我们的体验,如果大于 5 行,那么最大的高度就是 5.5 行,如果小于等于 5 行,就显示真正的高度。
swift
final var listHeight:CGFloat {
let number = min(5.5,rowNuber)
return number * 44
}
想要知道 List 的行数,那么就要知道数据源,我们新建一个属性,接受外部传递进来的服务器地址列表。
swift
let items:[String]
使用 ForEach 动态添加子视图
刚才我们是一个个加进 List,但是如果有一组数据,怎么能够循环的添加呢?这个时候我们需要用到 ForEach 的组件。
swift
List {
ForEach(items, id: \.self) { item in
Text(item)
.listRowInsets(EdgeInsets())
}
}

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

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