Reputation: 25
I'm trying to use regex and range for the first time in Swift. I want to see if the letter the user enters into the textfield will match the word that they have to guess. If it does match the matching letter or letters will be displayed in a UILabel (similar to how you play hangman, if you guess the correct letter once and there are multiple occurrences of that letter, all occurrences will show). When a button is clicked the method below is called. It works fine when finding the matching letters, and inserting them at the right location, BUT when the UILabel is updated after the loop it only updates the label with the result of the second/final loop. How can I get a combination of the result from all the iterations of the loop? Any help would be appreciated. Thank you
func findLetter(displayedWord toSearchin: String, userInput toSearchFor: String) {
let ranges: [NSRange]
var labelUpdate = String()
do {
let regex = try NSRegularExpression(pattern: toSearchFor, options: [])
let displayedWord = toSearchin as NSString
let rangeOfSearch = NSMakeRange(0, displayedWord.length)
ranges = regex.matches(in: toSearchin, range: rangeOfSearch).map {$0.range}
let nsStringlabel = wordLabel.text as NSString?
for range in ranges {
labelUpdate = (nsStringlabel?.replacingCharacters(in: range, with: toSearchFor))!
print(labelUpdate)
//the word is lavenders, so this prints:
//___e_____
//______e__
// I want:
//___e__e__
}
DispatchQueue.main.async(execute: {
self.wordLabel.text = labelUpdate
})
}
catch {
ranges = []
}
}
Upvotes: 0
Views: 249
Reputation: 909
As stated in a comment, you’re always updating the original nsStringlabel
variable, thus always overriding the previous modification in the previous loop run.
I’d recommend you init labelUpdate
with wordLabel.text as NSString?
and completely remove nsStringlabel
. This should solve your problem.
That being said, there are a lot of other problems that could be fixed in this function. In particular, why use regexes? It’s expensive and not useful there. Also, you’re dispatching before setting the label value, which is good, but not before retrieving the value… either a dispatch is needed or it is not, but it cannot be needed at one place and not at the other. If you call your function from the main thread (as a response to a user input for instance), you should be good and not need a dispatch.
Here what I would have done (should be safer and faster):
func updateLabel(withDestinationWord destinationWord: String, userInput: String) {
var labelText = wordLabel.text
var startIndex = labelText.characters.startIndex
while let r = destinationWord.range(of: userInput, options: .caseInsensitive, range: startIndex..<labelText.characters.endIndex) {
labelText.replaceSubrange(r, with: userInput)
startIndex = labelText.characters.index(after: r.lowerBound)
}
wordLabel.text = labelText
}
Be sure to have the same length for wordLabel.text
and destinationWord
!
Upvotes: 1