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
Understanding Optional Global Variables in Swift
How to Provide Default Implementation of an Objective-C Protocol in a Swift Protocol Extension
Swift 1.2 Assigning Let After Initialization
How to Insert a Row in Tableview Without Using Reloaddata Function in Cocoa
How to Get My Button to Reflect The Bool of My Environmentobject
How to Access Results from a Realm in Swift
Reload Tableview After Clicking The Button Present Inside One of The Tableview Cell in Swift
Swift Optionals Best Practices
What's The Correct Number Type for Financial Variables in Swift
Merging Two Arrays of Dictionaries Based on a Shared Value
How to Fill Triangle in Swift Using Core Graphics
Swift Struct Adopting Protocol with Static Read-Write Property Doesn't Conform
Realitykit - Keep Object Always in Front of Screen
Calculating Distance in Vapor4
Biometric Authentication Evaluation with Swiftui
How to Allow a Generic Collection to Perform Under The Hood Conversion in Swift