7

I've created a library of pre-defined colours for use in Swift and Objective-C and I've rewritten a plug-in for Xcode that previews the colour in the editor so that it works with my library (along with a few other changes).

The plugin currently has a list of the colours that's created. I'm not thrilled with this solution since it means that anytime I change the list of colours I have to modify my code here too. Plus I don't like having a data structure hanging around holding approximately 1500 colours and strings. Doesn't seem to be that efficient or elegant.

What I'd like to do is instead of storing all of the colours in memory is to call the proper function of NSColor when it comes across the function name in the code. I've found that I can check to see if NSColor responds to a selector with the function respondsToSelector: but calling performSelector: doesn't meet my needs since I need the return value from the call.

So is there a way to go from this.

let colourDict [ String : NSColor ] = [
    "blackColor" : NSColor.blackColor(),
    "whiteColor" : NSColor.whiteColor() ]
...

let foundColour = colourDict[ colourMethodName ]

To something like this.

if ( NColor.respondsToSelector ( Selector ( colourMethodName ) ) {
    foundColour = NSColor.performSelector ( Selector ( colourMethodName ) )
}

Obviously it wouldn't be the performSelector: function itself but I'm looking for that type of functionality except that it would be returning back the NSColor object.

Thanks.

  • I think you should be able to [assign the function to a variable, and call it when you need the return value](http://stackoverflow.com/a/24521823/1445366) to accomplish what you want. (Which makes this question a duplicate of [Alternative to performSelector in Swift?](http://stackoverflow.com/questions/24158427/alternative-to-performselector-in-swift)) – Aaron Brager May 11 '15 at 23:45
  • 1
    It sounds like you're trying to duplicate the functionality of the [`NSColorList`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSColorList_Class/index.html#//apple_ref/occ/cl/NSColorList) class... – rickster May 12 '15 at 05:19
  • 1
    You can have an array of methods, but honestly you shouldn't be holding 1500 colors. – Schemetrical May 12 '15 at 09:54
  • Swift doesn't really like weak typing :) – DeFrenZ May 12 '15 at 10:51
  • @Schemetrical Why do you say that I shouldn't be holding 1500 colours? My library defines that many new colours. I'm not throwing in hundreds of duplicates just for the fun of it. – CanadianMacFan May 13 '15 at 17:09
  • 1
    @AaronBrager Maybe I'm missing something but wouldn't that still mean creating a data structure holding the translations from the string values to the functions? Replacing a table of colours with a table of functions, if that's the case, doesn't really make for more maintainable code. – CanadianMacFan May 13 '15 at 17:16

2 Answers2

0

Since you're defining your own named colorspace, you'll need to do something on your own because NSColor doesn't hold that many pre-defined colors. I can think of two options:

You can create a tiny database that holds the colors and load it as a resource into the project. Look up the RGBA values by name and then create the color by that definition.

Extend NSColor with a function that switches on the name to return a color. Then you're not holding them all in memory. (My example is using UIColor, but NSColor would work too)

extension UIColor {
    class func colorByName(name:String) -> UIColor {
        switch name.lowercaseString {
            case "blackcolor":
                return UIColor.blackColor()
            case "whitecolor":
                return UIColor.whiteColor()
            case "redcolor":
                return UIColor.redColor()
            // etc
        default:
            return UIColor.clearColor()
        }
    }
}


let color = UIColor.colorByName("redcolor")

Of course, if you're really clever you can combine the two and make it so the color definitions can be updated at run-time.

Aaron Bratcher
  • 6,051
  • 2
  • 39
  • 70
  • I've created libraries and put them up on GitHub that are extensions of UIColor and NSColor which define about 1500 additional colours so that you can write code such as `UIColor.africanVioletColor()`. This plugin is to show you a little box in Xcode what that colour actually is (along with the other ways of creating colours). I'm just trying to avoid having a large data structure and the code to build it. – CanadianMacFan May 21 '15 at 20:42
  • @CanadianMacFan What is the URL? – Aaron Bratcher May 23 '15 at 11:26
  • The library is called [Rainbow UIColor Extension](https://github.com/NorthernRealities/Rainbow). I'm getting a version for NSColor up soon. If you are interested in the plugin that lets you view and change the colours in Xcode you can find it [here](https://github.com/NorthernRealities/ColorSenseRainbow). – CanadianMacFan May 24 '15 at 18:34
0

I don't understand what was wrong with Aaron Brager's original suggestion. Start with a dictionary of functions:

let colourDict : [ String : () -> UIColor ] = [
    "blackColor" : UIColor.blackColor,
    "whiteColor" : UIColor.whiteColor,
    // ... and so on ...
]

This is extremely efficient - far more efficient than generating all the colors in memory and holding them there - because it's just a list of pointers, basically.

To obtain an actual color, obtain a color function by name, and call it:

let actualColor = colourDict["blackColor"]!()
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks for the example and further explanation. And if I don't find something better I will go with this. The problem is that I have about 1500 items to define since my library adds that many new colours as an extension. Plus every time I make a change to my library this becomes another place I have to remember to make that change too. That's why I was hoping to find a solution similar to performSelector. Not that performSelector would have worked because it doesn't return anything and I need a NSColor object returned. – CanadianMacFan May 21 '15 at 20:55
  • Sure, I understand. If you really want string-to-method-call conversion, stick with Objective-C and have done with it. I was just illustrating an approach. – matt May 21 '15 at 21:31