Staggered Grid

Updated:

🔷 Staggered Grid

//  StaggeredGrid.swift

import SwiftUI

// Custom View Builder
// T -> is to hold the identifiable collection of data..

struct StaggeredGrid<Content: View, T: Identifiable>: View where T: Hashable {
// MARK: -  PROPERTY

// It will return each object from collection to build view..
var content: (T) -> Content

var list: [T]
var columns: Int

var showIndicators: Bool
var spacing: CGFloat

init(columns: Int , showIndicators: Bool = false, spacing: CGFloat = 10,  list: [T], @ViewBuilder content: @escaping (T)->Content) {
	self.content = content
	self.list = list
	self.spacing = spacing
	self.showIndicators = showIndicators
	self.columns = columns
}

// MARK: -  BODY
var body: some View {
ScrollView(.vertical, showsIndicators: showIndicators) {
	HStack (alignment: .top){
		ForEach(setUpList(), id: \.self) { columnsData in
				// For Optimized Using LazyStack..
			LazyVStack (spacing: spacing) {
				ForEach(columnsData) { object in
					content(object)
				}
			}
		}
	} //: HSTACK
} //: SCROLL
// Only vertical padding..
// horizontal padding will be user's optional..
.padding(.vertical)
}

// MARK: -  FUNCTION
// Staggered Grid Function
func setUpList() -> [[T]] {
	// Creating empty sub arrays of columns count...
	var gridArray: [[T]] = Array(repeating: [], count: columns)

	// spiliting array for Vstack oriented View...
	var currentIndex: Int = 0

	for object in list {
		gridArray[currentIndex].append(object)

		// increasing index count..
		// and resetting if overbounds the columns count...
		if currentIndex == (columns - 1) {
			currentIndex = 0
		} else {
			currentIndex += 1
		}
	}
	return gridArray
}
}

// MARK: -  PREVIEW
struct StaggeredGrid_Previews: PreviewProvider {
	static var previews: some View {
		ContentView()
	}
}

//  Home.swift

import SwiftUI

struct Home: View {
// MARK: -  PROPERTY
@State var posts: [Post] = []

// To show dynamic..
@State var columns: Int = 2

// Smooth hero effect..
@Namespace var animation

// MARK: -  BODY
var body: some View {
NavigationView {
StaggeredGrid(columns: columns, list: posts, content: { post in
	// Post Card View..
	PostCardView()
		.matchedGeometryEffect(id: post.id, in: animation)
})
	.padding(.horizontal)
	.navigationTitle("Staggered Grid")
	.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..
	.animation(.easeInOut, value: columns)
	}
	.onAppear {
		for _ in 1...10 {
			posts.append(Post(imageURL: "https://picsum.photos/200"))
		}
	}
}
}

// MARK: -  PREVIEW
struct Home_Previews: PreviewProvider {
static var previews: some View {
	Home()
}
}

// Since we declared T as Identifiable..
// So we need to pass Idenfiable confirm collection/Array...

struct PostCardView: View {
let url = URL(string: "https://picsum.photos/200")

var body: some View {
	AsyncImage(url: url) { image in
		image
			.resizable()
			.aspectRatio(contentMode: .fit)
			.cornerRadius(20)
	} placeholder: {
		ProgressView()
	}
}
}

스크린샷


🗃 Reference

SwiftUI 3.0: Staggered Grid With Matched Geometry Effect - Xcode 13 - WWDC 2021 - https://www.youtube.com/watch?v=VrwINubmq5g

Categories:

Updated:

Leave a comment