简体   繁体   English

如何在 Swift 5 中为多种类型进行扩展

[英]How to make an extension for multiple types in Swift 5

I have the following extension which I want to make usable for both an UITextView and an UILabel.我有以下扩展,我想让它同时用于 UITextView 和 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我试图将类型设置为 UIView 在这里找到: 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:这当然不起作用,因为 UIView 没有 text 属性,所以我收到以下错误:

Use of unresolved identifier 'text'

How can I solve this?我该如何解决这个问题?

That of course doesn't work because UIView has no text property这当然不起作用,因为 UIView 没有 text 属性

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.两件事:它需要一个text属性。 And it needs to be a class type (because you use a weak modifier on it).并且它需要是一个类类型(因为你在它上面使用了一个weak修饰符)。 So say that.所以这么说。

protocol TypeAnimated: AnyObject {
    var text: String? { get set }
}

Except that for historical reasons, UITextView has a String!除了历史原因,UITextView 有一个String! while UILabel has a String?UILabel有一个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.您可能应该将其设置为对主队列的一系列asyncAfter调用,或使用计时器,或在asyncAfter块内调用asyncAfter 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[...])
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM