Search code examples
iosswiftuiviewuitextviewnsattributedstring

Displaying text one character at a time in swift 2.0


New to Swift 2.0; trying to learn it, interested in animation and tried to write some code to display a message on screen one character at a time.

I wrote this, it works, but I cannot help but could I not do something with CALayers perhaps and/or alpha values? Or some animate gizmo, something more swift worthy; this feels & looks kinda clunky, sort 1977 really.

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


let supertext  = "A long time ago, in a galaxy far, far away ..."
let round = supertext.characters.count
for i in 0...round {
    let delay = Double(i)/10
    let subtext = supertext[supertext.startIndex..<supertext.startIndex.advancedBy(i)]
    NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: "ghostText:", userInfo: ["theText" :subtext], repeats: false)
    }
}

var view3:UITextView = UITextView()

func ghostText(timer:NSTimer) {
    view3.removeFromSuperview()
    let userInfo = timer.userInfo as! Dictionary<String, AnyObject>
    let tempText:NSString = (userInfo["theText"] as! NSString)
    print(tempText)

    let font = UIFont(name: "Georgia", size: 18.0) ?? UIFont.systemFontOfSize(18.0)
    let textFont = [NSFontAttributeName:font]

    let subChararacter = NSAttributedString(string: String(tempText), attributes: textFont)
    view3.frame = CGRect(x: 100, y: 120, width: CGRectGetWidth(self.view.frame), height: CGRectGetWidth(self.view.frame)-20)
    view3.attributedText = subChararacter
    self.view.addSubview(view3)
}

Solution

  • I suppose Swiftworthy is subjective, but here's another implementation based on how your code currently works that wraps the NSTimer in a Swift class.

    class Timer {
        typealias TimerFunction = (Int)->Bool
        private var handler: TimerFunction
        private var i = 0
    
        init(interval: NSTimeInterval, handler: TimerFunction) {
            self.handler = handler
            NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: "timerFired:", userInfo: nil, repeats: true)
        }
    
        @objc
        private func timerFired(timer:NSTimer) {
            if !handler(i++) {
                timer.invalidate()
            }
        }
    }
    
    class ViewController: UIViewController {
        let text: NSAttributedString = {
            let font = UIFont(name: "Georgia", size: 18.0) ?? UIFont.systemFontOfSize(18.0)
            return NSAttributedString(string: "A long time ago, in a galaxy far, far away ...", attributes: [NSFontAttributeName: font])
        }()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let textView = UITextView(frame: CGRect(x: 100, y: 120, width: CGRectGetWidth(self.view.frame), height: CGRectGetWidth(self.view.frame)-20))
            self.view.addSubview(textView)
    
            let _ = Timer(interval: 0.1) {i -> Bool in
                textView.attributedText = self.text.attributedSubstringFromRange(NSRange(location: 0, length: i+1))
                return i + 1 < self.text.string.characters.count
            }
        }
    }