147 lines
4.9 KiB
Swift
147 lines
4.9 KiB
Swift
//
|
|
// OnBoardingCardButtons.swift
|
|
// Pocket Poster
|
|
//
|
|
// Created by lemin on 5/31/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct OnBoardingCardButtons: View {
|
|
@Binding var isFinished: Bool
|
|
|
|
@Binding var idx: Int
|
|
@Binding var isAnimating: Bool
|
|
|
|
var showDismiss: Bool
|
|
var showFinish: Bool
|
|
|
|
var body: some View {
|
|
HStack {
|
|
// MARK: Dismiss Button
|
|
if showDismiss {
|
|
Button(action: {
|
|
playFinishAnimation()
|
|
}) {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "x.circle")
|
|
.imageScale(.large)
|
|
|
|
Text("Skip")
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 10)
|
|
.background(
|
|
Capsule().strokeBorder(Color.white, lineWidth: 1.25)
|
|
)
|
|
}
|
|
.accentColor(Color.white)
|
|
.accessibilityIdentifier("OnBoardingSkip")
|
|
}
|
|
|
|
// MARK: Next Button
|
|
Button(action: {
|
|
if showFinish {
|
|
playFinishAnimation()
|
|
} else {
|
|
// move to next
|
|
isAnimating = false
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
|
|
idx += 1
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
|
|
isAnimating = true
|
|
})
|
|
})
|
|
}
|
|
}) {
|
|
HStack(spacing: 8) {
|
|
Text(showFinish ? "Finish" : "Next")
|
|
|
|
Image(systemName: showFinish ? "checkmark.circle" : "arrow.right.circle")
|
|
.imageScale(.large)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 10)
|
|
.background(
|
|
Capsule().strokeBorder(Color.white, lineWidth: 1.25)
|
|
)
|
|
}
|
|
.accentColor(Color.white)
|
|
.accessibilityIdentifier("OnBoardingNext")
|
|
}
|
|
}
|
|
|
|
func playFinishAnimation() {
|
|
isAnimating = false
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
|
|
isFinished = true
|
|
})
|
|
}
|
|
}
|
|
|
|
struct OnBoardingCardView: View {
|
|
var info: OnBoardingPage
|
|
var pageCount: Int
|
|
@Binding var idx: Int
|
|
@Binding var isFinished: Bool
|
|
|
|
@State private var isAnimating: Bool = false
|
|
@State private var imgCornerRadius: CGFloat = 10.0
|
|
|
|
var body: some View {
|
|
VStack(spacing: 20) {
|
|
// Image
|
|
Image(info.image)
|
|
.resizable()
|
|
.scaledToFit()
|
|
.clipShape(RoundedRectangle(cornerRadius: imgCornerRadius))
|
|
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.15), radius: 8, x: 6, y: 8)
|
|
.scaleEffect(isAnimating ? 1.0 : 0.6)
|
|
|
|
// Title
|
|
Text(info.title)
|
|
.foregroundColor(Color.white)
|
|
.multilineTextAlignment(.center)
|
|
.font(.largeTitle)
|
|
.fontWeight(.heavy)
|
|
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.15), radius: 2, x: 2, y: 2)
|
|
|
|
// Description
|
|
Text(info.description)
|
|
.foregroundColor(Color.white)
|
|
.multilineTextAlignment(.center)
|
|
.padding(.horizontal, 16)
|
|
.frame(maxWidth: 480)
|
|
|
|
// Link Button
|
|
if let url = info.link, let linkName = info.linkName {
|
|
Link(destination: url) {
|
|
HStack(spacing: 8) {
|
|
Image(systemName: "link.circle")
|
|
.imageScale(.large)
|
|
|
|
Text(linkName)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 10)
|
|
.background(
|
|
Capsule().strokeBorder(Color.white, lineWidth: 1.25)
|
|
)
|
|
}
|
|
.accentColor(Color.white)
|
|
}
|
|
|
|
OnBoardingCardButtons(isFinished: $isFinished, idx: $idx, isAnimating: $isAnimating, showDismiss: idx == 0, showFinish: idx == pageCount-1)
|
|
}
|
|
.opacity(isAnimating ? 1.0 : 0.0)
|
|
.animation(.easeOut(duration: 0.5), value: isAnimating)
|
|
.onAppear {
|
|
isAnimating = true
|
|
}
|
|
.padding(.horizontal, 20)
|
|
.padding(.vertical, 25)
|
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
|
|
.background(LinearGradient(gradient: Gradient(colors: info.gradientColors), startPoint: .top, endPoint: .bottom).ignoresSafeArea())
|
|
}
|
|
}
|