I completed a challenge to create a story app that has different endings depending on the answer you give. And the app works fine but I don't really get it fully. Basically it's a very simple that that just has a UILabel with the text and 2 UIButtons that change every step of the game.
In order to separate our if statements from another we had to create a variable called storyIndex that we put after the sender.tag ==.
Code looks like this :
#IBAction func buttonPressed(_ sender: UIButton) {
if sender.tag == 1 && storyIndex == 1 {
storyTextView.text = story3
topButton.setTitle(answer3a, for: .normal)
bottomButton.setTitle(answer3b, for: .normal)
storyIndex = 3
}
else if sender.tag == 2 && storyIndex == 1 {
storyTextView.text = story2
topButton.setTitle(answer2a, for: .normal)
bottomButton.setTitle(answer2b, for: .normal)
storyIndex = 2
}
else if sender.tag == 1 && storyIndex == 3 {
storyTextView.text = story6
storyIndex = 6
topButton.isHidden = true
bottomButton.isHidden = true
}
else if sender.tag == 2 && storyIndex == 3 {
storyTextView.text = story5
storyIndex = 5
topButton.isHidden = true
bottomButton.isHidden = true
}
else if sender.tag == 1 && storyIndex == 2 {
storyTextView.text = story3
topButton.setTitle(answer3a, for: .normal)
bottomButton.setTitle(answer3b, for: .normal)
storyIndex = 3
}
else if sender.tag == 2 && storyIndex == 2 {
storyTextView.text = story4
storyIndex = 4
topButton.isHidden = true
bottomButton.isHidden = true
}
and the variable was put below the IBoutlets connections:
var storyIndex : Int = 1
and the top of the code just below the class we had our constants containing the story text:
eg:
let story1 = "Your car has blown a tire on a winding road in the middle of nowhere with no cell phone reception. You decide to hitchhike. A rusty pickup truck rumbles to a stop next to you. A man with a wide brimmed hat with soulless eyes opens the passenger door for you and asks: \"Need a ride, boy?\"."
So I finally figure out how to do it with little help from the Q&A tab but really I don't get how Swift knows that storyIndex let's say 2 is a story 2 ? where do I define that, basically where is the connection between the constants and the variable storyIndex. Does the word Index works like that ? Or did I define it?
Wouldn't be easier just to do something like this?
if sender.tag == 1 && storyTextView.text == (\story1) {
Sorry about my English, it's my third language.
The code you posted simply runs when the button is pushed. The button is determined based on the tag property, which is set in the InterfaceBuilder. Looks like action 1 is tag = 1 is previous and action 2 is tag = 2 is next.
Then, the code looks at current story index and the button pushed (tag), and determines what the the next story index is and sets the appropriate story text.
The reason it's not comparing story text is story text doesn't really give you an easy way to see where in the story you are unless you read the text. Also, comparing strings takes longer than comparing integers, although in this case it would be negligible.
Here, the mapping between the index and the story text is done inside each if statement. A better way would be to have an array that has story text indexed by story index (eg: storyText[storyIndex]).
Related
This is in switft code. I would like to change the background color a button that is currently red to blue. However it tapped again I would like it to change from blue to red. How I normally would do this would be.
var counter = 0
var button = UIButton()
func switch(){
if counter % 2 == 0 {
button.backgroundcolor = .blue
}
else {
button.backgroundcolor = .red }
counter += 1}
I am writing this question because although what I am doing is working. I am thinking there has to be a more efficient way to write code instead of declaring a var and diving it.
As there are only two states declare counter as Bool
var backgroundColorIsBlue = false
In the switch function – which doesn't compile because switch is a reserved word – just toggle (invert) the Bool and set the background color. The ternary operator uses the value after the ? if backgroundColorIsBlue is true otherwise the value after the :.
#objc func switchAction(_ sender : UIButton) {
backgroundColorIsBlue.toggle()
sender.backgroundColor = backgroundColorIsBlue ? .blue : .red
}
I have a UIImageView that displays picture cards of the Alphabet, A-Z. I use a segment control to determine if the cards display in order A-Z or are randomly displayed.
Whenever I start the in order, if I switch over to random and then back to in order, the index starts at where I left off. So if I was at card C, switch to random, do a few and then switch back to in order, the next card is D, I want it to start over at A (index 1).
How would I reset the index counter to index 1?
I'm using the below variable:
var number = 1
I called that on my button click:
#IBAction func dealBTN(_ sender: Any) {
if segControl.selectedSegmentIndex == 0 {
imageView.image = UIImage(named: "card1")
imageView.image = UIImage(named: "card\(number)")
number = number % 26 + 1
}
else if segControl.selectedSegmentIndex == 1 {
let Number = Int.random(in: 1...26)
imageView.image = UIImage(named: "card\(Number)")
}
}
You are using a property called number to keep track of your next card to display when they are in order. Simply set it to 1 whenever you are picking a random card so that the index is back at 1 the next time you choose an ordered card:
if segControl.selectedSegmentIndex == 0 {
imageView.image = UIImage(named: "card\(number)")
number = number % 26 + 1
}
else if segControl.selectedSegmentIndex == 1 {
let random = Int.random(in: 1...26)
imageView.image = UIImage(named: "card\(random)")
number = 1
}
I am currently learning Swift and can not continue. I create a dice game. If the "if query" is true, the program should pause briefly before a new query is possible.
#IBAction func guess(_ sender: Any) {
let diceRoll = arc4random_uniform(5)
let fingerErgebnis = diceRoll + 1
let fingerAntwort = String(fingerErgebnis)
if fingerTextfield.text == fingerAntwort{
ergebnis.text = "Richtig erraten!"
ergebnis.textColor = UIColor.green
ergebnis.font = ergebnis.font.withSize(20)
bild.image = UIImage(named: ("win.jpg"))
button.setTitle("Neues Spiel!", for: .normal)
fingerTextfield.text = " "
sleep (2)
}else if fingerErgebnis == 1 {
ergebnis.text = "Leider falsch! Die Zahl war \(fingerAntwort)."
ergebnis.textColor = UIColor.red
bild.image = UIImage(named: ("finger1.jpg"))
button.setTitle("Versuch es nochmal!", for: .normal)
} ...
As far as everything works, but I want that everything is running first and I have to wait 2 seconds until I can click the button again. My test is paused first, then the rest of the if commands are executed. I want that the other way around.
Sorry for my terrible english ;)
You can disable your button and re-enable it after two seconds using async(deadline:execute:). So, instead of sleep(2) put the following:
button.isUserInteractionEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
// assuming button is a property of the current view controller
self?.button.isUserInteractionEnabled = true
}
sleep (2)
This is the wrong way to create the delay you're looking for. sleep() stops your process entirely, at least on that thread. What you should do instead is to disable the button and then use a Timer (or some other form of delayed execution, but Timer is easy) to re-enable it two seconds later.
I am working on UITests using XCode. I have multiple CollectionView cells.
When I perform Count in the collectionView it shows the certain count.
I can able to access first two cells but coming to the 3rd cell as 3(depends on device size). It says that specific button I am looking for in 3rd Cell as exists.
But isHittable is false.
Is there any way I can tap on the button on the 3rd Cell.
I have tried using the extension for forceTapElement() which is available online, it didn’t help.
Extension Used:
extension XCUIElement{
func forceTapElement(){
if self.isHittable{
self.tap()
}else{
let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: .zero)
coordinate.tap()
}
}
}
Tried to perform swipeUp() and access the button. it still shows isHittable as false
The only way I've found is to swipe up untile the isHittable will be true.
app.collectionViews.cells.staticTexts["TEST"].tap()
Thread.sleep(forTimeInterval: 3)
let collectionView = app.otherElements.collectionViews.element(boundBy: 0)
let testAds = collectionView.cells
let numberOfTestAds = testAds.count
if numberOfTestAds > 0 {
let tester = collectionView.cells.element(boundBy: 2).buttons["ABC"]
for _ in 0..<100 {
guard !tester.isHittable else {
break;
}
collectionView.swipeUp()
}
}
Please note that the swipeUp() method will only move few pixels. If you want to use more comprehensive methods you can get AutoMate library and try swipe(to:untilVisible:times:avoid:from:):
app.collectionViews.cells.staticTexts["TEST"].tap()
Thread.sleep(forTimeInterval: 3)
let collectionView = app.otherElements.collectionViews.element(boundBy: 0)
let testAds = collectionView.cells
let numberOfTestAds = testAds.count
if numberOfTestAds > 0 {
let tester = collectionView.cells.element(boundBy: 2).buttons["ABC"]
collectionView.swipe(to: .down, untilVisible: tester)
// or swipe max 100 times in case 10 times is not enough
// collectionView.swipe(to: .down, untilVisible: tester, times: 100)
}
I'm using the following code to find a certain subview inside my view. It works fine, and the for loop runs only once because it only finds one subview that satisfies the where clause.
for view in boardView.subviews where (view as? PlayingCardView)?.position.location == source.
&& (view as! PlayingCardView).position.column == source.column
&& (view as! PlayingCardView).position.row >= source.row
However, it seems weird that I'm running a loop here when I'm not actually doing any...loop-y things (know what I mean)?
Is there some other way to "search" like this? Like a way to use subviews.index(of:) and use the conditions from the where clause, or something like that?
Also, I know that I could do the same code like this:
for view in boardView.subviews {
if let cardView = view as? PlayingCardView {
if cardView.position.location == source.location
&& (view as! PlayingCardView).position.column == source.column
&& (view as! PlayingCardView).position.row >= source.row {
// Do stuff
}
}
}
Is one of these ways computationally faster?
I believe you're looking for the filter method.
if let card = (boardView.subviews as? [PlayingCardView])?.filter({
return $0.position.location == source.location
&& $0.position.column == source.column
&& $0.position.row >= source.row
}).first {
// Do stuff
print(card)
}
Or if you want to find the first card that satisfies your arguments, you can use the first method.
if let card = (boardView.subviews as? [PlayingCardView])?.first(where: {
return $0.position.location == source.location
&& $0.position.column == source.column
&& $0.position.row >= source.row
}) {
// Do stuff
print(card)
}
first(where: ) will give you the first element in the array that satisfies a condition (assuming that you only want to do it on one element since it's not "loopy"):
let view = boardView.subviews.first {
guard let cardView = $0 as? PlayingCardView else { return false }
return cardView.position.location == source.location
&& cardView.position.column == source.column
&& cardView.position.row >= source.row
}
// do stuffs to `view`
It will stop as soon as a match is found so that's about as efficient as you can get. In reality though, it hardly matters as you tend to have only a small number of subviews. Otherwise the GPU will give out first from having to render all of them.