Skip to content

Instantly share code, notes, and snippets.

@Koshimizu-Takehito
Created February 15, 2025 01:25
Show Gist options
  • Save Koshimizu-Takehito/8a624af1e8b0f40be917dc00340e1d56 to your computer and use it in GitHub Desktop.
Save Koshimizu-Takehito/8a624af1e8b0f40be917dc00340e1d56 to your computer and use it in GitHub Desktop.
ColorNavigation2
import SwiftUI
struct ContentView: View {
@State var isExpanded = [true] + Array(repeating: false, count: PageItem.samples.count)
var body: some View {
ZStack {
ForEach(1..<isExpanded.count, id: \.self) { offset in
let item = PageItem.samples[(offset-1) % PageItem.samples.count]
PageView(
title: item.title,
message: item.message,
color: .color(offset: offset),
isExpanded: $isExpanded[offset],
isExpandedPrevious: isExpanded[offset-1]
)
}
}
.ignoresSafeArea()
}
}
struct PageView: View {
var title: String
var message: String
var color: Color
@Binding var isExpanded: Bool
var isExpandedPrevious: Bool
var body: some View {
circle
.overlay { contents }
.mask { circle }
.onTapGesture {
isExpanded.toggle()
}
}
var circle: some View {
CircleView(
color: color,
isExpanded: isExpanded,
isExpandedPrevious: isExpandedPrevious
)
}
var contents: some View {
GeometryReader { geometry in
VStack(alignment: .leading, spacing: 16) {
Text(title)
.font(.largeTitle)
.fontWeight(.bold)
TypingText(fullText: message, animatableData: isExpanded ? 1 : 0)
.font(.custom("Courier", size: 16))
.lineSpacing(12)
.animation(typing()?.delay(1.2), value: isExpanded)
}
.multilineTextAlignment(.leading)
.allowsTightening(true)
.fixedSize(horizontal: false, vertical: true)
.foregroundStyle(isExpanded ? .black : .clear)
.opacity(isExpanded ? 1 : 0)
.scaleEffect(isExpanded ? 1 : 0)
.offset(x: isExpanded ? 0 : 0.8 * geometry.size.width)
.animation(.customSpring, value: isExpanded)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
}
.padding(.horizontal)
.allowsHitTesting(false)
}
func typing() -> Animation? {
if isExpanded {
Animation.linear(duration: Double(message.count) / 30.0)
} else {
Animation?.none
}
}
}
struct TypingText: View, Animatable {
var fullText: String
var animatableData: Double
var body: some View {
Text(displayText)
}
private var displayText: AttributedString {
let text = fullText
let count = Int(Double(text.count) * min(max(animatableData, 0), 1))
let startIndex = text.index(text.startIndex, offsetBy: 0)
let endIndex = text.index(text.startIndex, offsetBy: count)
let visibles = AttributedString(text[startIndex..<endIndex])
var invisibles = AttributedString(text[endIndex..<text.endIndex])
invisibles.foregroundColor = .clear
return visibles + invisibles
}
}
struct CircleView: View {
var color: Color
var isExpanded: Bool
var isExpandedPrevious: Bool
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width
let height = geometry.size.height
let diagonal = sqrt(width * width + height * height)
let diameter = isExpanded ? diagonal : (min(width, height) / 2)
ZStack {
Circle()
.foregroundStyle(color)
Label("Next", systemImage: "arrow.right")
.font(.title2)
.fontWeight(.semibold)
.offset(x: isExpanded ? 0 : -0.1 * diameter/2)
.opacity(isExpanded ? 0 : 1)
.scaleEffect(isExpanded ? 0 : 1)
}
.frame(width: diameter, height: diameter)
.offset(
x: isExpanded ? -(diagonal - width)/2 : 0.2 * diameter,
y: isExpanded ? -(diagonal - height)/2 : 0.1 * diameter
)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
.scaleEffect(isExpandedPrevious ? 1 : 0, anchor: .bottomTrailing)
.offset(x: isExpandedPrevious ? 0 : diameter)
.animation(.customSpring, value: isExpandedPrevious)
}
.animation(.customSpring, value: isExpanded)
}
}
extension Animation {
static var customSpring: Self {
.interpolatingSpring(duration: 0.9, bounce: 0.3)
}
}
extension Color {
static func color(offset: Int) -> Self {
let hue = Double((123 * offset) % 360) / 360.0
return Color(hue: hue, saturation: 0.4, brightness: 0.96)
}
}
struct PageItem {
var title: String
var message: String
static let samples: [Self] = [
Self.init(
title: "SwiftUI\nFramework",
message: "SwiftUI is a declarative UI framework that simplifies cross-platform development with state-driven rendering, real-time previews, and data binding."
),
Self.init(
title: "UIKit\nFramework",
message: "UIKit is Apple’s core UI framework for iOS, offering components for layouts, animations, and interactions, with Auto Layout and extensive support."
),
Self.init(
title: "Metal\nFramework",
message: "Metal is a low-level API for high-performance graphics and computing, optimizing rendering for 3D graphics, gaming, and machine learning."
),
Self.init(
title: "Swift\nConcurrency",
message: "Swift Concurrency enables efficient asynchronous programming with async/await, structured concurrency, and actors for thread safety."
)
]
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment