使用SwifUI实现可动态增减列数的网格视图

动态网格视图 CustomGrid

我们都知道在UIKit中可以使用CollectionView可以实现瀑布流效果,在SwiftUI中我们能更简单实现可动态添加列数的网格视图。

首先,先理清楚我们的需求,我们的网格是可以显示所有遵循View协议的任何类我们可以让外部传入数据,只要数据支持Identifiable协议即可,方便我们在内部对该数据进行Loop操作。

1、创建CustomGrid类
  • 声明属性以及初始化
//Content 为最后整合数据并用于展示的内容 comforms to View 协议 
// T 数据 为任何comforms Identifiable Hashable 的数据 

struct CustomGrid: View where T: Hashable{

    var columns: Int // 内容展示的列数
    var spacing: CGFloat = 20 // 视图之间的间距 
    var showIndicator: Bool =  false // 是否显示指示条
    var list:[T] // 存放数据的数组
    var content:(T) -> Content // 闭包函数,用于生成整合数据后的视图 
    
    //初始化方法  
      init(columns: Int, spacing: CGFloat = 20, showIndicator: Bool = false,lists: [T], @ViewBuilder content: @escaping (T) -> Content) {
        self.lists = lists
        self.content = content
        self.columns = columns
        self.showIndicator = showIndicator
        self.spacing = spacing
    }
}

2、设置数据
func setupData() -> [[T]] {
   var outputData: [[T]]  = Array(repeating: [], count:columns) //根据所设定的列数创建对应的二维数组。
   var currentIndex = 0
   guard outputData.count > 0 else { return outputData }
   for object in lists {
   //将lists的数据平均分到outputData中的没个数组中
    outputData[currentIndex].append(object)
    if currentIndex == (columns - 1) {
        currentIndex = 0
   } else {
        currentIndex += 1
   }
   }
   return outputData
}
3、将数据进行展示
var body: some View {
        
  ScrollView(.vertical,showsIndicators: showIndicator) {
    HStack (alignment: .top) {
        ForEach(setupData(), id: \.self) { data in
            LazyVStack(spacing: spacing) {
                ForEach(data,id: \.self) { post in
                    content(post) //这里直接调用闭包函数。将外部传进来的内容进行展示 
                }
            }
        }
    }
    .padding(.vertical)
    }
}

4、代码调用

在ContentView中 实现如下代码

struct ContentView: View {
    @State var lists:[Post] = [] //数据源数组  Post 遵循Identifiable,Hashable 协议
    @State var columns: Int = 2
     @Namespace var namespace //用与进行matchedGeometryEffect展示增加和减少列数时内容动态变换的动画 
     
    var body: some View {
    NavigationView {
        CustomGrid(columns: columns, spacing: 20, lists: lists) { post in
            CardView(post: post)
                .matchedGeometryEffect(id: post.id, in: namespace)

        }
        .padding(.horizontal)
        .navigationTitle("CustomGrid")
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    columns += 1
                } label: {Image(systemName: "plus")}

            }
            ToolbarItem(placement: .navigationBarTrailing) {
                Button {
                    columns = max(columns - 1, 0)
                } label: {Image(systemName: "minus")}
            }
        }
        .animation(.spring(response: 0.3, dampingFraction: 0.8, blendDuration: 0.3), value: columns)

    }
    .onAppear{
        for index in 1...15 {
            lists.append(Post(imageName: "\(index)"))//
       }
    }
    }
}


struct CardView: View {
    var post: Post
    var body: some View {
        Image(post.imageName)
            .resizable()
            .aspectRatio(contentMode: .fit)
            .cornerRadius(10)
    }
}

struct Post: Identifiable,Hashable {
    let id = UUID().uuidString
    var imageName: String
}

5、效果展示
demo.gif

你可能感兴趣的:(使用SwifUI实现可动态增减列数的网格视图)