Resolve recursion in Swift property override

Here’s a simple extension in Swift, for UILabel,

let dur=0.1 // (set to say 2.0 to see the effect more clearly)
extension UILabel
    {
    func change(s:String)->()
        {
        print("attempting 'change' with \(s)")
        UIView.animateWithDuration( dur,
            animations: { self.alpha = 0.2 },
            completion:
                { _ in
                self.text = s      ///CCC
                UIView.animateWithDuration( dur,
                    animations: { self.alpha = 1.0 })
                })
        }
    }

with a UILabel, simply do this

  • Xcode 4: My iPhone projects have become Mac OS projects.. and I can't change this
  • How to check if WiFi is on or off in iOS Swift 2?
  • How to get the rect of a UICollectionViewCell?
  • Custom UINavigationBar background image appears darker than the original image
  • Trying to get the span size in meters for an iOS MKCoordinateSpan
  • Core Data: Fetch result from multiple entities or relationship
  • aLabel.change("hello there")
    

    It will quickly blend from the old to the new text. No problem.

    Of course, it would be better if we could write this …

    aLabel.text = "hello there"
    

    To do that, just make a new UILabel class, with a new version of the “.text” property.

    class CoolLabel:UILabel
        {
        override var text:String?
            {
            get { return super.text }
            set { super.change(newValue!) } //PROBLEM! AAA
            }
        }
    

    But! It doesn’t work: it goes in to an endless loop.

    Notice the “self.text” in the change() extension: at that point it goes in to a loop.

    (I also tried set { self.change(newValue!) } and it does not work.)

    The following works perfectly:

    class TOLabel:UILabel
        {
        override var text:String?
            {
            get { return super.text }
            set
                {
                UIView.animateWithDuration( dur,
                    animations: { self.alpha = 0.2 },
                    completion:
                        { _ in
                        super.text = newValue //BBB
                        UIView.animateWithDuration( dur,
                            animations: { self.alpha = 1.0 })
                        })
                }
            }
        }
    

    That’s fine, but what am I doing wrong in the first version?

    How would you write the setter to successfully use the .change extension?


    By the way, for anyone reading here is how you would more fully subclass IBLabel, you have to override the designated initialiser, and, you’ll need to keep a local “realtime” version of text so that the getter replies correctly during animations, immediately after you set the text.

    class TOLabel:UILabel
        {
        private var _text:String?
        required init?(coder aDecoder: NSCoder)
            {
            super.init(coder: aDecoder)
            self._text = super.text;
            }
        override var text:String?
            {
            get { return self._text }
            set {
                self._text = newValue;
                UIView.animateWithDuration( dur,
                    animations: { self.alpha = 0.2 },
                    completion:
                        { _ in
                        super.text = self._text
                        UIView.animateWithDuration( dur,
                            animations: { self.alpha = 1.0 })
                        })
                }
            }
        }
    

    Solutions Collect From Internet About “Resolve recursion in Swift property override”

    Your recursion is called when you use self.text= to assign a new value in your change extension method because this will call the setter, which calls change and so on.

    In your second example, you avoid recursion because you can call the superclass setter from your subclass setter. You don’t have this option in your extension because your code is running as an extension to UILabel and therefore there is no superclass setter to call.

    Arguably creating a subclass rather than using extension is the more correct approach in this instance anyway