Having Trouble with Random Numbers in Swift - swift

I am trying to generate random background colors to display to a user when they press a button on the screen. I have an immutable array of UIColor items and I have made a mutable copy to manipulate. When a random color is generated, that color is then returned and removed from the mutable copy of the array to prevent consecutive showings of the same color until all colors have been shown. This is supposed to happen until the array's count is 0, and then it recreates the array to repeat the process. However, when I get down to the array having between 2 and 0 items, the loop seems to turn into an infinite loop. What logic am I missing in my code (playground file)?
var currentColorIndexNumber = 0
var newColorIndexNumber = 0
let colorsArray = [
UIColor(red: 90/255.0, green: 187/255.0, blue: 181/255.0, alpha: 1.0), //teal color
UIColor(red: 222/255.0, green: 171/255.0, blue: 66/255.0, alpha: 1.0), //yellow color
UIColor(red: 223/255.0, green: 86/255.0, blue: 94/255.0, alpha: 1.0), //red color
UIColor(red: 239/255.0, green: 130/255.0, blue: 100/255.0, alpha: 1.0), //orange color
UIColor(red: 77/255.0, green: 75/255.0, blue: 82/255.0, alpha: 1.0), //dark color
UIColor(red: 105/255.0, green: 94/255.0, blue: 133/255.0, alpha: 1.0), //purple color
UIColor(red: 85/255.0, green: 176/255.0, blue: 112/255.0, alpha: 1.0), //green color
]
var mutableColorsArray: [AnyObject] = colorsArray
func randomNumber() -> Int {
// avoid repeating random integers
while currentColorIndexNumber == newColorIndexNumber {
var unsignedArrayCount = UInt32(mutableColorsArray.count)
var unsignedRandomNumber = arc4random_uniform(unsignedArrayCount)
newColorIndexNumber = Int(unsignedRandomNumber)
}
currentColorIndexNumber = newColorIndexNumber
return newColorIndexNumber
}
func randomColor() -> UIColor {
var randomIndex = randomNumber()
var randomColor = mutableColorsArray[randomIndex] as UIColor
mutableColorsArray.removeAtIndex(randomIndex)
if mutableColorsArray.count == 0 {
mutableColorsArray = colorsArray
}
return randomColor
}

Try shuffling instead. Something along these lines:
var colorsArray = ["red","green","blue","yellow","purple"]
var currentColorIndexNumber = colorsArray.count
func shuffle<T>(inout array: Array<T>) -> Void {
for index in 0..<colorsArray.count {
let swapLocation = index + Int(arc4random_uniform(UInt32(colorsArray.count - index)))
if index != swapLocation {
swap(&array[swapLocation], &array[index])
}
}
}
func randomColor() -> String {
// shuffle first time through, then every time the list is exhausted
if currentColorIndexNumber >= colorsArray.count - 1 {
// shuffle, but guarantee first color after shuffling
// is not the same as last color from previous round
do {
shuffle(&colorsArray)
} while colorsArray[0] == lastColor
currentColorIndexNumber = -1
lastColor = colorsArray[colorsArray.count-1]
}
currentColorIndexNumber += 1
return colorsArray[currentColorIndexNumber]
}
for i in (5*colorsArray.count) {
print("\(randomColor()) ")
if i % colorsArray.count == 0 {
println()
}
}

I have had good results with
func randomObject(array: [AnyObject]) -> AnyObject {
return array[Int(arc4random_uniform(UInt32(array.count)))]
}
To enumerate all of the elements in random order, just use a shuffle function as the one below, use the elements of the shuffled array in order and re-shuffle the array once it is exhausted.
func shuffleArray<T>(array: Array<T>) -> Array<T> {
for var i = array.count - 1; i > 0; i-- {
var j = Int(arc4random_uniform(UInt32(i-1)))
swap(&array[i], &array[j])
}
return array
}

Related

How to save a Color in the #AppStorage in SwiftUi

I want to save a Color value in the #AppStorage but I get an error. How can I fix it?
#AppStorage ("selectedColor") var selectedColor: Color = .teal
save color as string
#AppStorage ("selectedColor") var selectedColor: String = ""
create a function that transforms the color into a string
func updateCardColorInAppStorage(color: Color)-> String {
let uiColor = UIColor(color)
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
uiColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return "\(red),\(green),\(blue),\(alpha)"
}
and String to Color
if (cardColor != "" ) {
let rgbArray = cardColor.components(separatedBy: ",")
if let red = Double (rgbArray[0]), let green = Double (rgbArray[1]), let blue = Double(rgbArray[2]), let alpha = Double (rgbArray[3]){ bgColor = Color(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
}

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 Color Literal to cgColor in the Swift?

var CodeBackground = #colorLiteral(red: 0.1294117647, green: 0.1294117647, blue: 0.1960784314, alpha: 1)
cells?.layer.borderColor = //... how can i set this color literal to cgColor?
As I know how to convert that UIColor to cgColor in the Swift
as example
UIColor.black.cgColor
Bingo, but what about Color Literal to cgColor in the Swift
Thank you.
As, You already know the simpler way of using colorLiteral as cgcolor, I'll jump to the other way of doing that...
For that you need a Custom Function which gets the color-value(red , green , blue) from the colorLiteral , which is as below
extension UIColor {
func rgb() -> (red:Int, green:Int, blue:Int, alpha:Int)? {
var fRed : CGFloat = 0
var fGreen : CGFloat = 0
var fBlue : CGFloat = 0
var fAlpha: CGFloat = 0
if self.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha) {
let iRed = Int(fRed * 255.0)
let iGreen = Int(fGreen * 255.0)
let iBlue = Int(fBlue * 255.0)
let iAlpha = Int(fAlpha)
_ = (iAlpha << 24) + (iRed << 16) + (iGreen << 8) + iBlue
return (red:iRed, green:iGreen, blue:iBlue, alpha:iAlpha)
} else {
// Could not extract RGBA components:
return nil
}
}
}
//It's more convenient to use function in `UIColor` extension
Now , after this function created you can convert colorliteral into cgColor as below...
let CodeBackground = #colorLiteral(red: 0.1294117647, green: 0.1294117647, blue: 0.1960784314, alpha: 1)
let rgblit = CodeBackground.rgb()
let Converted_cgColor = CGColor(srgbRed: CGFloat(integerLiteral: rgblit!.red), green: CGFloat(integerLiteral: rgblit!.green), blue: CGFloat(integerLiteral: rgblit!.blue), alpha: CGFloat(integerLiteral: rgblit!.alpha))
You can directly use Converted_cgColor like
cells?.layer.borderColor = Converted_cgColor
HOPE IT HELPS

Convert UIColor initialization to Color Literal Xcode

I have multiple different UIColor objects. Some of them are initialized by a constructor some of them are shown as color literals.
static let optionsHeader = UIColor([ ]) // XCode is showing a color rect.
static let optionButtonSelected = UIColor(red: 0.865, green: 0.804, blue: 0.0, alpha: 1.0)
How can I convert the UIColor.init(...) statements to a color literal.
RGB color literal is same as UIColor initialization:
#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
Or you can select color after typing #colorLiteral().
You can also use extensions to use hex color instead of inputting rgba values
extension UIColor {
convenience init(hexString: String, alpha: CGFloat = 1.0) {
let hexString: String = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let scanner = Scanner(string: hexString)
if (hexString.hasPrefix("#")) {
scanner.scanLocation = 1
}
var color: UInt32 = 0
scanner.scanHexInt32(&color)
let mask = 0x000000FF
let r = Int(color >> 16) & mask
let g = Int(color >> 8) & mask
let b = Int(color) & mask
let red = CGFloat(r) / 255.0
let green = CGFloat(g) / 255.0
let blue = CGFloat(b) / 255.0
self.init(red:red, green:green, blue:blue, alpha:alpha)
}
func toHexString() -> String {
var r:CGFloat = 0
var g:CGFloat = 0
var b:CGFloat = 0
var a:CGFloat = 0
getRed(&r, green: &g, blue: &b, alpha: &a)
let rgb:Int = (Int)(r*255)<<16 | (Int)(g*255)<<8 | (Int)(b*255)<<0
return String(format:"#%06x", rgb)
}
}
Then you can code it with:
self.backgroundColor = UIColor(hexString: "#4A4A4A")

How can I make a Swift enum with UIColor value?

I'm making a drawing app and I would like to refer to my colors through use of an enum. For example, it would be cleaner and more convenient to use Colors.RedColor instead of typing out values every time I want that red color. However, Swift's raw value enums don't seem to accept UIColor as a type. Is there a way to do this with an enum or something similar?
I do it like this (basically using a struct as a namespace):
extension UIColor {
struct MyTheme {
static var firstColor: UIColor { return UIColor(red: 1, green: 0, blue: 0, alpha: 1) }
static var secondColor: UIColor { return UIColor(red: 0, green: 1, blue: 0, alpha: 1) }
}
}
And you use it like:
UIColor.MyTheme.firstColor
So you can have a red color inside your custom theme.
If your color isn't one of those defined by UIColor's convenience method, you can add an extension to UIColor:
extension UIColor {
static var firstColor: UIColor { return UIColor(red: 1, green: 0, blue: 0, alpha: 1) }
static var secondColor: UIColor { return UIColor(red: 0, green: 1, blue: 0, alpha: 1) }
}
// Usage
let myColor = UIColor.firstColor
I use computed properties to solve this problem, this is my code
enum MyColor {
case navigationBarBackgroundColor
case navigationTintCololr
}
extension MyColor {
var value: UIColor {
get {
switch self {
case .navigationBarBackgroundColor:
return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
case .navigationTintCololr:
return UIColor.white
}
}
}
}
then I can use MyColor like this:
MyColor.navigationBarBackgroundColor.value
How can I make a Swift enum with UIColor value?
This is how you would literally make an enum with a UIColor value:
import UIKit
final class Color: UIColor, RawRepresentable, ExpressibleByStringLiteral
{
// MARK:- ExpressibleByStringLiteral
typealias StringLiteralType = String
convenience init(stringLiteral: String) {
guard let (a,r,g,b) = Color.argb(hexColor: stringLiteral) else {
assertionFailure("Invalid string")
self.init(red: 0, green: 0, blue: 0, alpha: 0)
return
}
self.init(red: r, green: g, blue: b, alpha: a)
}
// MARK:- RawRepresentable
public typealias RawValue = String
convenience init?(rawValue: RawValue) {
guard let (a,r,g,b) = Color.argb(hexColor: rawValue) else { return nil }
self.init(red: r, green: g, blue: b, alpha: a)
}
var rawValue: RawValue {
return hexString()
}
// MARK:- Private
/// Return color components in range [0,1] for hexadecimal color strings.
/// - hexColor: case-insensitive string with format RGB, RRGGBB, or AARRGGBB.
private static func argb(hexColor: String) -> (CGFloat,CGFloat,CGFloat,CGFloat)?
{
let hexAlphabet = "0123456789abcdefABCDEF"
let hex = hexColor.trimmingCharacters(in: CharacterSet(charactersIn: hexAlphabet).inverted)
var int = UInt32()
Scanner(string: hex).scanHexInt32(&int)
let a, r, g, b: UInt32
switch hex.count {
case 3: (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) // RGB
case 6: (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) // RRGGBB
case 8: (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) // AARRGGBB
default: return nil
}
return (CGFloat(a)/255, CGFloat(r)/255, CGFloat(g)/255, CGFloat(b)/255)
}
private func hexString() -> String {
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
return String(format: "#%02X%02X%02X%02X", UInt8(red * 255), UInt8(green * 255), UInt8(blue * 255), UInt8(alpha * 255))
}
assertionFailure("Invalid colour space.")
return "#F00"
}
}
enum Colors: Color {
case red = "#F00"
// case blue = "#F00" // Raw value for enum case is not unique
}
let color3 = Color(rawValue: "#000") // RGB
let color6 = Color(rawValue: "#123456") // RRGGBB
let color8 = Color(rawValue: "#12345678") // AARRGGBB
print(Colors(rawValue:"#F00") as Any) // red
print(Colors(rawValue:"#FF0000") as Any) // red
print(Colors(rawValue:"#FFFF0000") as Any) // red
print(Colors(rawValue:"#ABC") as Any) // nil because it’s not a member of the enumeration
// print(Colors(rawValue:"#XYZ") as Any) // assertion on debug, black on release
print(Colors.red) // red
print(Colors.red.rawValue) // UIExtendedSRGBColorSpace 1 0 0 1
With help from
benhurott/UIColorExtension.md
Swift 3 UIColor utilities for random color and color from hex code
Raw type 'Bool' is not expressible by any literal
This can be done much more succinctly (and should):
extension UIColor
{
static let myColor = UIColor(displayP3Red: 0.0, green: 0.7, blue: 0.0, alpha: 1.0)
}
(Any other method that returns a UIColor is equally suitable, doesn't need to be displayP3Red)
Usage:
let someColor: UIColor = .myColor
Actually I use such implementation, it is very convenience for me because of two reason, first one I can use dex value and another all colors in constant
import UIKit
struct ColorPalette {
struct Gray {
static let Light = UIColor(netHex: 0x595959)
static let Medium = UIColor(netHex: 0x262626)
}
}
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(netHex: Int) {
self.init(red: (netHex >> 16) & 0xff, green: (netHex >> 8) & 0xff, blue: netHex & 0xff)
}
}
usage
let backgroundGreyColor = ColorPalette.Gray.Medium.cgColor
If you want to return multiple value then use below code...it's absolutely
working for me....
enum GetDriverStatus : String {
case ClockIn = "Clock In"
case TripStart = "Trip Start"
case BeaconTouchPlant = "Beacon Touch Plant"
case PickUp = "Pick Up"
case BeaconTouchSite = "Beacon Touch Site"
case BeaconLeftSite = "Beacon Left Site"
case DropOff = "Drop Off"
case BreakIn = "Break In"
case BreakOut = "Break Out"
case TripEnd = "Trip End"
case DayEnd = "Day End"
//case ClockOut = "Clock Out"
//Get data from ID
static var allValues: [GetDriverStatus] {
return [
.ClockIn,
.TripStart,
.BeaconTouchPlant,
.PickUp,
.BeaconTouchSite,
.BeaconLeftSite,
.DropOff,
.BreakIn,
.BreakOut,
.TripEnd,
.DayEnd
]
}
//Get Color
var colorAndStatus: (UIColor,String) {
get {
switch self {
case .ClockIn,.TripStart: //Idle
return (UIColor(red: 248/255, green: 39/255, blue: 71/255, alpha: 1.0),"Idle") //dark pink-red
case .BeaconTouchPlant,.PickUp:
return (UIColor(red: 46/255, green: 180/255, blue: 42/255, alpha: 1.0),"Picking up") //Green
case .BeaconTouchSite:
return (UIColor(red: 252/255, green: 172/255, blue: 0/255, alpha: 1.0),"On site") //orange
case .DropOff,.BeaconLeftSite:
return (UIColor(red: 12/255, green: 90/255, blue: 255/255, alpha: 1.0),"Dropping off") //blue
case .BreakIn,.BreakOut:
return (UIColor(red: 151/255, green: 151/255, blue: 151/255, alpha: 1.0),"On break") //warm-grey-two
case .TripEnd:
return (UIColor.black,"Trip end")
case .DayEnd:
return (UIColor.black,"Done for the day")
}
}
}
}
How to use this code
Passing .allvalues["index of your option"] you getting UIColor at 0 position as well as String value as 1 position
GetDriverStatus.allValues[1].colorAndStatus.0 //UIColor.Black
GetDriverStatus.allValues[2].colorAndStatus.1 //"Picking up"
Based on #Jano's answer I made an improvement by using Int as the literal type:
import UIKit
public final class Colors: UIColor {
}
extension Colors: ExpressibleByIntegerLiteral {
public typealias IntegerLiteralType = Int
public convenience init(integerLiteral value: Int) {
let red = CGFloat((value & 0xFF0000FF) >> 24) / 0xFF
let green = CGFloat((value & 0x00FF00FF) >> 16) / 0xFF
let blue = CGFloat((value & 0x0000FFFF) >> 8) / 0xFF
let alpha = CGFloat(value & 0x00FF00FF) / 0xFF
self.init(red: red, green: green, blue: blue, alpha: alpha)
}
}
extension Colors: RawRepresentable {
public typealias RawValue = Int
public var rawValue: RawValue {
return hex
}
public convenience init?(rawValue: RawValue) {
self.init(integerLiteral: rawValue)
}
}
fileprivate extension UIColor {
var hex: Int {
var fRed: CGFloat = 0
var fGreen: CGFloat = 0
var fBlue: CGFloat = 0
var fAlpha: CGFloat = 0
if self.getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha) {
let red = Int(fRed * 255.0)
let green = Int(fGreen * 255.0)
let blue = Int(fBlue * 255.0)
let alpha = Int(fAlpha * 255.0)
let rgb = (alpha << 24) + (red << 16) + (green << 8) + blue
return rgb
} else {
return 0x000000
}
}
}
public enum MainPalette: Colors {
case red = 0xFF0000ff
case white = 0xFFFFFFFF
}
public enum FeatureXPalette: Colors {
case blue = 0x024F9Eff
// case bluish = 0x024F9Eff // <- Can't do
case red = 0xFF0000ff
}
The advantage is that it doesn't allow duplicate colors (as a true enum) and also I support alpha.
As you can see, you can create multiple enums for different palettes/schemes. In the case you want views to be able to use any palette, you can just add a protocol:
protocol Color {
var color: UIColor { get }
}
extension MainPalette: Color {
var color: UIColor {
return rawValue
}
}
extension FeatureXPalette: Color {
var color: UIColor {
return rawValue
}
}
so that way you can have a function that takes in the protocol:
func printColorEquality(color1: Color, color2: Color) {
print(color1.color == color2.color)
}
let red1: Color = MainPalette.red
let red2: Color = FeatureXPalette.red
printColorEquality(color1: red1, color2: red2)
What I also like to do is add static vars for convenience:
extension MainPalette {
public static var brightRed: UIColor {
return MainPalette.red.color
}
}
that gives you a cleaner api:
view.backgroundColor = MainPalette.brightRed
Naming can be improved: you have to choose if you want a nice convenience api or nice naming for your enums.
This answer is probably late, but for others finding this question.
I was not satisfied with the answers above, since adding colors as UIColors extension is not always what you want, since:
It might not be the best solution from Software architecture perspective.
You can not use the power enums have, e.g. CaseIterable
This is the solution I came up with:
enum PencilColor {
case lightRed
case darkPurple
var associatedColor: UIColor {
switch self {
case .lightRed: return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
case .darkPurple: return UIColor(red: 67/255, green: 173/255, blue: 247/255, alpha: 1.0)
}
}
}