I have custom colors within my code. I use them several times and I would like to have them allocated only once.
The situation / problem
If we get a look at UIColor headers we can see the following :
[...]
// Some convenience methods to create colors. These colors will be as calibrated as possible.
// These colors are cached.
open class var black: UIColor { get } // 0.0 white
open class var darkGray: UIColor { get } // 0.333 white
[...]
I've created an extension
of UIColor, like so :
import UIKit
extension UIColor {
class func colorWithHexString(_ hex: String) -> UIColor {
print("\(#function): \(hex)")
// some code, then it return a UIColor
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
// Option A
open class var myColorOne : UIColor {
get {
return colorWithHexString("AABBCC")
}
}
// Option B
class func myColorTwo() -> UIColor {
return colorWithHexString("DDEEFF")
}
}
From there I can use my colors easily, with either having a variable or a function.
// A
UIColor.myColorOne
// B
UIColor.myColorTwo()
Sadly, I'm not fully happy with that. Indeed, every time I want to use those colors : a new UIColor allocation is made.
What I've tried
Apple managed to make their color cached apparently. I would like to do so myself too. I've tried several things but none seems to be ideal.
1 - Using dispatch_once ✗
As visible on Swift page : the free function dispatch_once
is no longer available in Swift.
2 - Creating a constant (let) ✗
I get the following error : extensions may not contain stored properties
3 - Creating a singleton ~
It does work (each color are created only once) with the following
import UIKit
class Colors : UIColor {
// Singleton
static let sharedInstance = Colors()
let myColorOne : UIColor = {
return UIColor.colorWithHexString("AABBCC")
}()
let myColorTwo : UIColor = {
return UIColor.colorWithHexString("DDEEFF")
}()
}
But it forces me to have one more file and call my colors like so
Colors.sharedInstance.myColorOne
Isn't there any way to get the colors I want like that UIColor.myColorOne
and have them cached like Apple does ?
You can use the same approach as in
Using a dispatch_once singleton model in Swift, i.e. static
constant stored properties
which are initialized lazily (and only once). These can be defined
directly in the UIColor
extension:
extension UIColor {
convenience init(hex: String) {
// ...
}
static let myColorOne = UIColor(hex:"AABBCC")
static let myColorTwo = UIColor(hex:"DDEEFF")
}
There might be a better way to do it, but using a global variable (like Grimxn mentioned in the comment) is one way to solve the problem.
Below is an example you can copy & paste into a playground:
import UIKit
extension UIColor {
class func colorWithHexString(_ hex: String) -> UIColor {
print("allocate color")
// do you conversion here...
return UIColor.black
}
}
private let myPrivateColorOne = UIColor.colorWithHexString("#ffffff")
extension UIColor {
open class var myColorOne: UIColor {
get {
print("get color")
return myPrivateColorOne
}
}
}
UIColor.myColorOne
UIColor.myColorOne
When you execute the code, the getter will be called twice, but colorWithHexString
only once.
You could use your singleton to store the generated UIColor values as above and solve the Colors.sharedInstance.myColorOne naming problem by extending UIColor and putting the access in there:
extension UIColor {
class func myColorTwo() -> UIColor {
return Colors.sharedInstance.myColorTwo
}
}
In Swift 3:
extension UIColor {
class func color(hexString: String) -> UIColor {
// ...
}
static let myColorOne = color(hexString: "AABBCC")
}
来源:https://stackoverflow.com/questions/42123954/how-to-create-several-cached-uicolor