Search code examples
iosswiftswift4ibinspectable

Value of @IBInspectable set in storyboard not propagated to the code


Without any experience with iOS, I have been asked to upgrade to Swift 4 an application that is using Swift 3. I updated the language to use in "Swift language version" and I followed the recommendations made by xCode (explicitly use @objc).

Once all errors fixed the application works, but one UI-related feature is not working anymore. The app has a custom UITextField that is used to enter a password. Depending on the value of a @IBInspectable class field of the custom text field the text is hidden or not.

The value of this variable is correctly set in MainStoryboard but when the class is instantiated this value is not set to the value in the storyboard, and as a result, the custom text field does not behave how it should. The log does not mention any error.

Does anybody experience the same problem?

I checked storyboard and class, I did not find any problems, I also compared the code of the version with Swift 3 and the one I updated with Swift 4 and there are no differences (except the @objc ones).

Are there something else to look that can help me to find why the value set in MainStoryboard is not propagated to the class?

Edit

Below is the code involved in the problem:

  • a variable isPassword is declared to reflect the value of the IBInspectable variable
  • the variable IBInspectable is declared
  • when awakeFromNib depending on the value of isPassword a method to either hide the text or not is called

    @IBDesignable class CustomEditText: UITextField {
    var isPassword = false
    
    // Inspector variable determining if the text must be hidden or not
    @IBInspectable var isAPasswordField: Bool {
        get{
            return self.isPassword
        }
    
        set{
            self.isPassword = isAPasswordField
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        setup()   // Set borders, font, ...
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        if isPassword {               // isPassword does not reflect the value of isAPasswordField set in storyboard
            self.isSecureTextEntry = true
            setRevealButton()         // the text is hidden
        } else {
            setCustomClearButton()    // the text is not hidden
        }
    }
    

...

Answer

@Alladinian gave the good answer. Below for completeness, another solution that I found:

@IBInspectable var isPasswordField: Bool = false {
    didSet{
        self.isPassword = isPasswordField

        if isPassword {
            self.isSecureTextEntry = true
            setRevealButton()
        } else {
            setCustomClearButton()
        }
    }
}

Solution

  • The correct form would be (note the setter assignment):

    @IBInspectable var isAPasswordField: Bool {
        get {
            return self.isPassword
        }
    
        set {
            self.isPassword = newValue
        }
    }
    

    since the actual value have not been (and will not be - at least 'directly') set on isAPasswordField (remember, we are implementing the setter of a computed property after all...)

    Also, why don't you just use isPassword directly by marking it as inspectable, avoiding the need for an extra ivar?


    PS: newValue is the default name for the to-be-set value. You can read more about it under Shorthand Setter Declaration section in the Swift Programming Language book