在 UIKit 中,我们可以使用扩展为几乎所有内容设置十六进制颜色,就像在这个教程中一样。但是当我尝试在 SwiftUI 中执行此操作时,这是不可能的,看起来 SwiftUI 没有获取
UIColor
作为参数。
Text(text)
.color(UIColor.init(hex: "FFF"))
错误信息:
Cannot convert value of type 'UIColor' to expected argument type 'Color?'
我什至尝试延长
Color
,而不是UIColor
,但我没有任何运气。
我的
Color
扩展:
import SwiftUI
extension Color {
init(hex: String) {
let scanner = Scanner(string: hex)
scanner.scanLocation = 0
var rgbValue: UInt64 = 0
scanner.scanHexInt64(&rgbValue)
let r = (rgbValue & 0xff0000) >> 16
let g = (rgbValue & 0xff00) >> 8
let b = rgbValue & 0xff
self.init(
red: CGFloat(r) / 0xff,
green: CGFloat(g) / 0xff,
blue: CGFloat(b) / 0xff, alpha: 1
)
}
}
错误信息:
Incorrect argument labels in call (have 'red:green:blue:alpha:', expected '_:red:green:blue:opacity:')
你就快到了,你使用了错误的初始化参数:
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
}
下面的另一种选择使用 Int 表示十六进制,但当然,如果您愿意,可以将其更改为 String。
extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 08) & 0xff) / 255,
blue: Double((hex >> 00) & 0xff) / 255,
opacity: alpha
)
}
}
使用示例:
Color(hex: 0x000000)
Color(hex: 0x000000, alpha: 0.2)
试试这个:
extension Color {
init(hex: Int, opacity: Double = 1.0) {
let red = Double((hex & 0xff0000) >> 16) / 255.0
let green = Double((hex & 0xff00) >> 8) / 255.0
let blue = Double((hex & 0xff) >> 0) / 255.0
self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity)
}
}
用途:
Text("Hello World!")
.background(Color(hex: 0xf5bc53))
Text("Hello World!")
.background(Color(hex: 0xf5bc53, opacity: 0.8))
这是一个包含我的解决方案的游乐场。它在后备之后添加了后备,并且仅依赖于颜色和 alpha 的 hexString。
import SwiftUI
extension Color {
init(hex string: String) {
var string: String = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if string.hasPrefix("#") {
_ = string.removeFirst()
}
// Double the last value if incomplete hex
if !string.count.isMultiple(of: 2), let last = string.last {
string.append(last)
}
// Fix invalid values
if string.count > 8 {
string = String(string.prefix(8))
}
// Scanner creation
let scanner = Scanner(string: string)
var color: UInt64 = 0
scanner.scanHexInt64(&color)
if string.count == 2 {
let mask = 0xFF
let g = Int(color) & mask
let gray = Double(g) / 255.0
self.init(.sRGB, red: gray, green: gray, blue: gray, opacity: 1)
} else if string.count == 4 {
let mask = 0x00FF
let g = Int(color >> 8) & mask
let a = Int(color) & mask
let gray = Double(g) / 255.0
let alpha = Double(a) / 255.0
self.init(.sRGB, red: gray, green: gray, blue: gray, opacity: alpha)
} else if string.count == 6 {
let mask = 0x0000FF
let r = Int(color >> 16) & mask
let g = Int(color >> 8) & mask
let b = Int(color) & mask
let red = Double(r) / 255.0
let green = Double(g) / 255.0
let blue = Double(b) / 255.0
self.init(.sRGB, red: red, green: green, blue: blue, opacity: 1)
} else if string.count == 8 {
let mask = 0x000000FF
let r = Int(color >> 24) & mask
let g = Int(color >> 16) & mask
let b = Int(color >> 8) & mask
let a = Int(color) & mask
let red = Double(r) / 255.0
let green = Double(g) / 255.0
let blue = Double(b) / 255.0
let alpha = Double(a) / 255.0
self.init(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
} else {
self.init(.sRGB, red: 1, green: 1, blue: 1, opacity: 1)
}
}
}
let gray0 = Color(hex: "3f")
let gray1 = Color(hex: "#69")
let gray2 = Color(hex: "#6911")
let gray3 = Color(hex: "fff")
let red = Color(hex: "#FF000044s")
let green = Color(hex: "#00FF00")
let blue0 = Color(hex: "0000FF")
let blue1 = Color(hex: "0000F")
从 Color 获取十六进制字符串.. 好吧,这不是公共 API。 我们仍然需要依赖 UIColor 实现。
PS:我看到了下面的组件解决方案..但是如果将来API发生变化,我的版本会更稳定一点。
extension Color {
init(_ hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xFF) / 255,
green: Double((hex >> 8) & 0xFF) / 255,
blue: Double(hex & 0xFF) / 255,
opacity: alpha
)
}
}
然后,你可以像这样使用它:
let red = Color(0xFF0000)
let green = Color(0x00FF00)
let translucentMagenta = Color(0xFF00FF, alpha: 0.4)
第二个扩展允许从十六进制字符串构建颜色,涵盖大多数已知的格式。它允许:
指定带或不带前导 # 的颜色。 灰色阴影的 2 位数字格式。 3 位数字格式为速记 6 位数字格式。 带 alpha 的灰色的 4 位数字格式。 RGB 的 6 位格式。 RGBA 的 8 位格式。 对于所有无效格式自动返回 nil。
extension Color {
init?(_ hex: String) {
var str = hex
if str.hasPrefix("#") {
str.removeFirst()
}
if str.count == 3 {
str = String(repeating: str[str.startIndex], count: 2)
+ String(repeating: str[str.index(str.startIndex, offsetBy: 1)], count: 2)
+ String(repeating: str[str.index(str.startIndex, offsetBy: 2)], count: 2)
} else if !str.count.isMultiple(of: 2) || str.count > 8 {
return nil
}
let scanner = Scanner(string: str)
var color: UInt64 = 0
scanner.scanHexInt64(&color)
if str.count == 2 {
let gray = Double(Int(color) & 0xFF) / 255
self.init(.sRGB, red: gray, green: gray, blue: gray, opacity: 1)
} else if str.count == 4 {
let gray = Double(Int(color >> 8) & 0x00FF) / 255
let alpha = Double(Int(color) & 0x00FF) / 255
self.init(.sRGB, red: gray, green: gray, blue: gray, opacity: alpha)
} else if str.count == 6 {
let red = Double(Int(color >> 16) & 0x0000FF) / 255
let green = Double(Int(color >> 8) & 0x0000FF) / 255
let blue = Double(Int(color) & 0x0000FF) / 255
self.init(.sRGB, red: red, green: green, blue: blue, opacity: 1)
} else if str.count == 8 {
let red = Double(Int(color >> 24) & 0x000000FF) / 255
let green = Double(Int(color >> 16) & 0x000000FF) / 255
let blue = Double(Int(color >> 8) & 0x000000FF) / 255
let alpha = Double(Int(color) & 0x000000FF) / 255
self.init(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
} else {
return nil
}
}
}
这里有一些示例颜色,演示了所有支持的格式:
let gray1 = Color("4f")
let gray2 = Color("#68")
let gray3 = Color("7813")
let red = Color("f00")
let translucentGreen = Color("#00FF0066")
let blue = Color("0000FF")
let invalid = Color("0000F")
祝你好运;)
我还使用了 hackingwithswift 的
UIColor
解决方案。
这是Color
的改编版本:
init?(hex: String) {
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
var rgb: UInt64 = 0
var red: Double = 0.0
var green: Double = 0.0
var blue: Double = 0.0
var opacity: Double = 1.0
let length = hexSanitized.count
guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil }
if length == 6 {
red = Double((rgb & 0xFF0000) >> 16) / 255.0
green = Double((rgb & 0x00FF00) >> 8) / 255.0
blue = Double(rgb & 0x0000FF) / 255.0
} else if length == 8 {
red = Double((rgb & 0xFF000000) >> 24) / 255.0
green = Double((rgb & 0x00FF0000) >> 16) / 255.0
blue = Double((rgb & 0x0000FF00) >> 8) / 255.0
opacity = Double(rgb & 0x000000FF) / 255.0
} else {
return nil
}
self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity)
}
对于此任务,我们必须使用按位右移
>>
运算符和按位 AND &
运算符。十六进制模式中的每个颜色通道使用 8 位,或从 0 到 255 的十进制值,或从 0x00 到 0xFF 的十六进制值。
这里使用位运算符来有效地分解十六进制值,方法是分别将颜色的红色分量向后移动 2 个字节(16 位),将绿色分量向后移动 1 个字节。让我们看看它在 Xcode 的 Live View 中是如何工作的。
0xFF == 0x0000FF // blue
0xFF00 == 0x00FF00 // green
0xFF0077 == 0xFF0077 // pink
这是代码:
import SwiftUI
extension Color {
init(_ hexColor: UInt32) {
self.init(uiColor: .init(
red: CGFloat(0xFF & (hexColor >> 0x10)) / 0xFF,
green: CGFloat(0xFF & (hexColor >> 0x08)) / 0xFF,
blue: CGFloat(0xFF & (hexColor >> 0x00)) / 0xFF,
alpha: 1.0))
}
}
struct ContentView: View {
@State private var hexColor: UInt32 = 0xFF0077 // Deep Pink HEX color
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
Rectangle()
.frame(width: 300, height: 300)
.foregroundColor(.init(hexColor))
}
}
}
使用方法
UIColor.init(hex: "f2000000")
UIColor.init(hex: "#f2000000")
UIColor.init(hex: "000000")
UIColor.init(hex: "#000000")
extension UIColor {
public convenience init(hex:String) {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 1.0
var rgbValue:UInt64 = 0
Scanner(string: cString).scanHexInt64(&rgbValue)
if ((cString.count) == 8) {
r = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
g = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
b = CGFloat((rgbValue & 0x0000FF)) / 255.0
a = CGFloat((rgbValue & 0xFF000000) >> 24) / 255.0
}else if ((cString.count) == 6){
r = CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0
g = CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0
b = CGFloat((rgbValue & 0x0000FF)) / 255.0
a = CGFloat(1.0)
}
self.init( red: r,
green: g,
blue: b,
alpha: a
)
} }
您可以创建扩展为
导入 SwiftUI
extension Color {
init(hex: UInt, alpha: Double = 1) {
self.init(
.sRGB,
red: Double((hex >> 16) & 0xff) / 255,
green: Double((hex >> 08) & 0xff) / 255,
blue: Double((hex >> 00) & 0xff) / 255,
opacity: alpha
)
}
}
如何使用
Text("In order to write about life first you must live it")
.foregroundColor(Color(hex: 0x969696))
在 6 位十六进制值之前使用 0x
用途:
Color(hex: "#FFFFFF") // hashtag + uppercase value
Color(hex: "#ffffff") // hashtag + lowercase value
Color(hex: "FFFFFF") // without hashtag
Color(hex: "FFFFFF", alpha: 0.2) // color value + alpha value
Color(hex: "#0080FF80") // long color & alpha value
我的解决方案是基于UIColor的代码,它在生产环境中表现良好。 原代码在github上
extension Color {
init(hex: String?, alpha: CGFloat? = nil) {
let normalizedHexString: String = Color.normalize(hex)
var ccc: CUnsignedLongLong = 0
Scanner(string: normalizedHexString).scanHexInt64(&ccc)
var resultAlpha: CGFloat {
switch alpha {
case nil: return ColorMasks.alphaValue(ccc)
default: return alpha!
}
}
self.init(CGColor(red: ColorMasks.redValue(ccc),
green: ColorMasks.greenValue(ccc),
blue: ColorMasks.blueValue(ccc),
alpha: resultAlpha))
}
func hexDescription(_ includeAlpha: Bool = false) -> String {
guard let cgColor = self.cgColor else {
return "Problem with cgColor"
}
guard cgColor.numberOfComponents == 4 else {
return "Color not RGB."
}
guard let components = cgColor.components else {
return "Problem with cgColor.components"
}
let aaa = components.map({ Int($0 * CGFloat(255)) })
let color = String.init(format: "%02x%02x%02x", aaa[0], aaa[1], aaa[2])
if includeAlpha {
let alpha = String.init(format: "%02x", aaa[3])
return "\(color)\(alpha)"
}
return color
}
fileprivate enum ColorMasks: CUnsignedLongLong {
case redMask = 0xff000000
case greenMask = 0x00ff0000
case blueMask = 0x0000ff00
case alphaMask = 0x000000ff
static func redValue(_ value: CUnsignedLongLong) -> CGFloat {
return CGFloat((value & redMask.rawValue) >> 24) / 255.0
}
static func greenValue(_ value: CUnsignedLongLong) -> CGFloat {
return CGFloat((value & greenMask.rawValue) >> 16) / 255.0
}
static func blueValue(_ value: CUnsignedLongLong) -> CGFloat {
return CGFloat((value & blueMask.rawValue) >> 8) / 255.0
}
static func alphaValue(_ value: CUnsignedLongLong) -> CGFloat {
return CGFloat(value & alphaMask.rawValue) / 255.0
}
}
fileprivate static func normalize(_ hex: String?) -> String {
guard var hexString = hex else {
return "00000000"
}
if hexString.hasPrefix("#") {
hexString = String(hexString.dropFirst())
}
if hexString.count == 3 || hexString.count == 4 {
hexString = hexString.map { "\($0)\($0)" } .joined()
}
let hasAlpha = hexString.count > 7
if !hasAlpha {
hexString += "ff"
}
return hexString
}
}
您可以使用此扩展程序
UIColor
extension UIColor {
convenience init(hexaString: String, alpha: CGFloat = 1) {
let chars = Array(hexaString.dropFirst())
self.init(red: .init(strtoul(String(chars[0...1]),nil,16))/255,
green: .init(strtoul(String(chars[2...3]),nil,16))/255,
blue: .init(strtoul(String(chars[4...5]),nil,16))/255,
alpha: alpha)}
}
Usage Example:
let lightGoldColor = UIColor(hexaString: "#D6CDB2")
Test Code:
从十六进制(3、4、6、8 个字符)创建 SwiftUI 颜色,支持
#
、alpha
、web constants
和 UIColor constants
。下面是使用示例。
Color
十六进制、随机、CSS 颜色和用户默认值的支持。