72 lines
2.3 KiB
Swift
72 lines
2.3 KiB
Swift
import SwiftUI
|
||
|
||
struct MarqueeText: View {
|
||
let text: String
|
||
let font: Font
|
||
let foregroundColor: Color
|
||
private let speed: CGFloat = 10 // пикселей в секунду
|
||
|
||
@State private var textWidth: CGFloat = 0
|
||
@State private var containerWidth: CGFloat = 0
|
||
@State private var offset: CGFloat = 0
|
||
@State private var animationStarted = false
|
||
|
||
var body: some View {
|
||
GeometryReader { geo in
|
||
Text(text)
|
||
.font(font)
|
||
.foregroundColor(foregroundColor)
|
||
.lineLimit(1)
|
||
.fixedSize() // важно, чтобы не обрезался
|
||
.background(WidthGetter())
|
||
.offset(x: offset)
|
||
.onAppear {
|
||
containerWidth = geo.size.width
|
||
if textWidth > containerWidth, !animationStarted {
|
||
animationStarted = true
|
||
let distance = textWidth - containerWidth
|
||
let duration = Double(distance / speed)
|
||
withAnimation(.linear(duration: duration).repeatForever(autoreverses: true)) {
|
||
offset = -distance
|
||
}
|
||
}
|
||
}
|
||
.clipped()
|
||
}
|
||
.frame(height: lineHeight(for: font))
|
||
.onPreferenceChange(WidthKey.self) { width in
|
||
textWidth = width
|
||
}
|
||
}
|
||
|
||
private func lineHeight(for font: Font) -> CGFloat {
|
||
switch font {
|
||
case .largeTitle: return 34
|
||
case .title: return 28
|
||
case .title2: return 22
|
||
case .title3: return 20
|
||
case .headline: return 17
|
||
case .body: return 17
|
||
case .callout: return 16
|
||
case .subheadline: return 15
|
||
case .caption: return 13
|
||
case .caption2: return 12
|
||
default: return 17
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: — Helpers для измерения ширины текста
|
||
private struct WidthKey: PreferenceKey {
|
||
static var defaultValue: CGFloat = 0
|
||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() }
|
||
}
|
||
|
||
private struct WidthGetter: View {
|
||
var body: some View {
|
||
GeometryReader { geo in
|
||
Color.clear.preference(key: WidthKey.self, value: geo.size.width)
|
||
}
|
||
}
|
||
}
|