Can UIColor be initialised with both dark and light mode color? - swift

I am using random pastel colours in my training app (bouncing circles), but when my iPad is in dark mode, the pastels looks very bland. Can I somehow initialise UIColor with two colours? Maybe something like:
UIColor(forLightMode: UIColor, forDarkMode: UIColor)
My current app is creating new colours properly, but they do not change automatically, when I change from dark to light mode. The app is just starting to generate new ones from pastel set.
func makeRandomColor() -> UIColor {
let fullRange : ClosedRange<CGFloat> = 0...255
let pastelRange : ClosedRange<CGFloat> = 127...255
let randomPastelRed = CGFloat.random(in: pastelRange) / 255
let randomPastelGreen = CGFloat.random(in: pastelRange) / 255
let randomPastelBlue = CGFloat.random(in: pastelRange) / 255
let randomRed = CGFloat.random(in: fullRange) / 255
let randomGreen = CGFloat.random(in: fullRange) / 255
let randomBlue = CGFloat.random(in: fullRange) / 255
let randomAlpha = CGFloat.random(in: 0.6...1)
return UIColor.init(dynamicProvider: { traitCollection in
if traitCollection.userInterfaceStyle == .dark {
return UIColor(
red: randomRed,
green: randomGreen,
blue: randomBlue,
alpha: randomAlpha
)
} else {
return UIColor(
red: randomPastelRed,
green: randomPastelGreen,
blue: randomPastelBlue,
alpha: randomAlpha
)
}
})
}

You'd need to listen to traitCollectionDidChange notification and redraw your UI either by calling setNeedsLayout() or similar methods from your UIView or UIViewController. For more Information check Implementing Dark Mode on iOS ~22min

You can do something like this:
var dynamicColor = UIColor {(traitCollection: UITraitCollection) -> UIColor in
if traitCollection.userInterfaceStyle == .dark {
return .white
} else {
return .black
}
}
Taken from a Screencast course on Ray Wenderlich (I'm not affiliated with the website): https://www.raywenderlich.com/3979883-dark-mode-deep-dive

Related

black color in function always, why?

I want my text to get random color, for that I made a function but I do get always black color, this function should return a random color for me.
struct ContentView: View {
#State var colorOfText = randomColorFunction()
var body: some View {
Text("Hello, world!").font(Font.largeTitle.bold()).foregroundColor(colorOfText)
}
}
func randomColorFunction() -> Color {
let redValue = UInt8.random(in: 0...255)
let greenValue = UInt8.random(in: 0...255)
let blueValue = UInt8.random(in: 0...255)
print(redValue.description, greenValue.description, blueValue.description)
return Color(red: Double(redValue/255), green: Double(greenValue/255), blue: Double(blueValue/255))
}
You need to change the return of randomColorFunction to this:
return Color(red: Double(redValue)/255.0, green: Double(greenValue)/255.0, blue: Double(blueValue)/255.0)
your solution did not work, because the result of the division always return Int and it causes to be 0 or 1 that means black color or white color
Your code is creating UInt8 values, and dividing them by the int value of 255. The result will always be an integer of 0 or 1. (and the odds are it will be 0 about 255 out of 256 times.)
You need to either use Double.random(in: 0.0...1.0) as suggested by Vadian, or cast your values to doubles:
let redValue = Double(Int.random(in: 0...255))
let greenValue = Double(Int.random(in: 0...255))
let blueValue = Double(Int.random(in: 0...255))
print(redValue.description, greenValue.description, blueValue.description)
return Color(
red: Double(redValue/255.0),
green: Double(greenValue/255.0),
blue: Double(blueValue/255.0))

How to convert a UIColor to a black and white UIColor

I am setting the background color of my label, but I would like to have the color be the black and white UIColor instead of the original UIColor.
self.MyLabel.backgroundColor = self.selectedColors.color
Looks like you'll need to convert your colour to grayscale.
While you can do this by averaging the R, G and B components of the colour, apple actually provide a nice method to grab the grayscale value:
func getWhite(_ white: UnsafeMutablePointer<CGFloat>?,
alpha: UnsafeMutablePointer<CGFloat>?) -> Bool
So to use this, you would first extract the grayscale colour and then init a new UIColor:
let originalColor = self.selectedColors.color
var white: CGFloat = 0
var alpha: CGFloat = 0
guard originalColor.getWhite(&white, alpha: &alpha) else {
// The color couldn't be converted! Handle this unexpected error
return
}
let newColor = UIColor(white: white, alpha: alpha)
self.MyLabel.backgroundColor = newColor
By thanks of #Sam answer, I write an extension for UIColor:
extension UIColor {
var grayScale: UIColor? {
var white: CGFloat = 0
var alpha: CGFloat = 0
guard self.getWhite(&white, alpha: &alpha) else {
return nil
}
return UIColor(white: white, alpha: alpha)
}
}
You can use it like this:
var grayScaleColorOfRed = UIColor.red.grayScale ?? UIColor.grey

How to convert UIColor to SwiftUI‘s Color

I want to use a UIColor as foregroundcolor for an object but I don’t know how to convert UIColor to a Color
var myColor: UIColor
RoundedRectangle(cornerRadius: 5).foregroundColor(UIColor(myColor))
Starting with beta 5, you can create a Color from a UIColor:
Color(UIColor.systemBlue)
Both iOS and macOS
Color has a native initializer for it that takes an UIColor or NSColor as an argument:
Color(.red) /* or any other UIColor/NSColor you need INSIDE parentheses */
DO NOT call Color(UIColor.red) explicitly !!!. This will couple your SwiftUI code to the UIKit. Instead, just call Color(.red) That will infer the correct module automatically.
Also, Note this difference:
Color.red /* this `red` is SwiftUI native `red` */
Color(.red) /* this `red` is UIKit `red` */
Note that:
Color.red and UIColor.red are NOT same! They have different values and look different with each other. So DON'T assume this worth nothing
These are equal instead: SwiftUI.Color.Red == UIKit.UIColor.systemRed
Also, You can check out How to get RGB components from SwiftUI.Color
Extension
You can implement a custom variable for it to make it more like cgColor and ciColor
extension UIColor {
/// The SwiftUI color associated with the receiver.
var suColor: Color { Color(self) }
}
so it would be like:
UIColor.red // UIKit color
UIColor.red.suColor // SwiftUI color
UIColor.red.cgColor // Core graphic color
UIColor.red.ciColor // Core image color
Note: Click here to see How to convert SwiftUI.Color to UIColor
Using two helper extensions:
To extract components from UIColor:
extension UIColor {
var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return (red, green, blue, alpha)
}
}
To init with UIColor:
extension Color {
init(uiColor: UIColor) {
self.init(red: Double(uiColor.rgba.red),
green: Double(uiColor.rgba.green),
blue: Double(uiColor.rgba.blue),
opacity: Double(uiColor.rgba.alpha))
}
}
Usage:
Color(uiColor: .red)
In Swift UI custom colors with a convenient extension:
extension UIColor {
struct purple {
static let normal = UIColor(red:0.043, green:0.576 ,blue:0.588 , alpha:1.00)
static let light = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
static let dark = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
}
struct gray {
static let normal = UIColor(red:0.5, green:0.5 ,blue:0.5 , alpha:1.00)
static let dark = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
}
}
Wrapping Color of SwiftUI:
extension UIColor {
var toSUIColor: Color {
Color(self)
}
}
and using this:
var body: some View {
Text("Hello World")
.foregroundColor(Color(UIColor.purple.normal))
.background(Color(UIColor.gray.normal))
// with wrap
//.foregroundColor(UIColor.purple.normal.toSUIColor)
//.background(UIColor.gray.normal.toSUIColor)
}
I'm a really old hobbyist. Here is one way that works for me. Yes, I do use globals for reusable statements in a Constant.swift file. This example is inline so that it is easier to see. I do not say this is the way to go, it is just my old way.
Screenshot (27k)
import SwiftUI
// named color from the developer's pallet
let aluminumColor = Color(UIColor.lightGray)
// a custom color I use often
let butterColor = Color.init(red: 0.9993399978,
green: 0.9350042167,
blue: 0.5304131241)
// how I use this in a SwiftUI VStack:
Text("Fur People")
.font(.title)
.foregroundColor(aluminumColor)
.shadow(color: butterColor, radius: 4, x: 2, y: 2)
.minimumScaleFactor(0.75)
Create an extension like this:
extension Color {
static let someColor = Color(UIColor.systemIndigo) // << Select any UIColor
}
Usage:
struct ContentView: View {
var body: some View {
Rectangle()
.frame(width: 100, height: 100)
.foregroundColor(.someColor)
}
}

Swift Random Color Generator

Is it possible to change the color a button with a label using a random generator?
For ex.. Label "A" is the title/color dictator and will notify "Button 1 and Button 2" what colors they will generate to be randomly.
let randomRed:CGFloat = CGFloat(arc4random_uniform(256))
let randomGreen:CGFloat = CGFloat(arc4random_uniform(256))
let randomBlue:CGFloat = CGFloat(arc4random_uniform(256))
let myColor = UIColor(red: randomRed/255, green: randomGreen/255, blue: randomBlue/255, alpha: 1.0)
Try this code:
Answer 1: Generate random colour from array(restrict to 3 colour)
Note: you have to set initial backgroundColor to your button in viewDidLoad
yourButtonName.backgroundColor = .red // set any colour
//Button background array
let buttonBG = [UIColor.red,UIColor.green,UIColor.black]
//Button title colour array
let buttonTitle = [UIColor.orange,UIColor.cyan,UIColor.yellow]
Usage: Try below code inside your button action.It will generate randomColor assigned from array...
let BGRandomIndex = Int(arc4random_uniform(UInt32(buttonTitle.count)))
yourButtonName.tintColor = buttonTitle[BGRandomIndex]
let TitleRandomIndex = Int(arc4random_uniform(UInt32(buttonBG.count)))
yourButtonName.backgroundColor = buttonBG[TitleRandomIndex]
Answer 2: Generate Random Colour.
func randomCGFloat() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UInt32.max)
}
func randomColor() -> UIColor {
let r = randomCGFloat()
let g = randomCGFloat()
let b = randomCGFloat()
// If you wanted a random alpha, just create another
// random number for that too.
return UIColor(red: r, green: g, blue: b, alpha: 1)
}
Usage: Try below code inside your button action.It will generate randomColor on every button click...
yourButtonName.backgroundColor = randomColor() // to get random background button backgroundColor
yourButtonName.tintColor = randomColor() //to get random background button title color
public func getRandomColor() -> UIColor{
let randomRed:CGFloat = CGFloat(drand48())
let randomGreen:CGFloat = CGFloat(drand48())
let randomBlue:CGFloat = CGFloat(drand48())
return UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
}
You're going to need a function to produce random CGFloats in the range 0 to 1:
you can use
CGFloat(arc4random()) / CGFloat(UInt32.max)
alternate for drand48()
If you want a alpha as a random, just create another random number for that
Just create a random background for your view like
self.view.backgroundColor = UIColor().getRandomColor()

Computing complementary, triadic, tetradic, and analagous colors

I have created swift functions, where I send color value to and want to return triadic and tetrads values. It sort of works, but I am not happy about the color results. Can anyone help me to fine-tune the formula please?
I was following few sources, but the returned colours were too bright or saturated in comparison to several online web based color schemes. I know it's a matter of preference as well and I kinda like the results of the code below, but in some instances of colors the result of one color returned is way too close to the original one, so it's barely visible. It applies only to a few colors...
I was using the formula from here:
my code:
func getTriadColor(color: UIColor) -> (UIColor, UIColor){
var hue : CGFloat = 0
var saturation : CGFloat = 0
var brightness : CGFloat = 0
var alpha : CGFloat = 0
let triadHue = CGFloat(color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha))
let triadColor1 = UIColor(hue: (triadHue + 0.33) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let triadColor2 = UIColor(hue: (triadHue + 0.66) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
return (triadColor1, triadColor2)
}
func getTetradColor(color: UIColor) -> (UIColor, UIColor, UIColor){
var hue : CGFloat = 0
var saturation : CGFloat = 0
var brightness : CGFloat = 0
var alpha : CGFloat = 0
let tetradHue = CGFloat(color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha))
let tetradColor1 = UIColor(hue: (tetradHue + 0.25) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let tetradColor2 = UIColor(hue: (tetradHue + 0.5) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
let tetradColor3 = UIColor(hue: (tetradHue + 0.75) - 1.0, saturation: saturation, brightness: brightness, alpha: alpha)
return (tetradColor1, tetradColor2, tetradColor3)
}
And I also found nice clean code for finding complementary color, which I am very happy about the results
func getComplementColor(color: UIColor) -> UIColor{
let ciColor = CIColor(color: color)
let compRed: CGFloat = 1.0 - ciColor.red
let compGreen: CGFloat = 1.0 - ciColor.green
let compBlue: CGFloat = 1.0 - ciColor.blue
return UIColor(red: compRed, green: compGreen, blue: compBlue, alpha: 1.0)
}
Your screen shot is of this web page. (Wayback Machine link because, six years later, the page has been deleted.) The formulas on that page are incorrect, because they specify the use of the absolute value function instead of the modulo function. That is, for example, your screen shot defines
H1 = |(H0 + 180°) - 360°|
but consider what this gives for the input H0 = 90°:
H1 = |(90° + 180°) - 360°| = |270° - 360°| = |-90°| = 90°
Do you think that the complementary hue of H0 = 90° is H1 = 90°, the same hue?
The correct formula is
H1 = (H0 + 180°) mod 360°
where “mod” is short for “modulo” and means “the remainder after dividing by”. In other words, if the answer would be above 360°, subtract 360°. For H0 = 90°, this gives the correct answer of H1 = 270°.
But you don't even have this problem in your code, because you didn't use the absolute value function (or the modulo function) in your code. Since you're not doing anything to keep your hue values in the range 0…1, your hue values that are less than zero are clipped to zero, and your hue values above one are clipped to one (and both zero and one mean red).
Your getComplementColor is also not at all the standard definition of the “complementary color”.
Here are the correct definitions:
extension UIColor {
var complement: UIColor {
return self.withHueOffset(0.5)
}
var splitComplement0: UIColor {
return self.withHueOffset(150 / 360)
}
var splitComplement1: UIColor {
return self.withHueOffset(210 / 360)
}
var triadic0: UIColor {
return self.withHueOffset(120 / 360)
}
var triadic1: UIColor {
return self.withHueOffset(240 / 360)
}
var tetradic0: UIColor {
return self.withHueOffset(0.25)
}
var tetradic1: UIColor {
return self.complement
}
var tetradic2: UIColor {
return self.withHueOffset(0.75)
}
var analagous0: UIColor {
return self.withHueOffset(-1 / 12)
}
var analagous1: UIColor {
return self.withHueOffset(1 / 12)
}
func withHueOffset(offset: CGFloat) -> UIColor {
var h: CGFloat = 0
var s: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: fmod(h + offset, 1), saturation: s, brightness: b, alpha: a)
}
}
Here are some examples of complementary colors (original on top, complementary beneath):
Here are split complementary colors (original on top):
Here are triadic colors (original on top):
Here are tetradic colors (original on top):
Here are analagous colors (original in the middle):
Here is the playground I used to generate those images:
import XCPlayground
import UIKit
let view = UIView(frame: CGRectMake(0, 0, 320, 480))
view.backgroundColor = [#Color(colorLiteralRed: 0.9607843137254902, green: 0.9607843137254902, blue: 0.9607843137254902, alpha: 1)#]
let vStack = UIStackView(frame: view.bounds)
vStack.autoresizingMask = [ .FlexibleWidth, .FlexibleHeight ]
view.addSubview(vStack)
vStack.axis = .Vertical
vStack.distribution = .FillEqually
vStack.alignment = .Fill
vStack.spacing = 10
typealias ColorTransform = (UIColor) -> UIColor
func tile(color color: UIColor) -> UIView {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = color
return view
}
func strip(transforms: [ColorTransform]) -> UIStackView {
let strip = UIStackView()
strip.translatesAutoresizingMaskIntoConstraints = false
strip.axis = .Vertical
strip.distribution = .FillEqually
strip.alignment = .Fill
strip.spacing = 0
let hStacks = (0 ..< transforms.count).map { (i: Int) -> UIStackView in
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .Horizontal
stack.distribution = .FillEqually
stack.alignment = .Fill
stack.spacing = 4
strip.addArrangedSubview(stack)
return stack
}
for h in 0 ..< 10 {
let hue = CGFloat(h) / 10
let color = UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1)
for (i, transform) in transforms.enumerate() {
hStacks[i].addArrangedSubview(tile(color: transform(color)))
}
}
return strip
}
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.complement }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.splitComplement0 },
{ $0.splitComplement1 }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.triadic0 },
{ $0.triadic1 }]))
vStack.addArrangedSubview(strip([
{ $0 },
{ $0.tetradic0 },
{ $0.tetradic1 },
{ $0.tetradic2 }]))
vStack.addArrangedSubview(strip([
{ $0.analagous0 },
{ $0 },
{ $0.analagous1 }]))
XCPlaygroundPage.currentPage.liveView = view