Random Value in Swift

How to generate a random number in Swift?

Swift 4.2+

Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random() method on numeric types.

let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()

Generating random numbers with Swift

let randomIntFrom0To10 = Int.random(in: 1..<10)
let randomFloat = Float.random(in: 0..<1)

// if you want to get a random element in an array
let greetings = ["hey", "hi", "hello", "hola"]
greetings.randomElement()

Generating random values in Swift between two integer values

try this

let randomNumber = arc4random_uniform(40) + 10
println(randomNumber)

in general form

let lower : UInt32 = 10
let upper : UInt32 = 50
let randomNumber = arc4random_uniform(upper - lower) + lower
println(randomNumber)

How to Generate a New Random Number Once Every time Content View Loads?

Approach #1:

MyView is created once, when ContentView renders.

Inside MyView, randomInt is set when the view is first created and then never modified. When you press the button, myMessage is set, but randomInt is never changed. If you wanted randomInt to change, you'd want to say something like:

randomInt = Int.random(in: 1...100)

inside your button action:

Approach #2:

You're creating a new randomInt variable in the local scope by declaring let randomInt = inside the view body.

Instead, if I'm reading your initial question correctly, I think you'd want something using onAppear:

struct MyView: View {

@State var randomInt = 0
@State var myMessage: String = ""

var body: some View {
VStack {
Text(String(randomInt))

Button("Press Me") {
myMessage = String(randomInt)
}

Text(myMessage)
}.onAppear {
randomInt = Int.random(in: 1...100)
}
}
}

You'll see that with this, every time you go back in the navigation hierarchy and then revisit MyView, there's a new value (since it appears again). The button triggering a re-render doesn't re-trigger onAppear

Approach #3:

MyView gets created on the first render of the parent view (ContentView). Unless something triggers a refresh of ContentView, you wouldn't generate a new random number here.


In conclusion, I'm a little unclear on what the initial requirement is (what does it mean for a View to 'load'? Does this just mean when it gets shown on the screen?), but hopefully this describes each scenario and maybe introduces the idea of onAppear, which seems like it might be what you're looking for.


Addendum: if you want the random number to be generated only when ContentView loads (as the title says), I'd create it in ContentView instead of your MyView and pass it as a parameter.


struct ContentView: View {
@State var randomInt = Int.random(in: 1...100)

var body: some View {

NavigationView {

NavigationLink(destination: MyView(randomInt: $randomInt)) {
Text("Link to MyView")
}
}
}
}

struct MyView: View {

@Binding var randomInt : Int
@State var myMessage: String = ""

var body: some View {

Text(String(randomInt))

Button("Press me to refresh only myMessage") {
myMessage = String(randomInt)
}

Button("Press me to change the randomInt as well") {
randomInt = Int.random(in: 1...100)
myMessage = String(randomInt)
}

Text(myMessage)
}

}

Generating a random number in Swift

It really depends on how much casting you want to avoid. You could simply wrap it in a function:

func random(max maxNumber: Int) -> Int {
return Int(arc4random_uniform(UInt32(maxNumber)))
}

So then you only have to do the ugly casting once. Everywhere you want a random number with a maximum number:

let r = random(max: _names.count)
let name: String = _names[r]

As a side note, since this is Swift, your properties don't need _ in front of them.

How to generate a random number in Swift without repeating the previous random number?

Store the previous generated number in a variable and compare the generated number to the previous number. If they match generate a new random number. Repeat the generation of new numbers until they don't match.

var previousNumber: UInt32? // used in randomNumber() 

func randomNumber() -> UInt32 {
var randomNumber = arc4random_uniform(10)
while previousNumber == randomNumber {
randomNumber = arc4random_uniform(10)
}
previousNumber = randomNumber
return randomNumber
}

Random number without repeat in swiftUI

This approach uses an extra observable class. It copies the 6 numbers into a temporary array. On tap one random number is removed from the array (this number is being displayed) so each number is only used once. If the array is empty the cycle starts over.

class Random : ObservableObject {
var array = [Int]()

@Published var randomNumber = 0

func next() {
if array.isEmpty { array = [1,2,3,4,5,6] }
let index = Int.random(in: 0..<array.count)
randomNumber = array.remove(at: index)
}
}

struct RanNum: View {

@StateObject private var random = Random()

var body: some View {
VStack{

Text(random.randomNumber == 0 ? "" : "\(random.randomNumber)")
.padding()
.multilineTextAlignment(.center)
.background(Color.white.opacity(0.5))
.cornerRadius(10)
.frame(width: 390, alignment: .center)

Button(action: {
random.next()
}) {
Image(systemName: "arrow.counterclockwise.circle.fill")
.foregroundColor(.black)
.font(.system(size: 30))
.padding()
}
}
}
}

How to generate different random number in a range in Swift?

A Set is simpler, it guarantees unique values

var randomIndex = Set<Int>()

while randomIndex.count < 3 {
randomIndex.insert(Int.random(in: 1 ... 10))
}

Another approach is to create an array of the values, pick a random element and remove this element from the array

var randomValues = Set<Int>()
var valueArray = Array(1...10)
for _ in 0..<3 {
guard let randomElement = valueArray.randomElement() else { break }
randomValues.insert(randomElement)
valueArray.remove(at: valueArray.firstIndex(of: randomElement)!)
}

Regarding is there any way to optimize my approach the for loop in your code is extremely expensive.

First of all the recommended syntax for an index based loop is

for i in 0 ..< generatedValue.count {

But actually you don't need the index so Fast Enumeration is better

for value in generatedValue {
if randomValue != value {

The most expensive part of the loop is that the entire array is always iterated even if the value was already found. At least add a break statement to exit the loop

for value in generatedValue {
if randomValue != value {
randomIndex.append(randomValue)
c += 1
break
}
}

But there is still a more efficient syntax

if !generatedValue.contains(randomValue) {
randomIndex.append(randomValue)
c += 1
}

How to make a list of random numbers that never repeat (consecutively) in swift ui

First, I'd move your numbers into their own model (I'm using NumberModel) so that they can have unique IDs, which will make them more convenient to use in the List/ForEach (especially if they end up changing values over time). This will also allow you to just use ForEach(numbers) and not deal with the indices at all.

You don't need @State variables for previousNumber and randomNumber because they're just used within onAppear. I've refactored your onAppear a bit, but you could definitely go further with it.

struct NumberModel : Identifiable {
var id = UUID()
var number : Int

var stringValue : String {
number.description
}
}

struct ContentView: View {
@State var numbers: [NumberModel] = []
var body: some View {
List{
ForEach(numbers) { i in
Text(i.stringValue)
}
}
.navigationTitle("numbers")
.onAppear() {
var previousNumber : Int? = nil
for _ in 0 ..< 100 {
while true {
let randomNumber = Int.random(in: 0...9)
guard randomNumber != previousNumber else {
continue
}
previousNumber = randomNumber
numbers.append(NumberModel(number: randomNumber))
break
}
}
}
}
}


Related Topics



Leave a reply



Submit