Whilst the code below worked previously, it has stopped working in Xcode 8 Beta 4, presumably because the components
return was a very un-Swift-y C-array of floats,
This looks like a transitional beta issue.
The Swift repo on Github includes an extensive SDK overlay for CoreGraphics, including a new version of CGColor.components
whose return type is a Swift array instead of an UnsafePointer
. Part of how they make that SDK overlay work is API notes, which map some of the underlying C calls to double-underscore'd Swift methods so that the overlay can wrap them in a more-Swifty interface.
It looks like the beta 4 and beta 5 compilers picked up the API notes change, but not the overlay that includes the new version of components
. Presumably a future beta (or the final Swift 3.0 / Xcode 8.0 release) will include everything that's now on github.
I may be mistaking something, but you can find this in the imported header of CGColor.
/* Return the color components (including alpha) associated with `color'. */
@available(OSX 10.3, *)
public var __unsafeComponents: UnsafePointer<CGFloat>? { get }
Isn't this what you are looking for?
I can write something like this:
public var cgColour: CGColor {
get {
return CGColor(red: self.colourRed, green: self.colourGreen, blue: self.colourBlue, alpha: self.colourAlpha)
}
set {
if let comps = newValue.__unsafeComponents, newValue.numberOfComponents == 4 {
self.colourRed = comps[0]
self.colourGreen = comps[1]
self.colourBlue = comps[2]
self.colourAlpha = comps[3]
}
}
}
It works as I expect, but I'm not sure it's as you expect, or Apple would treat this as using a private API. (Apple's latest documentation of CGColor does not contain double-underscore leaded symbols.)
extension UIColor {
var all4Components:(red:CGFloat, green:CGFloat, blue: CGFloat, alpha:CGFloat) {
let components = self.cgColor.components!
let red = components[0]
let green = components[1]
let blue = components[2]
let alpha = components[3]
return (red:red, green:green, blue: blue, alpha:alpha)
}
}
I'm also a little perplexed as to why they have removed the components
property from CGColor
, as there doesn't seem to be any kind of obvious replacement method/property. It looks like they're trying to get people to use the higher level (As @rickster has discovered, this looks more like a simple transitional issue).UIColor
or NSColor
classes instead.
One solution, as you're working with RGB colors, would be to simply wrap your CGColor
in a UIColor
/NSColor
, and then use the getRed(_:green:blue:alpha:) method to get out the components instead.
public var cgColour : CGColor {
get {
return CGColor(red: colourRed, green: colourGreen, blue: colourBlue, alpha: colourAlpha)
}
set {
NSColor(cgColor: newValue)?.getRed(&colourRed, green: &colourGreen, blue: &colourBlue, alpha: &colourAlpha)
}
}
Perhaps not the most ideal solution – would certainly be interested to know if anyone else has a better one, or knows more about this change. Depending on the usage of this property, you may also want to consider simply making it of type UIColor
/NSColor
to prevent the needless wrapping that this solution requires.
@Hamish's answer works, but my original intent was not to use UIColor
or NSColor
so that my code works in both iOS & MacOS. @LeoDabus suggested using SKColor
, but that's just a type alias to either NSColor
or UIColor
, and doesn't have a direct init from CGColor
anyway, however, Leo's suggestion prompted me to refine my kludge using CIColor
instead:
import CoreImage
extension CGColor {
var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
let ciColor = CIColor(cgColor: self)
return (ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha)
}
}