
iOS开发中的常见任务是UITableView中的可扩展/折叠部分。 今天,我们正在使用SwiftUI实现此任务。 作为一个小小的转折,在节标题中添加一个动画三角形,并使单元格也展开。
针对macOS Catalina 10.15.1的Xcode 11.2进行了开发
开始项目
启动Xcode,文件-新项目-单视图应用程序。 在对话框中,指定Swift开发语言,我们将使用SwiftUI形成UI。

资料
作为演示数据,我们将使用几种有趣的拉丁语带翅膀的表达方式并将其翻译成俄语。
向项目添加一个新的Swift文件,将其
命名为Data.swift并在其中写入以下内容:
struct QuoteDataModel : Identifiable { var id: String { return latin } var latin : String var russian : String var expanded = false } struct SectionDataModel : Identifiable { var id: Character { return letter } var letter : Character var quotes : [QuoteDataModel] var expanded = false }
QuoteDataModel是单个表达式的模型,将来它将成为每个单个单元格的内容。 我们在其中存储表达式的原始文本,其翻译和“扩展”单元格的符号(默认情况下为“最小化”)
SectionDataModel是每个单独部分的模型,在这里我们存储该部分的“字母”,以该字母开头的引号数组以及“ expanded”部分的符号(默认情况下也为“ collapsed”)
将来,我们将在“列表”视图中显示所有内容,这要求其数据符合“可
识别协议”。 为此,我们定义
id属性,该属性对于列表中的每个项目都必须是唯一的。
此外,在同一个Data.swift文件中,我们形成数据:
var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])]
让我们来处理界面
现在,我们将确定节标题和每个单元格的外观。
从菜单中选择文件-新建-文件-SwiftUI视图。 将文件命名为
HeaderView.swift并将其内容替换为以下内容:
import SwiftUI struct HeaderView : View { var section : SectionDataModel var body: some View { HStack() { Spacer() Text(String(section.letter)) .font(.largeTitle) .foregroundColor(Color.black) Spacer() } .background(Color.yellow) } } struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: latinities[0]) } }

现在再次是File-New-File-SwiftUI View。 将文件命名为
QuoteView.swift并将其内容替换为以下内容:
import SwiftUI struct QuoteView: View { var quote : QuoteDataModel var body: some View { VStack(alignment: .leading, spacing: 5) { Text(quote.latin) .font(.title) if quote.expanded { Group() { Divider() Text(quote.russian).font(.body) }.transition(.move(edge: .top)).animation(.default) } } } } struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: latinities[0].quotes[0]) } }

现在打开文件ContentView.swift并按如下所示更改ContentView的结构:
struct ContentView: View { var body: some View { List { ForEach(latinities) { section in Section(header: HeaderView(section: section), footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) } } } } } .listStyle(GroupedListStyle()) } }
恭喜,您刚刚在列表中填充了最新数据! 对于
latinities数组的每个元素
,我们创建一个节,该节具有基于
HeaderView的标题和一个空的页脚。 如果该部分是“展开的”,那么对于quotes数组中的每个表达式,我们将基于
QuoteView形成一个单元格。 在我们的数据中,所有节和所有单元格都“折叠”,因此,如果使Canvas可见,您将仅看到节标题:

如您所知,现在该应用程序完全“死了”,距离我们的最终目标还很遥远。 但是很快我们将修复它!
略微修改节标题
回到HeaderView.swift文件。 在HeaderView结构内部,紧接在主体之后,添加以下代码:
struct Triangle : Shape { func path(in rect: CGRect) -> Path { var path = Path() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: 0, y: rect.height - 1)) path.addLine(to: CGPoint(x: sqrt(3)*(rect.height)/2, y: rect.height/2)) path.closeSubpath() return path } }
该结构返回等边三角形。 现在将三角形添加到标题中。 在
HStack内部,在第一个
Spacer之前添加以下代码:
Triangle() .fill(Color.black) .overlay( Triangle() .stroke(Color.red, lineWidth: 5) ) .frame(width : 50, height : 50) .padding() .rotationEffect(.degrees(section.expanded ? 90 : 0), anchor: .init(x: 0.5, y: 0.5)).animation(.default))

修改资料
回到我们的数据。 打开Data.swift并将
latinities数组
包装在新的UserData类中,如下所示:
class UserData : ObservableObject { @Published var latinities : [SectionDataModel] = [ SectionDataModel(letter: "C", quotes: [ QuoteDataModel(latin: "Calvitium non est vitium, sed prudentiae indicium.", russian: " , ."), QuoteDataModel(latin: "Conjecturalem artem esse medicinam.", russian: " ."), QuoteDataModel(latin: "Crede firmiter et pecca fortiter!", russian: " !")]), SectionDataModel(letter: "H", quotes: [ QuoteDataModel(latin: "Homo sine religione sicut equus sine freno.", russian: " ."), QuoteDataModel(latin: "Habet et musca splenem.", russian: " .")]), SectionDataModel(letter: "M", quotes: [ QuoteDataModel(latin: "Malum est mulier, sed necessarium malum.", russian: " , ."), QuoteDataModel(latin: "Mulierem ornat silentium.", russian: " .")])] }
请记住,还应将纬度标记为
@Published 。
我们做了什么?ObservableObject是数据的特殊对象,可以“绑定”到某些View。 SwiftUI会“监视”所有可能影响View的更改,并在数据更改后更改View。
在“包裹”了拉丁语之后,我们遇到了很多错误,我们将对其进行纠正。 打开
HeaderView.swift 并按如下所示纠正
HeaderView_Previews的结构:
struct HeaderView_Previews: PreviewProvider { static var previews: some View { HeaderView(section: UserData().latinities[0]) } }
现在对
QuoteView.swift进行类似的更改:
struct QuoteView_Previews: PreviewProvider { static var previews: some View { QuoteView(quote: UserData().latinities[0].quotes[0]) } }
打开ContentView.swift文件,并将其添加到
主体声明之前
@ObservedObject var userData = UserData()
复兴风景
返回文件ContentView.swift。 在ContenView结构内部,紧随userData定义之后,添加两个函数:
func sectionIndex(section : SectionDataModel) -> Int { userData.latinities.firstIndex(where: {$0.letter == section.letter})! } func quoteIndex(section : Int, quote : QuoteDataModel) -> Int { return userData.latinities[section].quotes.firstIndex(where: {$0.latin == quote.latin})! }
将
onTapGesture修饰符添加到我们正在
创建的节标题和单元格中。
身体内容的最终观点:
var body: some View { List { ForEach(userData.latinities) { section in Section(header: HeaderView(section: section) .onTapGesture { self.userData.latinities[self.sectionIndex(section: section)].expanded.toggle() }, footer: EmptyView()) { if section.expanded { ForEach(section.quotes) { quote in QuoteView(quote: quote) .onTapGesture { let sectionIndex = self.sectionIndex(section: section) let quoteIndex = self.quoteIndex(section: sectionIndex, quote: quote) self.userData.latinities[sectionIndex].quotes[quoteIndex].expanded.toggle() } } } } } } .listStyle(GroupedListStyle()) }
sectionIndex和
quoteUndex函数返回传递给它们的节和表达式的索引。 收到这些索引后,我们更改了
拉丁语数组中
扩展属性的值,这导致该节或表达式的折叠/展开。

结论
完成的项目可以在
这里下载 。
一些有用的链接:
希望该出版物对您有所帮助!