Swift 5.4 Hex to Nscolor

Swift 5.4 hex to NSColor

you could try something like this:

EDIT: included toHex(alpha:), from code I probably got from the net somewhere many years ago.

EDIT3,4: included the case for #RRGGBBAA

EDIT 5: stripping blank spaces in the hex string, to make NSColor (hex:" # 2196f380 ") work as well.

extension NSColor {

convenience init(hex: String) {
let trimHex = hex.trimmingCharacters(in: .whitespacesAndNewlines)
let dropHash = String(trimHex.dropFirst()).trimmingCharacters(in: .whitespacesAndNewlines)
let hexString = trimHex.starts(with: "#") ? dropHash : trimHex
let ui64 = UInt64(hexString, radix: 16)
let value = ui64 != nil ? Int(ui64!) : 0
// #RRGGBB
var components = (
R: CGFloat((value >> 16) & 0xff) / 255,
G: CGFloat((value >> 08) & 0xff) / 255,
B: CGFloat((value >> 00) & 0xff) / 255,
a: CGFloat(1)
)
if String(hexString).count == 8 {
// #RRGGBBAA
components = (
R: CGFloat((value >> 24) & 0xff) / 255,
G: CGFloat((value >> 16) & 0xff) / 255,
B: CGFloat((value >> 08) & 0xff) / 255,
a: CGFloat((value >> 00) & 0xff) / 255
)
}
self.init(red: components.R, green: components.G, blue: components.B, alpha: components.a)
}

func toHex(alpha: Bool = false) -> String? {
guard let components = cgColor.components, components.count >= 3 else {
return nil
}

let r = Float(components[0])
let g = Float(components[1])
let b = Float(components[2])
var a = Float(1.0)

if components.count >= 4 {
a = Float(components[3])
}

if alpha {
return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
} else {
return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
}
}
}

let nscol = NSColor(hex: "#2196f3") // <-- with or without #

EDIT2:

you can do the same for UIColor, and for Color (with UIColor or NSColor):

extension Color {
public init(hex: String) {
self.init(UIColor(hex: hex))
}

public func toHex(alpha: Bool = false) -> String? {
UIColor(self).toHex(alpha: alpha)
}
}

How to convert hex to NSColor?

Here is exact same thing with swift, however this uses UIColor rather than NSColor,

func colorWithHexColorString(var colorString: String) -> UIColor?
{
if colorString.hasPrefix("#") {
colorString = dropFirst(colorString)
}

var color: UIColor? = nil

var colorCode = UInt32()

var redByte:CGFloat = 255;
var greenByte:CGFloat = 255;
var blueByte: CGFloat = 255;

let scanner = NSScanner(string: colorString)
if scanner.scanHexInt(&colorCode) {
redByte = CGFloat(colorCode & 0xff0000)
greenByte = CGFloat(colorCode & 0x00ff00)
blueByte = CGFloat(colorCode & 0xff)
color = UIColor(red: redByte, green: greenByte, blue: blueByte, alpha: 1.0)
}

return color
}

colorWithHexColorString("#ffff00")
colorWithHexColorString("ff0000")

Convert Hex Color Code to NSColor

+ (NSColor*)colorWithHexColorString:(NSString*)inColorString
{
NSColor* result = nil;
unsigned colorCode = 0;
unsigned char redByte, greenByte, blueByte;

if (nil != inColorString)
{
NSScanner* scanner = [NSScanner scannerWithString:inColorString];
(void) [scanner scanHexInt:&colorCode]; // ignore error
}
redByte = (unsigned char)(colorCode >> 16);
greenByte = (unsigned char)(colorCode >> 8);
blueByte = (unsigned char)(colorCode); // masks off high bits

result = [NSColor
colorWithCalibratedRed:(CGFloat)redByte / 0xff
green:(CGFloat)greenByte / 0xff
blue:(CGFloat)blueByte / 0xff
alpha:1.0];
return result;
}

It doesn't take alpha values into account, it assumes values like "FFAABB", but it would be easy to modify.

How to use hex color values

#ffffff are actually 3 color components in hexadecimal notation - red ff, green ff and blue ff. You can write hexadecimal notation in Swift using 0x prefix, e.g 0xFF

To simplify the conversion, let's create an initializer that takes integer (0 - 255) values:

extension UIColor {
convenience init(red: Int, green: Int, blue: Int) {
assert(red >= 0 && red <= 255, "Invalid red component")
assert(green >= 0 && green <= 255, "Invalid green component")
assert(blue >= 0 && blue <= 255, "Invalid blue component")

self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)
}

convenience init(rgb: Int) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF
)
}
}

Usage:

let color = UIColor(red: 0xFF, green: 0xFF, blue: 0xFF)
let color2 = UIColor(rgb: 0xFFFFFF)

How to get alpha?

Depending on your use case, you can simply use the native UIColor.withAlphaComponent method, e.g.

let semitransparentBlack = UIColor(rgb: 0x000000).withAlphaComponent(0.5)

Or you can add an additional (optional) parameter to the above methods:

convenience init(red: Int, green: Int, blue: Int, a: CGFloat = 1.0) {
self.init(
red: CGFloat(red) / 255.0,
green: CGFloat(green) / 255.0,
blue: CGFloat(blue) / 255.0,
alpha: a
)
}

convenience init(rgb: Int, a: CGFloat = 1.0) {
self.init(
red: (rgb >> 16) & 0xFF,
green: (rgb >> 8) & 0xFF,
blue: rgb & 0xFF,
a: a
)
}

(we cannot name the parameter alpha because of a name collision with the existing initializer).

Called as:

let color = UIColor(red: 0xFF, green: 0xFF, blue: 0xFF, a: 0.5)
let color2 = UIColor(rgb: 0xFFFFFF, a: 0.5)

To get the alpha as an integer 0-255, we can

convenience init(red: Int, green: Int, blue: Int, a: Int = 0xFF) {
self.init(
red: CGFloat(red) / 255.0,
green: CGFloat(green) / 255.0,
blue: CGFloat(blue) / 255.0,
alpha: CGFloat(a) / 255.0
)
}

// let's suppose alpha is the first component (ARGB)
convenience init(argb: Int) {
self.init(
red: (argb >> 16) & 0xFF,
green: (argb >> 8) & 0xFF,
blue: argb & 0xFF,
a: (argb >> 24) & 0xFF
)
}

Called as

let color = UIColor(red: 0xFF, green: 0xFF, blue: 0xFF, a: 0xFF)
let color2 = UIColor(argb: 0xFFFFFFFF)

Or a combination of the previous methods. There is absolutely no need to use strings.

Swift macOS convert Color to NSColor

NSColor already have a Color init.
You can use it like

NSColor(Color("backgroundTheme")).withAlphaComponent(0.3)

Or you can get color by name
like this

NSColor(named: "backgroundTheme")?.withAlphaComponent(0.3)

NSColorPanel to hex issue?

You must have entered coordinates in different color space (probably device's, since on my Mac, #4272AA in color space of my display when converted to calibrated color space produces almost the same result, #345C9A).

To change color space in NSColorPanel, click tiny rainbow button. NSCalibratedRGBColorSpace corresponds to "Generic RGB" selection — since your get-hex method uses calibrated, you need to use the same if you want to get the same numbers back.

Sample Image

A tiny bit of warning: this piece of code from developer.apple.com is harmful.

  • When people say hex codes to each other, it is almost universally assumed to mean the same color as HTML/CSS would produce. This means hex codes must be in sRGB color space, since web standards dictate to use sRGB whenever color space information is omitted/missing.
  • Apple's "Generic RGB" (NSCalibratedRGBColorSpace) is very different from sRGB and native color spaces of most modern displays. Your problem just demonstrates how huge that difference is.
  • Most of displays in the world are manufactured to match sRGB as best as possible, and high quality displays of modern Apple's devices are particularly good at that.
  • This leads to somewhat surprising conclusion: if need those hex codes to produce same colors as in HTML, using NSDeviceRGBColorSpace, however wrong that is, instead of NSCalibratedRGBColorSpace gives much better results. You can easily verify that fact by entering the same color coordinates in Device, Generic and sRGB color spaces and comparing to what HTML page in Safari produces.
  • If you need correct and guaranteed match with hex codes used in Web, you will have to do color conversion to sRGB manually, since NSColor does not support reading components for any color profiles other than Device RGB and Calibrated RGB.

How to get a string from a CGColor MacOS

Here's a useful extension I made for doing this with CGColor. You can get the RGBA components, as well as convert them back.

Code:

extension CGColor {
/// Components of a color. Contains `rgba`.
struct Components: Codable {
let red: CGFloat
let green: CGFloat
let blue: CGFloat
let alpha: CGFloat

init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
self.red = red
self.green = green
self.blue = blue
self.alpha = alpha
}

init?(rgba: [CGFloat]) {
guard rgba.count == 4 else { return nil }

red = rgba[0]
green = rgba[1]
blue = rgba[2]
alpha = rgba[3]
}

var all: [CGFloat] {
[red, green, blue, alpha]
}
}

var components: Components? {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
_ = NSColor(cgColor: self)?.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let components = [red, green, blue, alpha]
return Components(rgba: components)
}

static func create(from components: Components) -> CGColor {
self.init(
red: components.red,
green: components.green,
blue: components.blue,
alpha: components.alpha
)
}
}

Usage:

if let components = CGColor(red: 1, green: 0.2, blue: 0.2, alpha: 1).components {
print(components)
let new = CGColor.create(from: components)
print(new)
}

Prints: Components(red: 1.0, green: 0.2, blue: 0.2, alpha: 1.0)

Prints: <CGColor 0x600003f1d500> [<CGColorSpace 0x600003f1ab80> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Generic RGB Profile)] ( 1 0.2 0.2 1 )



Related Topics



Leave a reply



Submit