0

I've created a custom widget, which is animated. Now my problem is that I can't redraw the view, when the corresponding data gets updated.

Just don't hold anything against me in the code. This is my first piece of code in swift and I haven't worked with neither swift nor with Objective-C :-D

And also I've read the following questions, but they didn't help me:

How to force a view to render itself?

what-is-the-most-robust-way-to-force-a-uiview-to-redraw

p.s. : I can see the output of print(digit.phase) in the console.

p.s.s: I've also used performSelectorOnMainThread for calling the setNeedsDisplay function

The code:

import UIKit

struct Digit {
    var targetDigit: Int
    var currentDigit: Int
    var phase: Float
}

@IBDesignable class RollerCounter: UIView {
    var view: UIView!
    var viewRect: CGRect!
    var intNumber: Int
    var digits = [Digit]()
    let baseY = 20
    var timer: NSTimer?

    @IBInspectable var number: Int {
        get {
            return intNumber
        }
        set(number) {
            intNumber = number
            digits = []

            var tempNumber:Int = intNumber
            while tempNumber > 0 {
                digits.append(Digit(targetDigit: tempNumber % 10, currentDigit: Int(rand()) % 10, phase: 0.0))
                tempNumber /= 10
            }
        }
    }

    //init
    override init(frame: CGRect) {
        // set properties:
        intNumber = 1111
        super.init(frame: frame)

        // setup the thing!
        setup()
    }

    required init?(coder aDecoder: NSCoder) {

        intNumber = 1111

        super.init(coder: aDecoder)

        // setup the thing
        setup()
    }

    // Inital setup
    func setup() {
        let viewRect = CGRect(x: 0, y: 0, width: 280, height: 40)
        view = UIView(frame: viewRect)

        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

        addSubview(view)

        self.setNeedsDisplay()

        backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.0)
    }

    func animate() {
        timer = NSTimer.scheduledTimerWithTimeInterval(0.016, target: self, selector: Selector("tick"), userInfo: nil, repeats: true)
    }

    func tick() {
        for var digit in digits {
            digit.phase += Float(rand() % 100) / 100
            print(digit.phase)
        }

        setNeedsDisplay()

        //TEST: Also tested this
//      if let rect = viewRect {
//          drawRect(rect)
//      } else {
//          viewRect = CGRect(x: 0, y: 0, width: 280, height: 40)
//          drawRect(viewRect
//      }

    }


    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        // Drawing code
        var tempNumber: Int = number
        let strTempNumber = String(tempNumber)

        var index: Int = 1
        let width = Float(rect.width)
        let charWidth: Float = Float(rect.width) / Float(strTempNumber.characters.count)
        let charHeight: CGFloat = 36

        let color = UIColor.blackColor()
        let font: UIFont = UIFont(name: "Helvetica Neue", size: charHeight)!
        let paraStyle = NSMutableParagraphStyle()
        paraStyle.lineSpacing = 6.0
        let skew = 0.1

        let textAttribs = [
            NSForegroundColorAttributeName: color,
            NSParagraphStyleAttributeName: paraStyle,
            NSObliquenessAttributeName: skew,
            NSFontAttributeName: font
        ]

        for digit in digits {

            let strCurrentDigit: NSString = String(digit.currentDigit) as NSString
            let strNextDigit: NSString = String(digit.currentDigit - 1) as NSString

            let xPos = width - Float(index) * charWidth
            let yPos = Float(baseY) + Float(charHeight) * digit.phase

            let point: CGPoint = CGPoint(x: Int(xPos), y: Int(yPos))
            strCurrentDigit.drawAtPoint(point, withAttributes: textAttribs)

            let nextDigitYPos = yPos - Float(charHeight) * 1.2
            let nextDigitPoint: CGPoint = CGPoint(x: Int(xPos), y: Int(nextDigitYPos))
            strNextDigit.drawAtPoint(nextDigitPoint, withAttributes: textAttribs)

            index++
            tempNumber /= 10
        }
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Milad.Nozari
  • 2,243
  • 3
  • 26
  • 47

1 Answers1

0

Sorry folks. My bad :-(

There's nothing wrong with the invalidation system. Here's what's wrong:

for var digit in digits {
            digit.phase += Float(rand() % 100) / 100
            print(digit.phase)
        }

As it turns out, the changes to phase only get reflected in the local digit instance inside the for loop

But just to be clear, the setNeedsDisplay() call inside the tick method is crucial for the view to be updated.

Milad.Nozari
  • 2,243
  • 3
  • 26
  • 47