Over a million developers have joined DZone.

Composite Components in Swift: Dial Based RGB / CMYK Picker

· Java Zone

Microservices! They are everywhere, or at least, the term is. When should you use a microservice architecture? What factors should be considered when making that decision? Do the benefits outweigh the costs? Why is everyone so excited about them, anyway?  Brought to you in partnership with IBM.

Continuing on from my last blog post, Creating a Numeric Dial Control in Swift for iOS, this post looks at creating a composite component using more that one of my dial controls. The example I've created is a colour picker which allows the user to use dials to set either the red, green and blue or cyan, magenta, yellow and black components.


My RGBpicker class is an extended UIControl which contains either three or four dials, depending on whethercmykMode is false or true. Much like the dial control itself, its components are added in the init() method. Because the dial for black is only used in CMYK mode, it's an optional while the other three dials aren't.


The colour dials are responsible for different colours based on the mode and are named accordingly. For example, the dial that changes either the red or cyan component is named redCyanDial.


The RGBpicker has a currentColor property and inside its didSet observer is the code to update the individual dials based on the new colour. That code is basically split into two with one set of calculations for CMYK and one set for RBG:

    var currentColor : UIColor = UIColor.blackColor()
    {
        didSet
        {
            if updateDialsOnColorChange
            {
                let colorRef = CGColorGetComponents(currentColor.CGColor);
                
                removeDispatchers()
          
                if cmykMode
                {
                    let k = 1 - max(max(Double(colorRef[0]), Double(colorRef[1])), Double(colorRef[2]))
                    
                    blackDial!.currentValue = k
                    redCyanDial.currentValue = (1 - Double(colorRef[0]) - k) / (1 - k)
                    greenMagentaDial.currentValue = (1 - Double(colorRef[1]) - k) / (1 - k)
                    blueYellowDial.currentValue = (1 - Double(colorRef[2]) - k) / (1 - k)
                }
                else
                {
                    redCyanDial.currentValue = Double(colorRef[0])
                    greenMagentaDial.currentValue = Double(colorRef[1])
                    blueYellowDial.currentValue = Double(colorRef[2])
                }
        
                addDispatchers()
            }
            
            swatch.backgroundColor = currentColor
            
            sendActionsForControlEvents(.ValueChanged)
        }

    }

The addDispatchers() and removeDispatchers() functions prevent the dials from dispatching change events while I'm updating them. Without these, the code enters an infinite loop.


All four dials invoke the same action when they're change by the user. Again, this method is split into two depending on the mode and updates the control's current colour based on the dial values:

    func numericDialValueChanged(numericDial : NumericDial)
    {
        updateDialsOnColorChange = false
        
        if cmykMode
        {
            let red = (1 - CGFloat(redCyanDial.currentValue)) * (1 - CGFloat(blackDial!.currentValue))
            let green = (1 - CGFloat(greenMagentaDial.currentValue)) * (1 - CGFloat(blackDial!.currentValue))
            let blue = (1 - CGFloat(blueYellowDial.currentValue)) * (1 - CGFloat(blackDial!.currentValue))
            
            currentColor = UIColor(red: red, green: green, blue: blue, alpha: 1)
        }
        else
        {
            let red = CGFloat(redCyanDial.currentValue)
            let green = CGFloat(greenMagentaDial.currentValue)
            let blue = CGFloat(blueYellowDial.currentValue)
            
            currentColor = UIColor(red: red, green: green, blue: blue, alpha: 1)
        }
        
        updateDialsOnColorChange = true

    }

You'll notice that each dial has a unique label. I've updated the NumericDial class with a labelFunction property of type(Double) -> (String), by default it is:

    class func defaultLabelFunction(value : Double) -> String
    {
        return NSString(format: "%.4f", value)

    }

But I'm labelling RGB colours with their hex value and CMYK colours as a percentage. I could hand craft a separate label function for each of the seven dials, but creating a function that creates these based on the CMYK mode is a far better solution. So, inside the RGBpicker's overridden didMoveToWindow() I set the label functions with this code:

        let redCyanLabel = cmykMode ? "Cyan" : "Red"
        let greenMagentaLabel = cmykMode ? "Magenta" : "Green"
        let blueYellowLabel = cmykMode ? "Yellow" : "Blue"
        
        redCyanDial.labelFunction = createLabelFunction("\(redCyanLabel): ")
        greenMagentaDial.labelFunction = createLabelFunction("\(greenMagentaLabel): ")
        blueYellowDial.labelFunction = createLabelFunction("\(blueYellowLabel): ")
        
        if cmykMode
        {
            blackDial?.labelFunction = createLabelFunction("Black: ")

        }

...with my funky createLabelFunction() dynamically creating and returning a function for each of the seven cases:

    func createLabelFunction(label : String) -> ((Double) -> String)
    {
        func rgbLabelFunction(value : Double) -> String
        {
            if (cmykMode)
            {
                return label + NSString(format: "%d", Int(value * 100)) + "%"
            }
            else
            {
                return label + NSString(format: "%2X", Int(value * 255))
            }
        }
        
        return rgbLabelFunction

    }

Back up in the ViewController, two instances of RGBpicker are created and added to the display - one with itscmykMode set to true. Both of these update the view controller's currentColor property and using a didSet observer update the currentColor of a big colour swatch and both colour pickers. 
The end result is as one colour picker is changed by the user, the swatch and the other picker are both updated.


All the source code is available in my GitHub repository.

Discover how the Watson team is further developing SDKs in Java, Node.js, Python, iOS, and Android to access these services and make programming easy. Brought to you in partnership with IBM.

Topics:

Published at DZone with permission of Simon Gladman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}