这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
前文
一、目标
实现通讯录列表以及右边的字母导航栏。

二、思路
其实SwiftUI提供了List组件是最简单实现这种列表布局的,不过这个页面并非完全规整的List布局,因为有搜索框,而在iOS14还无法自定义List子组件去掉下划线这种操作。所以需要换一种实现方式。
iOS15中通过给子组件增加.listRowSeparator(.hidden)属性,可以去掉下划线。
所以只能使用ScrollView的方式,通过自定义子组件样式,一样可以达成目的。
不过通讯录列表有个难点在于滑动到标题时,需要固定在顶部直到划过这个内容,如下图所示:

三、固定标题
看个代码例子
ScrollView{
LazyVStack(pinnedViews: [.sectionHeaders]){
Section(header: Text("A").background(Color.red)){
Text("Apple")
Text("Aaron")
}
Section(header: Text("B").background(Color.red)){
Text("Black")
Text("Bad")
}
}
}
复制代码
LazyVStack提供参数pinnedViews,可以设置锁定头部标题.sectionHeaders还是结尾标题.sectionFooters- 通过
Section组件,可以将子组件划分为不同的组,header指定标题内容,包裹的即为组内元素。
四、自定义列表元素,仿List

剖析下结构,就很简单了。
- 使用
VStack可以纵向布局红色和绿色的元素 - 使用
HStack可以横向布局图标和文本元素 - 使用
ZStack叠加图标和矩形元素
代码如下:
VStack(alignment: .leading){
HStack{
ZStack{
Rectangle().fill().cornerRadius(5).foregroundColor(color).aspectRatio(1, contentMode: .fit).frame(height: 50)
Image(systemName: "person.2.circle").foregroundColor(.white).font(.system(size: 40))
}.padding(.trailing)
Text(text)
}
Divider()
}.padding()
复制代码
aspectRatio用于指定目标元素的宽高比率,对矩形和图片都适用
接下来再将header元素改造一下。
创建MailSectionHeader.swift文件。
struct MailSectionHeader: View {
var title: String
var body: some View {
HStack{
Text(title)
Spacer()
}.padding().background(Color(red: 237/255, green: 237/255, blue: 237/255))
}
}
复制代码
然后改造Section
Section(header: MailSectionHeader(title: "A").background(Color.red)){
MailSection(text: "Apple")
MailSection(text: "Aaron")
}
复制代码

五、字母导航栏
HStack{
Spacer()
VStack(spacing: 15){
ForEach(0..<words.count, id: \.self){index in
if index == tapped {
SelectedText(title: words[index]).onTapGesture {
tapped = index
}
} else{
Text(words[index]).onTapGesture {
tapped = index
}
}
}
}.padding()
}
复制代码
样式之类的前面的章节讲过很多,主要讲一下怎么定位点击的导航字母。
- 使用
onTapGesture增加点击事件,并记录点击的序号。 - 通过
@State修饰的变量,会在变化的时候,使SwiftUI触发更新UI事件,进而重新执行if index == tapped判断。

六、导航事件
导航按钮的作用是能够点击立即跳转到对应元素的位置,所以需要ScrollViewReader组件支持
将该组件内嵌在ScrollView里,可以通过.scrollTo(id)的方式跳转到对应id的元素位置上。
先将对应标题元素打上id标记

然后增加onChange方法,监听wordNavigationtappedIndex字段,当点击对应的字母时,改变wordNavigationtappedIndex的值,就会触发onChange方法方法,再调用scrollTo方法即可跳转。
.onChange(of: wordNavigationtappedIndex, perform: { index in
value.scrollTo(index)
})
复制代码
这里之所以使用
onChange监听wordNavigationtappedIndex的方式,是因为字母导航不在ScrollViewReader范围内,所以无法直接通过value.scrollTo(index)的方式跳转。
























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)