add onboarding tutorial + icon

This commit is contained in:
leminlimez committed 2025-05-31 21:13:48 -04:00
1 parent 3bad6ffe14
commit 2a81e09858
21 files changed
+406 -15

No files matched your search

+1 -1
View File
@@ -92,7 +92,7 @@ struct ContentView: View {
}
.alert("Success!", isPresented: $showSuccessAlert) {
Button("OK") {
PosterBoardManager.runShortcut(named: "PosterBoard") // shortcut url: https://www.icloud.com/shortcuts/9035ef1d51c247dba8c9861dc10b4aa7
PosterBoardManager.runShortcut(named: "PosterBoard")
}
} message: {
Text(lastError ?? "???")
@@ -0,0 +1,145 @@
//
// OnBoardingCardButtons.swift
// Pocket Poster
//
// Created by lemin on 5/31/25.
//
import SwiftUI
struct OnBoardingCardButtons: View {
@AppStorage("finishedTutorial") var finishedTutorial: 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: {
finishedTutorial = true
})
}
}
struct OnBoardingCardView: View {
var info: OnBoardingPage
var pageCount: Int
@Binding var idx: Int
@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(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())
}
}
@@ -0,0 +1,57 @@
//
// OnBoardingPage.swift
// Pocket Poster
//
// Created by lemin on 5/31/25.
//
import SwiftUICore
struct OnBoardingPage: Identifiable {
var id = UUID()
var title: String
var description: String
var image: String
var link: URL?
var linkName: String?
var gradientColors: [Color]
init(title: String, description: String, image: String, link: URL? = nil, linkName: String? = nil, gradientColors: [Color] = [Color("WelcomeLight"), Color("WelcomeDark")]) {
self.id = UUID()
self.title = title
self.description = description
self.image = image
self.link = link
self.linkName = linkName
self.gradientColors = gradientColors
}
}
let onBoardingCards: [OnBoardingPage] = [
.init(
title: "Welcome to Pocket Poster!",
description: "Here is a tutorial to help you get started with the app.",
image: "Logo"
),
.init(
title: "Install the Shortcut",
description: "To apply, you will need a shortcut that opens PosterBoard.",
image: "Shortcuts",
link: URL(string: "https://www.icloud.com/shortcuts/9035ef1d51c247dba8c9861dc10b4aa7"),
linkName: "Get Shortcut"
),
.init(
title: "Install Nugget",
description: "To get the app bundle id, Nugget is required.\n\nOn your computer, download Nugget from the GitHub.",
image: "Nugget",
link: URL(string: "https://github.com/leminlimez/Nugget/releases/latest"),
linkName: "Open GitHub"
),
.init(
title: "Enjoy!",
description: "You can find wallpapers on the official Cowabun.ga website.",
image: "Cowabunga",
link: URL(string: "https://cowabun.ga/wallpapers"),
linkName: "Find Wallpapers"
)
]
@@ -0,0 +1,18 @@
//
// OnBoardingView.swift
// Pocket Poster
//
// Created by lemin on 5/31/25.
//
import SwiftUI
struct OnBoardingView: View {
@State var currentIndex: Int = 0
var body: some View {
VStack {
OnBoardingCardView(info: onBoardingCards[currentIndex], pageCount: onBoardingCards.count, idx: $currentIndex)
}
}
}