Xcode 8 Beta 4 CGColor.components unavailable

2019-02-25 04:39发布

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, and has been removed. The error is bald - "'components' is unavailable" - and I can't find what has replaced it, if anything. Does anyone know how to produce the same functionality?

public var cgColour: CGColor {
    get {
        return CGColor(red: self.colourRed, green: self.colourGreen, blue: self.colourBlue, alpha: self.colourAlpha)
    }
    set {
        let comps = newValue.components // No longer available
        self.colourRed = (comps?[0])!
        self.colourGreen = (comps?[1])!
        self.colourBlue = (comps?[2])!
        self.colourAlpha = (comps?[3])!
    }
}

Update @Hamish's answer works, but my original intent was not to use UIColor nor NSColor so that my code works in both iOS & MacOS. What I've ended up doing is this...

#if os(iOS)
import UIKit
#elseif os(OSX)
import Cocoa
#endif

extension CGColor {
    var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var r: CGFloat = 0
        var g: CGFloat = 0
        var b: CGFloat = 0
        var a: CGFloat = 0
        #if os(iOS)
            UIColor(cgColor: self).getRed(&r, green: &g, blue: &b, alpha: &a)
        #elseif os(OSX)
            NSColor(cgColor: self)?.getRed(&r, green: &g, blue: &b, alpha: &a)
        #endif
        return (r, g, b, a)
    }
}
// Playground code to test...
#if os(iOS)
let rgba = UIColor.brown.cgColor.components //(0.6, 0.4, 0.2, 1.0)
#elseif os(OSX)
let rgba = NSColor.brown.cgColor.components //(0.6, 0.4, 0.2, 1.0)
#endif

... this is such a kludge - has anyone got a better answer?

5条回答
家丑人穷心不美
2楼-- · 2019-02-25 05:01

@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)
    }
}
查看更多
我想做一个坏孩纸
3楼-- · 2019-02-25 05:06

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.

查看更多
ゆ 、 Hurt°
4楼-- · 2019-02-25 05:09
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)

    }
}
查看更多
混吃等死
5楼-- · 2019-02-25 05:13

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 UIColor or NSColor classes instead. (As @rickster has discovered, this looks more like a simple transitional issue).

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.

查看更多
Ridiculous、
6楼-- · 2019-02-25 05:19

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.)

查看更多
登录 后发表回答