Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

Non repeating random number with UIColor

Hi,

I just completed "Build a Simple iPhone App with Swift 2.0" course and am trying to play around with the color model in that tutorial:

// // ColorModel.swift // FunFacts //

import UIKit import GameKit

struct ColorModel { let colors = [ 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 ]

func getRandomColor() -> UIColor  {
    let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(colors.count)
    return colors[randomNumber]
}

}

Can anyone tell me how i can replace GKRandomSource so that random, non-repeating colors come up? It seems to throw people off when they press a button and the color stays the same.

Thanks

2 Answers

Okay, code compiles fine in ViewController now, but results are about the same. Is there anyway to compare the previous randomNumber with the newly generated index in this statement?

if randomNumber == selectedIndex { randomNumber = (randomNumber + 1) % colors.count }

Just trying get something that works in the short term. Thank you for your help.

Hi Anjali,

I finally saw my mistake. Everything works great.

Regards,

Will

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

Alright. I'm glad I could help!

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

One way to do it would to have another property representing the selected background color index (let's call this selectedIndex). Then you could keep track of which color is currently being used for the background color. If randomNumber equals selectedIndex, you could change randomNumber in some way so it doesn't equal selectedIndex (for example, by adding 1 to randomNumber and modding the result by colors.count to make sure you don't get an index out of bounds error). After doing this, you can set selectedIndex to randomNumber. NOTE: to change a property inside a struct using a function from that struct, you have to mark the function as mutating.

var selectedIndex: Int = 0
mutating func getRandomColor() -> UIColor  {
    var randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(colors.count)
    if randomNumber == selectedIndex {
        randomNumber = (randomNumber + 1) % colors.count
    }
    selectedIndex = randomNumber
    return colors[randomNumber]
}

This is a bit of a quick hack - you'd also need to define the initial behavior of selectedIndex when you first set the background color. This implementation also takes away from the reusability of this ColorModel struct, because now you have a property inside ColorModel corresponding to some specific Object outside of the ColorModel.

I suppose another way to go about things would be to create a separate struct that keeps track of the indices, and modify ColorModel so that it can show the index of a particular color chosen - perhaps by adding a function where you input a UIColor, and the ColorModel returns the index of that UIColor in colors, or -1 if that UIColor isn't in colors?

I have to leave now, but I'll probably get back to you on this.

I hope this helps!

Okay, I injected that into my code in the ColorModel and everything seemed fine until I ran simulator. Then an issue came up in the ViewController:

 @IBAction func showFunFact() {
       let randomColor = ColorModel().getRandomColor()
       funFactButton.tintColor = randomColor
       view.backgroundColor = randomColor
       funFactLabel.text = factModel.getRandomFact()
   }

Got back error message on line 2:

let randomColor = ColorModel().getRandomColor()

Message reads: "Cannot use mutating member on immutable value: function call returns immutable value"

This message also had "ColorModel()" underlined.

Thanks again!

Anjali Pasupathy
Anjali Pasupathy
28,883 Points

I think what's going wrong here is that the compiler considers the instance ColorModel() to be a constant rather than a variable. The code you've written is almost equivalent to this code:

let colorModel = ColorModel()
let randomColor = colorModel.getRandomColor()
/*SET COLORS AND GET RANDOM FACT*/

Because colorModel is a constant, and getRandomColor() is a mutating function (which means it changes the Object it belongs to), the compiler sees this as attempting to change the constant colorModel. To fix this, you need to make colorModel a variable, and make that variable a property of the View Controller so it can keep track of the index beyond when you press the button:

/*AT THE TOP OF THE VIEW CONTROLLER*/
var colorModel = ColorModel()

/*INSIDE THE showFunFact FUNCTION*/
let randomColor = colorModel.getRandomColor()
//SET COLORS AND GET RANDOM FACT

Again, this solution is a quick hack, and there's probably a more elegant solution than this.

I hope this helps!