简介在当前正式 SwiftUI 版本而言,很多控件都是缺少的。比如在 UIKit 框架里有 UICollectionView 组件,可以很方便地做 Gird 格子类型的视图。但是在 SwiftUI 这个框架里面,就没有对应 UICollectionView 的组件。我们当然可以用 UIViewRepresentable 来封装一个 UICollectionView ,但是本篇文章要探讨的是,如何使用 SwiftUI 来实现 Grid 格子视图,现在一起来实现吧。 实现思考在思考前,我们先来定义生成随机颜色的函数,后面会用到的。 extension Double { static func randomData() -> Double { Double(arc4random()) / Double(UInt32.max) } } extension Color { static func random() -> Color { .init(red: Double.randomData(), green: Double.randomData(), blue: Double.randomData()) } } 想必 HStack 横向布局与 VStack 竖向布局你们已经掌握得很熟练了,比如竖向排列 6 个 Color 视图。 var data: [Color] { [ Color.random(), Color.random(), Color.random(), Color.random(), Color.random(), Color.random() ] } var body: some View { VStack { ForEach(0..<data.count) { index in self.data[index] } } } 有些童鞋会疑问,为什么颜色也能算是视图呢?这是因为在 SwiftUI 中,View 是一个协议,而 Color 也遵循了 View 协议,所以 Color 也是一个视图,可以直接在界面上展示它。 说回来现在的例子,效果长这样。 只需将上面代码里的 VStack 换成 HStack,就会变成这样,代码就不贴了,直接上效果图。 那么是不是可以通过组合 HStack 与 VStack 能够实现我们想要的 Grid 视图呢?答案是可以肯定的。 你们肯定发现了,视图的上方和下方出现了空白,这是因为 iPhoneX 及之后的版本存在安全边距,只需通过设置 .edgesIgnoringSafeArea(.vertical) Grid 实现为了简单起见,我们先来打造一行三列的 Grid 视图。定义一个 View 取名为 GCRowView,视图的大小按照屏幕的宽度三分之一进行计算,这里的视图宽和高是一致的,代码如下所示,关键的代码我会标注数字,在后面进行讲解。 struct GCRowView: View { var itemPerRow = 3 // 1 var views: [AnyView] = [ // 2 AnyView(Image("1").resizable().aspectRatio(contentMode: .fill)), AnyView(Image("2").resizable().aspectRatio(contentMode: .fill)), AnyView(Image("3").resizable().aspectRatio(contentMode: .fill)), ] var itemWidth: CGFloat { // 3 UIScreen.main.bounds.width / CGFloat(itemPerRow) } var body: some View { HStack(spacing: 0) { // 4 ForEach(0..<views.count) { index in self.views[index] .frame(width: self.itemWidth, height: self.itemWidth) .clipped() // 5 } } } }
现在的效果是这样的。 可以看到视图正确地显示出来了。 现在创建 GCGirdContentView ,在其内实现一些算法,分别是计算总共有多少行和每一行展示的具体视图。先来实现 rowCount(contentNums:itemPerRow:) 方法计算总行数,参数分别是 func rowCount(contentNums: Int, itemPerRow: Int) -> Int { if contentNums % itemPerRow == 0 { return contentNums / itemPerRow } return contentNums / itemPerRow + 1 }
计算出每行排列的视图,返回视图数组,用于给 GCRowView 进行显示,方法的参数分别是 func rowViews(currentRow: Int, itemPerRow: Int) -> [AnyView] { var views = [AnyView]() for i in 0..<itemPerRow { // 1 let index = i + itemPerRow * currentRow // 2 if index < contentViews.count { // 3 views.append(contentViews[index]) // 4 } } return views }
接着把 GCRowView 封装得通用一点,把 itemPerRow 和 views 的默认值去除。 struct GCRowView: View { var itemPerRow: Int var views: [AnyView] //... 直到目前,我们已经完成了大部分的工作,现在来组装一下 GCGirdContentView 视图。 struct GCGirdContentView: View { var itemPerRow = 3 var contentViews: [AnyView] = [] init() { // 1 for i in 1...12 { contentViews.append(AnyView(Image("\(i)").resizable().aspectRatio(contentMode: .fill))) } } var body: some View { VStack(alignment: .leading, spacing: 0) { // 2 ForEach(0..<rowCount(contentNums: contentViews.count, itemPerRow: itemPerRow)) { i in // 3 GCRowView(itemPerRow: self.itemPerRow, views: self.rowViews(currentRow: i, itemPerRow: self.itemPerRow)) // 4 } } } }
现在运行,最终效果图如下所示。 总结在 SwiftUI 里实现 Grid 其实不算是复杂,通过组合 HStack 与 VStack 就能够助我们实现 Grid 视图。 在最新的 SwiftUI Beta 版里,苹果推出了如 LazyVGrid、LazyHGrid、GridItem 来实现管理 Grid 视图,我们就拭目以待吧,后续有机会再来更新一波。 源码下载我已经把源码 GCGridView 上传到 GitHub 上,往期所有的 Demo 源码皆放在了SwiftUI-Tutorials,欢迎自取。如果该项目帮到你的话,请给我个 Star 告知,谢谢!喜欢本篇文章的小伙伴,欢迎给个关注,后续继续更新更多文章,谢谢! 关于作者博文作者:GarveyCalvin 微博:https://weibo.com/feiyueharia 博客园:https://www.cnblogs.com/GarveyCalvin 本文版权归作者,欢迎转载,但必须保留此段声明,并给出原文链接,谢谢合作! |
|