vibetunnel/mac/VibeTunnel/Presentation/Components/ShimmerView.swift
Peter Steinberger a9fd66c291 refactor: major project restructuring - move macOS app to mac/ directory
- Move all macOS-specific code from root to mac/ directory
- Move app icons and assets to dedicated assets/ directory
- Update GitHub workflows for new structure
- Consolidate documentation files
- Clean up root directory for better multi-platform organization
2025-06-20 13:20:01 +02:00

98 lines
2.9 KiB
Swift

import SwiftUI
/// A shimmer loading effect view that maintains consistent height
struct ShimmerView: View {
@State private var isAnimating = false
let height: CGFloat
let cornerRadius: CGFloat
init(height: CGFloat = 20, cornerRadius: CGFloat = 4) {
self.height = height
self.cornerRadius = cornerRadius
}
var body: some View {
RoundedRectangle(cornerRadius: cornerRadius)
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color.gray.opacity(0.3),
Color.gray.opacity(0.15),
Color.gray.opacity(0.3)
]),
startPoint: .leading,
endPoint: .trailing
)
)
.frame(height: height)
.mask(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(
LinearGradient(
gradient: Gradient(colors: [
Color.black.opacity(0.3),
Color.black,
Color.black.opacity(0.3)
]),
startPoint: isAnimating ? .leading : .trailing,
endPoint: isAnimating ? .trailing : .leading
)
)
.animation(
Animation.linear(duration: 1.5)
.repeatForever(autoreverses: false),
value: isAnimating
)
)
.onAppear {
isAnimating = true
}
}
}
/// A text-sized shimmer that matches the height of text
struct TextShimmer: View {
let text: String
let font: Font
@State private var textHeight: CGFloat = 20
var body: some View {
ZStack {
// Hidden text to measure height
Text(text)
.font(font)
.opacity(0)
.background(
GeometryReader { geometry in
Color.clear
.preference(key: HeightPreferenceKey.self, value: geometry.size.height)
}
)
.onPreferenceChange(HeightPreferenceKey.self) { height in
textHeight = height
}
ShimmerView(height: textHeight, cornerRadius: 4)
}
}
}
private struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
#Preview {
VStack(spacing: 20) {
ShimmerView()
.frame(width: 200)
TextShimmer(text: "Loading...", font: .body)
.frame(width: 100)
}
.padding()
}