第九章 List|GeometryReader|ForEach

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

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

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

这个组件不止在登陆页面用,以后还用到切换工厂等操作,我们将这个组件放在 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:) 设置具体高度

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

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条,看一下效果。

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

相关推荐
东坡肘子1 天前
Swift 并发正被更广泛地接纳 -- 肘子的 Swift 周报 #133
人工智能·swiftui·swift
文件夹__iOS4 天前
SwiftUI 核心选型:class + ObservableObject VS struct + @State
ios·swiftui·swift
Wenzar_5 天前
# 发散创新:SwiftUI 中状态管理的深度实践与重构艺术 在 SwiftUI 的世界里,**状态驱动 UI 是核心哲学**。但随
java·python·ui·重构·swiftui
大熊猫侯佩6 天前
GeometryReader 生存指南(下集):与恶魔共舞——陷阱、禁忌与最终救赎
swiftui·performance·layout·frame·stack·geometryreader·preferencekey
大熊猫侯佩7 天前
别被系统绑架:SwiftUI List 替换背后的底层逻辑
swiftui·swift·apple
东坡肘子8 天前
从 OpenSwiftUI 到 DanceUI:换个方式 Dive SwiftUI -- 肘子的 Swift 周报 #132
人工智能·swiftui·swift
用户79457223954139 天前
【SwiftyJSON】拯救你的 as? [String: Any]——链式 JSON 访问的正确姿势
swiftui·objective-c·swift
用户79457223954139 天前
【Moya】为什么你的 Alamofire 代码需要再封装一层?
swiftui·objective-c·swift
空中海10 天前
第二章:SwiftUI 视图基础
ios·swiftui·swift