Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: How to implement User Defined Runtime Attributes

Tags:

xcode

ios

swift

So I am implementing a custom "chooser" toolbar, like the iOS equivalent of a radio button set (UISegmentedControl). Just a horizontal bar divided into options.

To do this, I created a subclass of UIControl called SegmentedControl and implemented custom drawing. However, with such a view, I need the option to set what the available options are. I could have just accessed the view from the controller's viewDidLoad() and set those there, but I like using the interface builder for that kind of stuff.

So I discovered this wonderful thing called "User Defined Runtime Attributes." I created a String attribute with a key buttonValues and set a value (this is a simple Male/Female chooser so I went with "Male|Female"). I found out that you can access these values using the function self.valueForKey() and pass in the key. I made a parser to turn that string into an array and then added functionality for the drawRect() function to use the array to set up the buttons.

When I ran the app, I got an error about "Key Value Coding-compliance."

So I looked that up, and I found out that the class has to have backing variables to store the attributes. So fine, I added an instance variable called buttonValues and initialized it to "". Now the app runs fine but the value comes out empty from the self.valueForKey() function. I looked up tutorials on how to set up user defined runtime attributes but they don't go into enough detail. They talk about Key Value Coding-compliance like it's something I should just know.

I would like to know exactly what I must do for this to work properly, in gory detail.

like image 891
jchitel Avatar asked Jun 26 '14 08:06

jchitel


1 Answers

For your purposes you can use either user-defined runtime attributes or expose your class and properties as editable in Interface Builder. Either way, you'll want to declare your properties as implicitly unwrapped Optional variables -- IBOutlets are created the same way. If you need to make other changes once your properties have a value, give them didSet property observers. The properties will be at their default value during initialization (or nil if no default was set) and set when the view is added to a superview.

User Defined Runtime Attributes

This works more or less like you've described above -- here's the simplest version:

class LabeledView : UIView {
    var viewLabel: String! {
    didSet {
        println("didSet viewLabel, viewLabel = \(self.viewLabel)")
    }
    }

    init(coder aDecoder: NSCoder!)  {
        super.init(coder: aDecoder)
        println("init with coder, viewLabel = \(self.viewLabel)")
    }
}

Then set the viewLabel attribute to "Hello" on the view in your storyboard, like so:

Xcode panel showing user-defined runtime attributes

When you build & run, the console will show that the property is being set correctly:

init with coder, viewLabel = nil
didSet viewLabel, viewLabel = Hello

@IBDesignable & @IBInspectable

This gives your custom view a much nicer interface in IB -- set the @IBDesignable attribute on your class and @IBInspectable attributes on each property. Here's the same class:

@IBDesignable class LabeledView : UIView {
    @IBInspectable var viewLabel: String!

    init(coder aDecoder: NSCoder!)  {
        super.init(coder: aDecoder)
    }
}

Now you can set your inspectable properties at the top of the Attributes Inspector in Interface Builder:

Attributes Inspector with inspectable property

like image 127
Nate Cook Avatar answered Nov 20 '22 17:11

Nate Cook