gh-EmergeTools-Pow/Example/Pow Example/Examples/Screens/SocialFeedExample.swift
Joe Fabisevich 5b95fe95b0
Moving Pow to @emergetools (#36)
Co-authored-by: Robert Böhnke <robb@robb.is>
Co-authored-by: Kasper Lahti <kasper@lahti.email>
2023-11-29 12:08:53 -03:00

261 lines
9 KiB
Swift

import Pow
import SwiftUI
struct SocialFeedExample: View, Example {
@State
var isLiked = false
@State
var isBoosted = false
@State
var clapCount = 202
@State
var isBookmarked = false
@State
var notificationCount: Int = 0
var body: some View {
ScrollView {
VStack(alignment: .trailing, spacing: 24) {
HStack(alignment: .top, spacing: 12) {
Image("mvp")
.resizable()
.aspectRatio(contentMode: .fill)
.clipShape(Circle())
.frame(width: 52, height: 52)
VStack(alignment: .leading, spacing: 6) {
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .firstTextBaseline) {
Text("Moving Parts").font(.headline.bold())
Spacer()
Text("12 min")
.font(.footnote)
.foregroundColor(.secondary)
}
Text("@movingpartsio").foregroundColor(.secondary)
}
.padding(.top, 3)
Text("Use Pow's Change Effects to give your buttons a little extra flair.")
Text("Try it on these buttons here:")
}
}
ViewThatFits {
buttonBar
.labelStyle(CustomButtonLabelStyle())
buttonBar
.labelStyle(.iconOnly)
}
.buttonStyle(.bordered)
.controlSize(.mini)
.tint(.gray)
.font(.footnote.weight(.medium).monospacedDigit())
}
.padding(12)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous))
.padding(12)
}
.task {
try? await Task.sleep(for: .seconds(3))
withAnimation {
notificationCount += 1
}
}
.safeAreaInset(edge: .bottom, spacing: 0) {
tabBar
}
.navigationBarTitleDisplayMode(.inline)
}
var buttonBar: some View {
HStack(alignment: .firstTextBaseline, spacing: 12) {
let pop = SoundEffect("pop1", "pop2", "pop3", "pop4", "pop5")
Button {
isLiked.toggle()
} label: {
let likeCount = isLiked ? 144: 143
Label {
Text(likeCount.formatted())
} icon: {
Image(systemName: "heart.fill")
.changeEffect(
.spray {
Image(systemName: "heart.fill").foregroundStyle(.red)
},
value: likeCount,
isEnabled: isLiked
)
}
}
.tint(isLiked ? .red : .gray)
.changeEffect(.feedback(pop), value: isLiked, isEnabled: isLiked)
let sparkle = SoundEffect(isBoosted ? "sparkle.rising" : "sparkle.falling")
Button {
isBoosted.toggle()
} label: {
let boostCount = isBoosted ? 55 : 54
Label {
Text(boostCount.formatted())
} icon: {
Image(systemName: "arrow.3.trianglepath")
.changeEffect(
.rise {
Text("+1")
.font(.footnote.weight(.heavy))
.shadow(color: .green.opacity(0.5), radius: 1)
.foregroundStyle(.green.gradient)
},
value: boostCount,
isEnabled: isBoosted
)
}
}
.tint(isBoosted ? .green : .gray)
.changeEffect(.feedback(sparkle), value: isBoosted)
Button {
clapCount += 1
} label: {
Label {
Text(clapCount.formatted())
} icon: {
Image(systemName: "hands.clap.fill")
.changeEffect(
.spray {
Group {
Image(systemName: "circle.fill").foregroundColor(.red)
Image(systemName: "square.fill").foregroundColor(.green)
Image(systemName: "circle.fill").foregroundColor(.blue)
Image(systemName: "diamond.fill").foregroundColor(.orange)
Image(systemName: "triangle.fill").foregroundColor(.indigo)
}
.shadow(radius: 1)
.font(.caption.weight(.black))
},
value: clapCount
)
}
}
.changeEffect(.feedback(pop), value: clapCount)
.tint(clapCount > 202 ? .blue : .gray)
let pick = SoundEffect(isBookmarked ? "pick.rising" : "pick.falling")
Button {
isBookmarked.toggle()
} label: {
Label {
ZStack {
Text("Saved").hidden()
Text(isBookmarked ? "Saved" : "Save")
}
} icon: {
Image(systemName: "bookmark.fill")
}
}
.tint(isBookmarked ? .orange : .gray)
.animation(.spring(response: 0.4, dampingFraction: 1), value: isBookmarked)
.changeEffect(.feedback(pick), value: isBookmarked)
}
}
var tabBar: some View {
HStack {
Label("Home", systemImage: "house")
.labelStyle(SocialFeedTabBarLabelStyle(isSelected: true))
Label("Search", systemImage: "magnifyingglass")
Label {
Text("Notifications")
} icon: {
Image(systemName: "bell")
.overlay(alignment: .topTrailing) {
Text(notificationCount.formatted())
.fixedSize()
.font(.caption.monospacedDigit())
.foregroundColor(.white)
.padding(.vertical, 2)
.padding(.horizontal, 7)
.background(.red, in: Capsule())
.changeEffect(.pulse(shape: Capsule(), style: .red, count: 3), value: notificationCount)
.alignmentGuide(.top) { dimensions in
dimensions[VerticalAlignment.center] - 2
}
.alignmentGuide(.trailing) { dimensions in
dimensions[HorizontalAlignment.center]
}
.scaleEffect(notificationCount > 0 ? 1 : 0.1)
.opacity(notificationCount > 0 ? 1 : 0)
}
}
.onTapGesture {
withAnimation {
notificationCount += 1
}
}
Label {
Text("Archive")
} icon: {
Image(systemName: "archivebox")
.changeEffect(.jump(height: 50), value: isBookmarked, isEnabled: isBookmarked)
}
Label("Profile", systemImage: "person")
}
.labelStyle(SocialFeedTabBarLabelStyle(isSelected: false))
.padding(12)
.padding(.bottom, 2)
.background(.regularMaterial, in: Capsule(style: .continuous))
.padding(.horizontal)
}
static let localPath = LocalPath()
static var icon: Image? {
Image(systemName: "heart")
}
}
private struct SocialFeedTabBarLabelStyle: LabelStyle {
var isSelected: Bool
func makeBody(configuration: Configuration) -> some View {
VStack(spacing: 6) {
configuration.icon
.imageScale(.medium)
.symbolVariant(isSelected ? .fill : .none)
.font(.system(size: 22))
}
.foregroundStyle(isSelected ? AnyShapeStyle(.tint) : AnyShapeStyle(Color.primary))
.frame(maxWidth: .infinity)
}
}
private struct CustomButtonLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(spacing: 4) {
configuration.icon
configuration.title
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
}
.frame(maxWidth: .infinity)
}
}