In the stock Photos app, there's a transition animation when switching the view from Month to Year. Initially each month is represented as a single cell but when switching to year, the cells combine into a single new cell. A similar animation style can be seen here as well.
I'm attempting my hand at recreating using a combination of LazyVGrid and matchedGeometryEffect but the cells don't combine smoothly like in the video. Any help would be greatly appreciated!
import SwiftUI
struct TestV: View {
@Namespace private var namespace
@State private var isExpanded = false
var body: some View {
VStack {
ZStack {
if !isExpanded {
SubviewOne(namespace: namespace, isExpanded: $isExpanded)
.transition(.offset())
} else {
SubviewTwo(namespace: namespace, isExpanded: $isExpanded)
.transition(.offset())
}
}
Spacer()
Toggle(isOn: $isExpanded.animation(.easeInOut(duration: 2)), label: { Text("Matched") }).frame(width: 140)
}
}
}
struct SubviewOne: View {
var namespace: Namespace.ID
@Binding var isExpanded: Bool
let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple]
let groups = ["one", "two", "two"]
var gridItemLayout = [GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: gridItemLayout, spacing: 0) {
ForEach((0...2), id: \.self) { num in
ZStack {
Rectangle().fill(colors[num % colors.count])
Text("\(num)")
.font(.system(size: 30))
}
.matchedGeometryEffect(id: isExpanded ? groups[num]:"", in: namespace, isSource: false)
.frame(height: 150)
.frame(maxWidth: .infinity)
}
}
}
}
}
struct SubviewTwo: View {
var namespace: Namespace.ID
@Binding var isExpanded: Bool
let colors: [Color] = [.red, .orange, .yellow, .green, .blue, .purple]
let groups = ["one", "two"]
var gridItemLayout = [GridItem(.flexible())]
var body: some View {
ScrollView {
LazyVGrid(columns: gridItemLayout, spacing: 20) {
ForEach((0...1), id: \.self) { num in
ZStack {
Rectangle().fill(colors[num % colors.count])
Text("\(num)")
.font(.system(size: 30))
}
.matchedGeometryEffect(id:groups[num], in: namespace)
.frame(width: 350, height: 250)
}
}
}
}
}
#Preview {
TestV()
}