Skip to content

Instantly share code, notes, and snippets.

@anettodev
Last active August 11, 2025 20:28
Show Gist options
  • Select an option

  • Save anettodev/4fd022236a7db2c9d22cd60260d8f905 to your computer and use it in GitHub Desktop.

Select an option

Save anettodev/4fd022236a7db2c9d22cd60260d8f905 to your computer and use it in GitHub Desktop.
Test LiquidGlass Music Template
//
// ContentView.swift
// tetetetee
//
// Created by Antonio Netto on 11/08/25.
//
import SwiftUI
// MARK: - Data Models
struct Artist: Identifiable {
let id: Int
let name: String
let imageName: String
let topSong: String
let rank: Int
}
struct ListeningStats {
let minutes: Int
let period: String
}
// MARK: - Tab Items Enum
enum TabItem: String, CaseIterable {
case dashboard = "Dashboard"
case profile = "Profile"
case search = "Search"
var iconName: String {
switch self {
case .dashboard: "music.note.list"
case .profile: "person.fill"
case .search: "magnifyingglass"
}
}
var title: String {
return self.rawValue
}
@ViewBuilder
func view() -> some View {
switch self {
case .dashboard: DashboardView()
case .profile: ProfileView()
case .search: SearchView()
}
}
}
// MARK: - Action Items Enum
enum ActionItem: CaseIterable {
case like
case play
var iconName: String {
switch self {
case .like:
"heart.fill"
case .play:
"play"
}
}
var color: Color {
switch self {
case .like: .red
case .play: .blue
}
}
}
// MARK: - Reusable Components
struct GradientBackgroundView: View {
var body: some View {
LinearGradient(
gradient: Gradient(colors: [
Color.orange.opacity(0.5),
Color.red.opacity(0.5),
Color.pink.opacity(0.4),
Color.clear
]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea(.all)
}
}
struct FloatingActionButtonView: View {
@State private var isExpanded = false
var body: some View {
GlassEffectContainer {
HStack {
Spacer()
if isExpanded {
ForEach(ActionItem.allCases, id: \.self) { item in
Button(action: {
withAnimation {
self.isExpanded = false
// TODO ACTIONS
}
}) {
Label("", systemImage: item.iconName)
.labelStyle(.iconOnly)
.frame(width: 60, height: 60)
.background(.clear, in: Circle())
.foregroundColor(item.color)
}
.glassEffect(.clear.interactive())
.padding([.bottom, .trailing], 20)
}
}
Button { self.isExpanded.toggle() } label: {
Label("Add", systemImage: "plus")
.labelStyle(.iconOnly)
.frame(width: 60, height: 60)
.background(.ultraThinMaterial, in: Circle())
.foregroundColor(.primary)
.rotationEffect(.degrees(isExpanded ? 45 : 0))
}
.glassEffect(
.clear.interactive().tint(.clear)
)
.padding([.trailing, .bottom], 20)
}
.tint(.primary)
.frame(height: 100)
}
.animation(.smooth(duration: 0.5), value: isExpanded)
}
}
// MARK: - Dashboard Components
struct StatsCardView: View {
let stats: ListeningStats
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Text("You listened for")
.font(.title3)
.foregroundColor(.primary)
Spacer()
}
HStack {
Text("\(stats.minutes.formatted())")
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(.primary)
Text("minutes")
.font(.title3)
.foregroundColor(.primary)
Text("in \(stats.period).")
.font(.title3)
.foregroundColor(.secondary)
Spacer()
}
}
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
}
struct SectionHeaderView: View {
let title: String
var body: some View {
HStack {
Text(title)
.font(.title2)
.fontWeight(.semibold)
.foregroundColor(.primary)
Spacer()
Button("View All") {
// TODO: View all action
}
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
struct ArtistCardView: View {
let artist: Artist
var body: some View {
VStack(alignment: .leading, spacing: 12) {
// Artist image with rank overlay
ZStack(alignment: .topLeading) {
RoundedRectangle(cornerRadius: 12)
.fill(LinearGradient(
colors: [.blue, .purple, .pink],
startPoint: .topLeading,
endPoint: .bottomTrailing
))
.aspectRatio(1, contentMode: .fit)
// Rank number
Text("\(artist.rank)")
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(8)
}
// Artist info
VStack(alignment: .leading, spacing: 4) {
Text(artist.name)
.font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
HStack {
Image(systemName: "music.note")
.font(.caption)
.foregroundColor(.secondary)
Text(artist.topSong)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(1)
}
// Play controls
HStack {
Button(action: {}) {
Image(systemName: "play.fill")
.font(.caption)
.foregroundColor(.primary)
}
Button(action: {}) {
Image(systemName: "forward.fill")
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
}
}
}
.padding(12)
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
}
// MARK: - Tab Views
struct DashboardView: View {
// Sample data
private let stats = ListeningStats(minutes: 8491, period: "May")
private let topArtists = [
Artist(id: 1, name: "ZAYN", imageName: "zayn", topSong: "Let Me Tonight", rank: 1),
Artist(id: 2, name: "Justin Bieber", imageName: "justin", topSong: "Sorry", rank: 2),
Artist(id: 3, name: "The Weeknd", imageName: "weeknd", topSong: "Blinding Lights", rank: 3),
Artist(id: 4, name: "Dua Lipa", imageName: "dua", topSong: "Levitating", rank: 4),
Artist(id: 5, name: "Billie Eilish", imageName: "billie", topSong: "Bad Guy", rank: 5),
Artist(id: 6, name: "Ed Sheeran", imageName: "ed", topSong: "Shape of You", rank: 6)
]
var body: some View {
NavigationStack {
ZStack(alignment: .bottomTrailing) {
GradientBackgroundView()
ScrollView {
VStack(spacing: 24) {
// Stats card
StatsCardView(stats: stats)
// Top Artists section
VStack(alignment: .leading, spacing: 16) {
SectionHeaderView(title: "Your Top Artists")
LazyVGrid(columns: [
GridItem(.flexible(), spacing: 16),
GridItem(.flexible(), spacing: 16)
], spacing: 16) {
ForEach(topArtists) { artist in
ArtistCardView(artist: artist)
}
}
}
// Bottom padding for floating action button
Spacer()
.frame(height: 120)
}
.padding(.horizontal)
.padding(.top)
}
FloatingActionButtonView()
}
.navigationTitle("Replay")
.navigationBarTitleDisplayMode(.large)
}
}
}
struct ProfileView: View {
var body: some View {
NavigationStack {
ZStack {
GradientBackgroundView()
}
.navigationTitle("Profile")
}
}
}
struct SearchView: View {
@State private var searchString: String = ""
var body: some View {
NavigationStack {
ZStack {
GradientBackgroundView()
}
.navigationTitle("Search")
.searchable(text: $searchString)
}
}
}
// MARK: - Main Tab View
struct MainTabView: View {
@State private var selectedTab: TabItem = .dashboard
var body: some View {
if #available(iOS 26.0, *) {
TabView(selection: $selectedTab) {
ForEach(TabItem.allCases, id: \.self) { tab in
Tab(
tab.title,
systemImage: tab.iconName,
value: tab,
role: tab == .search ? .search : nil
) {
tab.view()
}
}
}
#if os(iOS)
.tabBarMinimizeBehavior(.onScrollDown)
#endif
} else {
// Fallback < iOS 26.0
TabView {
DashboardView()
.tabItem {
Image(systemName: TabItem.dashboard.iconName)
Text(TabItem.dashboard.title)
}
ProfileView()
.tabItem {
Image(systemName: TabItem.profile.iconName)
Text(TabItem.profile.title)
}
SearchView()
.tabItem {
Image(systemName: TabItem.search.iconName)
Text(TabItem.search.title)
}
}
}
}
}
// MARK: - Content View
struct ContentView: View {
var body: some View {
MainTabView()
.tabViewStyle(.sidebarAdaptable)
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment