#IBAction func generateBtn(sender: UIButton) {
let strt = UInt32(strtNum.text!)
let end = UInt32(endNum.text!)
let ttlNums = Int(amtNums.text!)
let x = RandomNum()
var z = 0
while z<ttlNums{
let y = x.rndNumGen(strt!, end: end!)
z += 1
var h = [String]()
h.append(String(y))
let display:String = h.joinWithSeparator(", ")
winningNums.text = display
print (display)
}
}
I don't know what is wrong with this code. I am trying to put the string display into the label and it prints out the last number from the random number generator. When i print it to the console it shows all of the random numbers.
The primary issue here is that your array is created fresh in every loop iteration, and your label is being set in every loop iteration. That means that the array will only ever contain the element made in that iteration, after which it's reset to a new array, and a new element is added. The array needs to be initialized once at the start, and have elements added to it repeatedly in the loop, then put into the label once at the end.
#IBAction func generateBtn(sender: UIButton) {
guard let startText = strtNum.text, let start = UInt32(startText),
let endText = endNum.text, let end = UInt32(endText),
let ttlText = amtNums.text, let ttlNums = UInt32(ttlText) else {
//one of these is nil, handle it gracefully here
return
}
let randomGenerator = RandomNum()
var h = [String]()
h.reserveCapacity(ttlNums)
for _ in 0..<ttlNums {
let randomNum = randomGenerator.rndNumGen(start, end: end)
h.append(String(RandomNum))
}
let display = h.joinWithSeparator(", ")
winningNums.text = display
print(display)
}
I've made a few other changes to bring this code in line with Swift best practices and conventions:
Don't force unwrap. Use an if let or guard let binding to safely handle nil values.
Give your variables meaningful names. Avoid single-letter names except in specific instances.
Don't put spaces beside a function/method name and the proceeding brackets.
Don't use a while loop to iterate over a known range. Instead, use a for in loop.
Dn't type in t3xtspk, it mks ur code look lik an angsty teenagr wrote it. Autocomplete will finish off words for you, so you barely end up typing anyway. Make it easy and readable.
I would suggest you make a few changes yourself:
Rename generateBtn. Functions/methods DO things, they're actions. They should be named with verbs, or verb phrases. Perhaps try something like displayRandomArray.
Refactor the random array generation into its own method.
Rename RandomNum. By the looks of it, it's not a number at all, it's a random number generator. Perhaps try RandomNumberGenerator
Rename h.
Add code to deal with what happens when the .text is nil, or what happens when it contains a string that isn't a UInt32 (thus causing the UInt32 initializer to fail and return nil)
Related
I have received an array index out of range error. I have two arrays cardsCurrentlyInPlay and currentCardsSelected. Every card that is in this game has a unique ID. I am attempting to find find the index of the card in cardsCurrentlyInPlay whose cardID matches the cardID of the card in currentCardsSelected. I am doing this by using the index(where:) method that takes a closure. My closure just checks if the IDs match, they obviously match because I am using the ! to unwrap them and the app does not crash there. It seems as though the index(where:) method is returning the wrong index. I have looked at this for hours and I do not understand whats going on.
Heres the code:
let indexOfFirstCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[0].cardID)}))!
let indexOfSecondCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[1].cardID)}))!
let indexOfThirdCard = cardsCurrentlyInPlay.index(where: ({($0?.cardID == currentCardsSelected[2].cardID)}))!
if deck.isEmpty && selectedCardsMakeASet() {
/* Remove the old cards */
cardsCurrentlyInPlay.remove(at: indexOfFirstCard)
cardsCurrentlyInPlay.remove(at: indexOfSecondCard)
cardsCurrentlyInPlay.remove(at: indexOfThirdCard) // where code is blowing up
currentCardsSelected.removeAll()
/* Return indicies of cards to clear from the UI */
return .deckIsEmpty(indexOfFirstCard, indexOfSecondCard, indexOfThirdCard)
}
The index you’re getting is correct when your get it, but it becomes wrong when you remove other cards. Consider:
var a = ["x", "y", "z"]
let indexOfX = a.index(of: "x")! // returns 0
let indexOfZ = a.index(of: "z")! // returns 2
a.remove(at: indexOfX) // removes "x"; now a = ["y", "z"]
a.remove(at: indexOfZ) // index 2 is now out of bounds
You could interleave the calls to index(of:) and remove(at:), but a better approach would be to remove all three cards in a single pass, something like this:
let selectedCardIDs = currentCardsSelected.map { $0.cardID }
cardsCurrentlyInPlay = cardsCurrentlyInPlay.filter { card in
!selectedCardIDs.contains(card.cardID)
}
Note that this has the added benefit of avoiding the force unwrap, a sign of sounder logic.
This is because of out of bounds.
After the first two code excuted.
cardsCurrentlyInPlay.remove(at: indexOfFirstCard) &
cardsCurrentlyInPlay.remove(at: indexOfSecondCard)
there is only one element in the cardsCurrentlyInPlay .
Then if you excute cardsCurrentlyInPlay.remove(at: indexOfThirdCard), the program will crash.
I am having some trouble with my swift code. I want to make an endless game similar to the line zen, where a random node appears from the top. I used this code to help use the randomizer:
let randomWallNameIndex = Int(arc4random_uniform(2))
let wallNames = ["obsticle #1", "obsticle #2"]
//can also be [1, 2,]
if wallNames == "obsticle #1"{
//insert code here
}
Although, I'm having trouble using the if statement to tell whether a specific number was selected and I can spawn that certain node.
Can someone find the solution?
let randomWallNameIndex = Int(arc4random_uniform(2))
let wallNames = ["obsticle #1", "obsticle #2"]
//can also be [1, 2,]
let wall = wallNames[randomWallNameIndex]
if wall == "obsticle #1"{
//insert code here
}
Just add a variable that holds the string from the array at that index and use that variable in the if statement.
I am developing a IOS custom keyboard. I was wondering if there was a way to fetch the current text inside of the text field and how it would work.
For example, we can use textDocumentProxy.hasText() to see if the textfield has text inside but I want to know the exact string that is inside the textfield.
The closest things would be textDocumentProxy.documentContextBeforeInput and textDocumentProxy.documentContextAfterInput. These will respect sentences and such, which means if the value is a paragraph, you will only get the current sentence. Users have been known to retrieve the entire string by repositioning the cursor multiple times until everything is retrieved.
Of course, you generally do not have to worry about this if the field expects a single value like a username, email, id number, etc. Combining the values of both before and after input contexts should suffice.
Sample Code
For the single phrase value, you would do:
let value = (textDocumentProxy.documentContextBeforeInput ?? "") + (textDocumentProxy.documentContextAfterInput ?? "")
For values that might contain sentence ending punctuation, it will be a little more complicated as you need to run it on a separate thread. Because of this, and the fact that you have to move the input cursor to get the full text, the cursor will visibly move. It is also unknown whether this will be accepted into the AppStore (after all, Apple probably did not add an easy way to get the full text on purpose in order to prevent official custom keyboards from invading a user's privacy).
Note: the below code is based off of this Stack Overflow answer except modified for Swift, removed unnecessary sleeps, uses strings with no custom categories, and uses a more efficient movement process.
func foo() {
dispatch_async(dispatch_queue_create("com.example.test", DISPATCH_QUEUE_SERIAL)) { () -> Void in
let string = self.fullDocumentContext()
}
}
func fullDocumentContext() {
let textDocumentProxy = self.textDocumentProxy
var before = textDocumentProxy.documentContextBeforeInput
var completePriorString = "";
// Grab everything before the cursor
while (before != nil && !before!.isEmpty) {
completePriorString = before! + completePriorString
let length = before!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(-length)
NSThread.sleepForTimeInterval(0.01)
before = textDocumentProxy.documentContextBeforeInput
}
// Move the cursor back to the original position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(completePriorString.characters.count)
NSThread.sleepForTimeInterval(0.01)
var after = textDocumentProxy.documentContextAfterInput
var completeAfterString = "";
// Grab everything after the cursor
while (after != nil && !after!.isEmpty) {
completeAfterString += after!
let length = after!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
textDocumentProxy.adjustTextPositionByCharacterOffset(length)
NSThread.sleepForTimeInterval(0.01)
after = textDocumentProxy.documentContextAfterInput
}
// Go back to the original cursor position
self.textDocumentProxy.adjustTextPositionByCharacterOffset(-(completeAfterString.characters.count))
let completeString = completePriorString + completeAfterString
print(completeString)
return completeString
}
I have a multidimensional array that I want to display the values of onto one UILabel in each respective cell.
My arrays look like this:
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
I'm looping through these values in the cellForRowAtIndexPath in order for it to display on different cells (on a UILabel) the appropriate values:
if let onTheLabel: AnyObject = arrayOfContactsFound as? AnyObject {
for var i = 0; i < objects!.count; i++ {
cell?.contactsUserHas.text = "\(onTheLabel[indexPath.row][i])" as! String
print("arrayOfContactsFound Printing! \(onTheLabel)")
}
}
When printing to the console I get:
arrayOfContactsFound Printing! (
(
beverages,
"supply chain",
pharmacuticals
)
)
But on my label I get "beverages". That's it. How can I get the other 2 values (or X amount if there are more or less than 3 values)?
My for in loop is obviously not doing the trick. Assuming I can optimize/fix that to display all the values?
Thanks in advance.
In your loop you're setting the text of your label multiple times. Each time you set it it doesn't accumulate, it completely replaces the current text with the new text. You'll want something like this:
// Remove the cast and the loop in your code example, and replace with this
let items = arrayOfContactsFound[indexPath.row]
let itemsString = items.joinWithSeparator(" ")
cell?.contactsUserHas.text = itemsString
Another thing to note is your cast doesn't quite make a lot of sense.
var arrayExample = [["beverages", "food", "suppliers"]["other stuff, "medicine"]]
So arrayExample is of type [[String]]. I'm assuming each cell in your table view represents one of the arrays of strings in your array. So each cell represents one [String]. So your items should be arrayExample[indexPath.row]. The cast to AnyObject doesn't make too much sense. If anything you'd be casting it to [[AnyObject]], but there's no reason to because the compiler should already know it's [[String]].
I am working on a very basic (I think) program in Xcode. I am trying to write an app for "drawing straws" where the last person who is chosen is the loser. There are a few things I'd like to do. First, see the code below:
import UIKit
let players = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
var draw = Int(arc4random_uniform(4))
playerNames.removeAtIndex(draw)
print(playerNames)
let round2 = playerNames.count
var draw2 = Int(arc4random_uniform(2))
playerNames.removeAtIndex(draw2)
print(playerNames)
let round3 = playerNames.count
var draw3 = Int(arc4random_uniform(1))
playerNames.removeAtIndex(draw3)
print(playerNames)
The first thing that's wrong is I'm currently hard-coding the random integer being drawn in var draw = Int(arc4random_uniform(4)). When I try to reference players instead of just typing in 4, I get an error. Can someone please help explain the problem there?
I'll stop there for now to see if I can fix that, and I'll wait until that is fixed before posting a new question. Thank you.
The function arc4random_uniform() takes a UInt32, so you need to declare the variable players as UInt32
let players: UInt32 = 4
var draw = (arc4random_uniform(players))
For handling the round variables, you would cast the count to UInt32.
let round2 = UInt32(playerNames.count)
You can also refactor your code
let players: UInt32 = 4
var playerNames: [String] = ["John", "Tyler", "Pete", "Dave"]
println(playerNames)
for loop in 1...players {
RemovePlayer(&playerNames)
println(playerNames)
}
And the code for the RemovePlayer function
func RemovePlayer(inout names: [String]) {
// get the number of names in the array
var maxNames = UInt32(names.count)
var draw = Int((arc4random_uniform(maxNames)))
names.removeAtIndex(draw)
}
As Blackfrog already stated your randomization function requires a UInt32
Since you stated that you just started programing I would like to give you some other advice. Your code can be written as:
var playerNames = ["John", "Tyler", "Pete", "Dave"]
for var index = playerNames.count ; index > 1 ; index -= 1{
var random:Int = Int(arc4random_uniform(UInt32(index)))
playerNames.removeAtIndex(random)
}
println(playerNames[0])
This has a few advantages. At the moment you are hardcoding the values for how many players join a game. You will need to re-write an unnecessary amount of code if you want to add a player.
Let’s see what the above code does:
We start by declaring the playerNames adding a type declaration of [String] isn’t necessary, swift already knows that it is this type by its initial value.
for var index = playerNames.count; index > 1 ; index -= 1{
We create a new variable called index this will be set to the amount of items in the playerNames array (4 in this case)
After this we will declare that we want the loop as long as our index is greater than 1 this will make it run 3 times and make sure we are left with 1 item in our array
The index -=1 will subtract 1 from the index after each iteration through the loop.
var random:Int = Int(arc4random_uniform(UInt32(index)))
Here we declare a new variable random this will be of type Integer. Let’s work from the inside to the outside. UIt32(index) will convert our index which is of type Int to a type of UInt32, this is needed cause our random function requires a UInt32
Next up we request a random value which will lay between the index and 0 (thus between the bounds of the array). We want to remove the player who belongs to this random value in our array. To do this we need to convert our random Uint32 back to an Int we can do this by using Int().
Next we remove the player at the index using
playerNames.removeAtIndex(random)
Lastly we print the first (and only) item left in the array using
println(playerNames[0])