I have the following extension which I want to make usable for both an UITextView and an UILabel.
extension UITextView {
func setTextWithTypeAnimation(typedText: String, characterDelay: TimeInterval = 2.5) {
text = ""
var writingTask: DispatchWorkItem?
writingTask = DispatchWorkItem { [weak weakSelf = self] in
for character in typedText {
DispatchQueue.main.async {
weakSelf?.text!.append(character)
}
Thread.sleep(forTimeInterval: characterDelay/100)
}
}
if let task = writingTask {
let queue = DispatchQueue(label: "typespeed", qos: DispatchQoS.userInteractive)
queue.asyncAfter(deadline: .now() + 0.05, execute: task)
}
}
}
I tried to set the type to UIView found here: Stack Overflow: Single extension for UITextView and UITextField in Swift
extension UIView {
func setTextWithTypeAnimation(typedText: String, characterDelay: TimeInterval = 2.5) {
if self is UILabel || self is UITextView {
text = ""
var writingTask: DispatchWorkItem?
writingTask = DispatchWorkItem { [weak weakSelf = self] in
for character in typedText {
DispatchQueue.main.async {
weakSelf?.text!.append(character)
}
Thread.sleep(forTimeInterval: characterDelay/100)
}
}
if let task = writingTask {
let queue = DispatchQueue(label: "typespeed", qos: DispatchQoS.userInteractive)
queue.asyncAfter(deadline: .now() + 0.05, execute: task)
}
}
}
}
That of course doesn't work because UIView has no text property so I get the following error:
Use of unresolved identifier 'text'
How can I solve this?
That of course doesn't work because UIView has no text property
This is exactly the piece you want to think about. What do you need in order to write this method? Two things: it needs a text
property. And it needs to be a class type (because you use a weak
modifier on it). So say that.
protocol TypeAnimated: AnyObject {
var text: String? { get set }
}
Except that for historical reasons, UITextView has a String!
while UILabel
has a String?
. That's very frustrating, but we can bridge the two with a new property:
protocol TypeAnimated: AnyObject {
var animatedText: String { get set }
}
Now, given that protocol, you can write you method:
extension TypeAnimated {
func setTextWithTypeAnimation(typedText: String, characterDelay: TimeInterval = 2.5) {
animatedText = ""
var writingTask: DispatchWorkItem?
writingTask = DispatchWorkItem { [weak weakSelf = self] in
for character in typedText {
DispatchQueue.main.async {
weakSelf?.animatedText!.append(character)
}
Thread.sleep(forTimeInterval: characterDelay/100)
}
}
if let task = writingTask {
let queue = DispatchQueue(label: "typespeed", qos: DispatchQoS.userInteractive)
queue.asyncAfter(deadline: .now() + 0.05, execute: task)
}
}
}
Now, you just need to label any types you want to conform to this protocol, and they'll get the extension.
extension UILabel: TypeAnimated {
var animatedText: String {
get { return text ?? "" }
set { text = newValue }
}
}
extension UITextView: TypeAnimated {
var animatedText: String {
get { return text ?? "" }
set { text = newValue }
}
}
As a side note, generating a new queue every time this is executed is almost certainly not what you mean. You should probably just set this up as a series of asyncAfter
calls to the main queue, or use a Timer, or call asyncAfter
inside the asyncAfter
block. But none of this really impacts your question.
I haven't tested this, but this is how I would probably approach the problem:
extension TypeAnimated {
func setTextWithTypeAnimation(typedText: String, characterDelay: TimeInterval = 2.5/100) {
func addNextCharacter(from string: Substring) {
DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay) { [weak self] in
if let self = self, let nextChar = string.first {
self.animatedText.append(nextChar)
addNextCharacter(from: string.dropFirst())
}
}
}
animatedText = ""
addNextCharacter(from: typedText[...])
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.