Created
February 15, 2025 02:45
-
-
Save Koshimizu-Takehito/9aea2fda75a1b7c2f3260de5e633cf24 to your computer and use it in GitHub Desktop.
TypingText
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContentView: View { | |
private var title: String { | |
Item.sample.title | |
} | |
private var message: String { | |
Item.sample.message | |
} | |
@State private var progress: Double = 0 | |
@State private var isOn = false | |
var body: some View { | |
VStack(alignment: .leading, spacing: 16) { | |
Text(title) | |
.font(.largeTitle) | |
.fontWeight(.bold) | |
TypingText(fullText: message, animatableData: progress) | |
.font(.custom("Courier", size: 20)) | |
.lineSpacing(10) | |
} | |
.onChange(of: progress) { _, progress in | |
if progress.truncatingRemainder(dividingBy: 1) == 0 { | |
isOn = progress == 1 | |
} | |
} | |
.onChange(of: isOn) { _, isOn in | |
withAnimation(.linear(duration: Double(message.count) / 30.0)) { | |
progress = isOn ? 1 : 0 | |
} | |
} | |
.multilineTextAlignment(.leading) | |
.frame(maxHeight: .infinity) | |
.padding(.horizontal) | |
.ignoresSafeArea() | |
.overlay { | |
HStack { | |
Slider(value: $progress, in: 0...1) | |
Toggle(isOn: $isOn) { | |
Text("Toggle") | |
} | |
} | |
.frame(maxHeight: .infinity, alignment: .bottom) | |
.padding() | |
.toggleStyle(.button) | |
.fontWeight(.black) | |
} | |
.tint(.orange) | |
} | |
} | |
struct TypingText: View, Animatable { | |
var fullText: String | |
var animatableData: Double // 0...1 | |
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) | |
var visibles = AttributedString(text[startIndex..<endIndex]) | |
visibles.foregroundColor = .red // .black | |
var invisibles = AttributedString(text[endIndex..<text.endIndex]) | |
invisibles.foregroundColor = .black.opacity(0.2) // .clear | |
return visibles + invisibles | |
} | |
} | |
struct Item { | |
var title: String | |
var message: String | |
static let sample: Self = .init( | |
title: "SwiftUI\nFramework", | |
message: "SwiftUI is Apple’s declarative UI framework for cross-platform development. It simplifies UI building with state-driven rendering and real-time Xcode previews. Data binding and built-in components enhance efficiency and consistency." | |
) | |
} | |
#Preview { | |
ContentView() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment