Can't set @IBInspectable computed property in UIView
Asked Answered
I

2

2

I'm trying to add an IBInspectable color to UIView, so that I can set it in the storyboard and later use it in code. In this post regarding UITextField I've seen that I can take advantage of extensions and adding a computed property, but I can't make it work for UIView.

Identity inspector enter image description here

I get a crash: Failed to set (additionalColor1) user defined inspected property on (UIView): [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key additionalColor1.

Any idea what's causing the crash and how to fix it?

Here's my code:

extension UIView {
    @IBInspectable var additionalColor1: UIColor? {
        return self.additionalColor1
    }
}

For the reference, I'm pasting the code that can be used to set the placeholder color for UITextField (same as the above url). This works ok:

extension UITextField {
    @IBInspectable var placeHolderColor: UIColor? {
        get {
            return self.placeHolderColor
        }
        set {
            self.attributedPlaceholder = NSAttributedString(string: self.placeholder != nil ? self.placeholder! : "", attributes:[NSForegroundColorAttributeName: newValue!])
        }
    }
}
Improvisation answered 19/2, 2017 at 23:35 Comment(4)
How is self.additionalColor1 being set? If it's in code, how can a design-time tool execute it?Delve
Afaik self.additionalColor1 should be set in storyboard. I've pasted the image showing that I've set it in identity inspector. In my pasted snipped I haven't impelemented a setter. However, adding self.additionalColor1 = newValue to a setter does still result in a crash.Improvisation
I'm confused. My original comment (quickly deleted) also asked if you could put an IBInspectable in an extension. I then noticed you claim you can (placeHolderColor). So assuming it can, why are you coding additionalColor1 differently? You have an explicit getter/setter in one but not the other. And (I think) if you remove the IBInspectable concept for additionalColor1, you've coded a read-only property.Delve
Thnx @dfd, you pointed me in the right direction. Why coding differently? I've mistakenly thought that I've omitted the non important part, as I wanted to use the color in code in my ViewController. Without explicit set and get it's supposed to be a get only property. It looks like when you set the color in the above way you can only use it within the scope of the setter (eg. for changing "internal" properties). The getter doesn't make any sense and can easily return nil all the time.Improvisation
G
5

As mentioned in your question title

Swift extensions can only add computed properties to a type, but they cannot add stored properties.

(For more detailed information please refer to the Extension chapter in The Swift Programming Language.)

The example you posted is actually flawed — even if it has 50 upvotes on Stackoverflow at this time. If you return the value of a property itself from the property's getter you're creating a loop.

@IBInspectable var additionalColor1: UIColor? {
    return self.additionalColor1
}

If you have a view and you try to access view.additionalColor1 anywhere in your code your property's getter will be called which returns self.additionalColor1 — or with other words: it returns the property's value again — and guess how? By calling the propery's getter! (And so on...)

The example from the post you mentioned only "works" because the getter is evidently never called. It's only setting that computed property placeHolderColor that changes another stored property, namely the text field's attributedPlaceholder.

So while you can add computed properties to an existing class through an extension you can never think of it as a concrete value that's stored somewhere. Computed properties may only be used to somehow transform the value you assign to it and store the result in existing stored properties.

Goodall answered 20/2, 2017 at 0:33 Comment(1)
We should downvote or edit the linked flawed recursive answer.Chiropractor
F
1

How is your additionalColor going to be used?

I had to do something similar to this recently, but in my case I was always applying the extra value right away.

For example, I wanted to create a button that looked like a parallelogram. So, I wanted a way to put in a value in the Storyboard, which would apply a CGAffineTransform. I'm not really storing the skew value, just using to change what the thing looks like. Then, in the get, I'm passing back the value from the view's affineTransform routine.

@IBInspectable var skewOffset: Float {
    set(newSkewOffset) {
        let affineTransform : CGAffineTransform = CGAffineTransform(a: 1.0, b: 0.0, c: CGFloat(newSkewOffset), d: 1.0, tx: 0.0, ty: 0.0)
        layer.setAffineTransform(affineTransform)
    }
    get {
        return Float(layer.affineTransform().c)
    }
}

So, I'm not storing skewOffset, I'm applying it, and I know how I can look it up later, if I need to get it.

Foreclosure answered 8/6, 2017 at 15:46 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.