Created
January 16, 2024 04:53
-
-
Save bigmountainstudio/780e95801d3412a34df239eb9b4ac6f4 to your computer and use it in GitHub Desktop.
Cool transitions between List and Grid using SwiftData and MatchedGeometryEffect.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright © 2024 Big Mountain Studio. All rights reserved. Twitter: @BigMtnStudio | |
// Website for awesome SwiftUI picture books 😃: bigmountainstudio.com | |
import SwiftData | |
import SwiftUI | |
import UIKit | |
// Refer to SwiftData Mastery book for more info. | |
@Model | |
class ArtistModel { | |
var name = "" | |
var number = 0 | |
var image: Data | |
init(name: String = "", number: Int = 0, image: Data) { | |
self.name = name | |
self.number = number | |
self.image = image | |
} | |
@MainActor | |
static var preview: ModelContainer { | |
let container = try! ModelContainer(for: ArtistModel.self, | |
configurations: ModelConfiguration(isStoredInMemoryOnly: true)) | |
container.mainContext.insert(ArtistModel(name: "Adele", number: 3, image: UIImage(resource: .adele).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Ariana Grande", number: 3, image: UIImage(resource: .ariana).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Beyoncé", number: 4, image: UIImage(resource: .beyonce).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Billie Eilish", number: 5, image: UIImage(resource: .billie).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Billy Joel", number: 2, image: UIImage(resource: .billyJoel).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Bring Me the Horizon", number: 5, image: UIImage(resource: .bringMeTheHorizon).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Bruno Mars", number: 3, image: UIImage(resource: .brunoMars).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Green Day", number: 7, image: UIImage(resource: .greenDay).pngData()!)) | |
container.mainContext.insert(ArtistModel(name: "Hozier", number: 5, image: UIImage(resource: .hozier).pngData()!)) | |
return container | |
} | |
var viewImage: UIImage { | |
UIImage(data: image) ?? UIImage(resource: .adele) | |
} | |
} | |
struct ArtistsView: View { | |
@Namespace var namespace | |
@Query(sort: \ArtistModel.name) private var artists: [ArtistModel] | |
@State var display = "list" | |
var body: some View { | |
NavigationStack { | |
Group { | |
if display == "list" { | |
List(artists) { artist in | |
NavigationLink { | |
} label: { | |
Label( | |
title: { Text(artist.name) }, | |
icon: { | |
Image(uiImage: artist.viewImage) | |
.resizable() | |
.scaledToFill() | |
.clipShape(Circle()) | |
.matchedGeometryEffect(id: artist.name, in: namespace) // This is what makes the magic happen. Refer to SwiftUI Animations Mastery book for more options. | |
} | |
) | |
} | |
} | |
} else { | |
ScrollView { | |
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 8) { | |
ForEach(artists) { artist in | |
VStack { | |
Image(uiImage: artist.viewImage) | |
.resizable() | |
.scaledToFill() | |
.frame(width: 70, height: 70) | |
.clipShape(Circle()) | |
.matchedGeometryEffect(id: artist.name, in: namespace) // This is what makes the magic happen | |
.overlay(alignment: .bottomTrailing) { | |
Text(artist.number, format: .number) | |
.foregroundStyle(.white) | |
.padding(6) | |
.background(.green, in: .circle) | |
.offset(x: 6, y: 6) | |
} | |
Text(artist.name) | |
.lineLimit(2) | |
.multilineTextAlignment(.center) | |
} | |
.padding() | |
.frame(maxWidth: .infinity) | |
.frame(height: 150, alignment: .top) | |
.background(.quaternary.opacity(0.3), in: .rect(cornerRadius: 16)) | |
} | |
} | |
.padding(.horizontal) | |
} | |
.font(.footnote) | |
} | |
} | |
.navigationTitle("Artists") | |
.toolbar { | |
Button { | |
withAnimation { | |
display = display == "list" ? "grid" : "list" | |
} | |
} label: { | |
Image(systemName: display == "list" ? "list.bullet" : "square.grid.2x2") | |
} | |
} | |
} | |
} | |
} | |
#Preview("List") { | |
ArtistsView(display: "list") | |
.modelContainer(ArtistModel.preview) | |
} | |
#Preview("Grid") { | |
ArtistsView(display: "grid") | |
.modelContainer(ArtistModel.preview) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment