Im looking for a solution in Swift.
I have a counter variable "counter" which adds 1 every time the func is called. Now with every new calling of the func, another variable should get called, like:
func questions() {
counter += 1
labelQuestion.text = question"counter" -> question0 -> question1 -> ...
}
How I can do that?!
Thanks already for answers!
May be use a didSet property observer on counter :
var counter: Int = 0 {
didSet {
labelQuestion.text = set the new question
}
}
func nextQuestion() {
counter += 1
}
Related
I'm learning closures... albeit slowly and I have come across the following example which provides an example of how closures 'close over' the variables/constants within their scope. I'll post the code first and ask my question after:
func countingClosure() -> () -> Int {
var counter = 0
let incrementCounter: () -> Int = {
counter += 1
return counter
}
return incrementCounter
}
let counter1 = countingClosure()
let counter2 = countingClosure()
counter1() // 1
counter2() // 1
counter1() // 2
counter1() // 3
counter2() // 2
I get the function block and can see how it returns the value it does. What is throwing me is how counter1 and counter2 retain their values, or, maybe, how the function retains the value (which I didn't think they could?). Surely every time counter1 or counter2 is called, counter resets to 0 as its declared inside the function?
I know it's right as the code compiles, I just can't understand how the Int value returned by counterClosure function is retained?
If you rename some of your names, perhaps it becomes a bit clearer:
func makeANewAutoIncrementingClosure() -> () -> Int {
// Initialize a variable, lives on stack initially but...
var counter = 0
let incrementingCounterClosure: () -> Int = {
// counter is captured and its storage is
// therefore moved from the stack to
// somewhere that hangs around as long as this function
// does
counter += 1
return counter
}
// Now we return this new closure, which is like
// [ function ]--attached to its closed over vars--> {counter: 0}
return incrementingCounterClosure
}
makeANewAutoIncrementingClosure // () -> () -> Int
// is a function that returns a closure that starts out closing over 0
let incr1 = makeANewAutoIncrementingClosure() // () -> Int
// a function that increments its captured int and returns it
let incr2 = makeANewAutoIncrementingClosure()
incr1() // 1 this does not call makeANewAutoIncrementingClosure, it calls the incr1 function (i.e. the incrementingCounterClosure). No assignments to 0 involved
incr2() // 1
incr1() // 2
incr1() // 3
incr2() // 2
I can't find a way to implement a wait function, I'm using swiftforwindows and no examples online have been able to solve it so far. It's Swift 4.2
The class is basically an array that when a function is called each index on the array gets a constant value deducted. the tick function is what is being called. I'm new to Swift.
class resProj {
var list = [1,1,1,1]
var projReq = [100,200,300,50]
var completed = false
func tick(){
for count in 0..<projReq.count{
if projReq[count] <= list[count]{
projReq[count] = 0
}
else if projReq[count] > list[count]{
projReq[count] -= list[count]
}
}
print(projReq)
}
init(
mathsP mathsIn: Int,
scienceP sciecnceIn: Int,
enginerP enginerIn: Int,
businessP businessIn: Int) {
self.list [0] = mathsIn
self.list [1] = sciecnceIn
self.list [2] = enginerIn
self.list [3] = businessIn
}
}
var spaceElev = resProj(
mathsP: 10,
scienceP: 20,
enginerP: 30,
businessP: 5)
var x = false
while x == false{
//wait function here pls//
print("tick", terminator:"?")
let y = readLine()
if y == "y"{
spaceElev.tick()
}
else{
print("gotta put y")
}
var templist = spaceElev.projReq
var templistcount = 0
templistcount = templist.count
for loop in 0..<templistcount{
if templist[loop] == 0{
templistcount -= 1
}
}
if templistcount == 0 {
x = true
print("project completed")
}
}
}
Where it says //wait function here pls// I would like to make the program wait for 1 second.
There are a lot of way to do this but most common way is create a completion function. For example:
func doSth(_ someParameter: String, _ completion: ()->()) {
print(someParameter)
// After your code is finish call completion
completion()
}
And when you call (there is two way to call):
doSth("Done") {
print("You can be sure that this block will work after your func finish")
}
or you can simply create another func and send it as a parameter.
You can also use DispatchQueue:
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
// put your func here...
}
You can simple use the UNIX-Functin func sleep(_: UInt32) -> UInt32.
In your case use sleep(1) to wait one second.
You could use Grand Central Dispatch or perform.
GCD solution:
let delayInSeconds = 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {
print("tick", terminator:"?")
}
If you want to learn more about Grand Central Dispatch (GCD) I suggest you read through this:
Grand Central Dispatch - Wikipedia
Grand Central Dispatch Tutorial - Ray Wenderlich
Perform solution:
Create a function like this:
#objc func delayedFunc() {
//write the code here that you want to execute with a one second delay
}
Then call this where you want the delayed function to execute:
let delayInSeconds = 1
perform(#selector(delayedFunc), with: nil, afterDelay: delayInSeconds)
You can use the RunLoop class:
func wait(for interval: TimeInterval) {
RunLoop.current.run(until: Date() + interval)
}
We came across this odd behaviour when using loops in a didSet. The idea was that we have a data type with a tree structure and in each element we wanted to store the level that item is on. So in the didSet of the level attribute we would also set the level attribute of the children. However we realised that this does only work if one uses forEach and not when using for .. in. Here a short example:
class Item {
var subItems: [Item] = []
var depthA: Int = 0 {
didSet {
for item in subItems {
item.depthA = depthA + 1
}
}
}
var depthB: Int = 0 {
didSet {
subItems.forEach({ $0.depthB = depthB + 1 })
}
}
init(depth: Int) {
self.depthA = 0
if depth > 0 {
for _ in 0 ..< 2 {
subItems.append(Item(depth: depth - 1))
}
}
}
func printDepths() {
print("\(depthA) - \(depthB)")
subItems.forEach({ $0.printDepths() })
}
}
let item = Item(depth: 3)
item.depthA = 0
item.depthB = 0
item.printDepths()
When I run this I get the following output:
0 - 0
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
1 - 1
0 - 2
0 - 3
0 - 3
0 - 2
0 - 3
0 - 3
It seems like it will not call the didSet of the subItems attribute when it's called from an for .. in loop. Does anyone know why this is the case?
UPDATE:
The problem is not that the didSet is not called from the init. We change the attribute afterwards (see last 4 lines of code) and only one of the two depth attribute will propagate the new value to the children
If you use defer, for updating any optional properties or further updating non-optional properties that you've already initialized and after you've called any super init methods, then your willSet, didSet, etc. will be called.
for item in subItems {
defer{
item.depthA = depthA + 1
}
}
When you use the forEach it makes kindOf "contract" with the elements and because it's an instance method unlike for .. in loop, it triggers the didSet of the variable. The above case applies where we use the loop, we have to trigger the didSet manually
This Solves the problem I think. Hope it helps!!
It seems that in the init, didSet is not called.
Tried this line on the Swift REPL
class A { var a: Int { didSet { print("A") } }; init() { a = 5 } }
Then called A()
didSet is NOT called
But
A().a = 7
Found that didSet is called
So, the solution is to make a function (prefered to be final), that makes your effect you need to put in didSet. Then call it both from didSet and from init. You can put this in your class.
final func updateA() {
// Do your "didSet" here
}
var a: Int {
didSet {
updateA()
}
}
init() {
a = 5
updateA()
}
So in your case:
func updateDepthA() {
for item in subItems {
item.depthA = depthA + 1
}
}
var depthA: Int = 0 {
didSet {
updateDepthA()
}
}
...
init(depth: Int) {
self.depthA = 0
updateDepthA()
...
This question already has answers here:
How replace position++ code to make it Swift 3 compatible?
(2 answers)
Closed 6 years ago.
i'm using Swift3. This syntax gives me an error:
func countingClosure() -> (() -> Int) {
var counter = 0
let incrementCounter: () -> Int = {
return counter+=1;
}
return incrementCounter
}
I cant increment using counter++, since its deprecated.
Is there a alegant way to deal with this, so the first value i return will be 0 ?
I "hacky" way will be to initiate counter = -1. And increment it a line before.
counter+=1;
return counter;
Thanks.
Edit:
I've tried to search StackOverflow for this error, and didn't find an answer. This question was marked as duplicate, but there was no way i could find the relevant/original question.
Just another way, use defer to increment counter after return
func countingClosure() -> (() -> Int) {
var counter = 0
let incrementCounter: () -> Int = {
defer {
counter += 1
}
return counter
}
return incrementCounter
}
Im attempting to make something similar to the high low game. So far i can get a new integer between 1-25 to be generated.
Ive tried to write a function that when you press the 'lower' button it checks the new integer against the previous one, and if it is less then text is displayed below saying 'You Were Correct' and updates the users score +1.. and if wrong 'You Were Wrong' displays instead and score is reset to 0.
Whenever i press the lower button it gives me a new int but score is not updated and the message doesn't display. This is my first real attempt at making this so bear with me :)
Lower Button
#IBAction func lower(sender: AnyObject) {
var newNumber = randomIntBetween(2, high: 26)
var oldNumber: Int?
func lowerNumber() -> Int {
if newNumber <= oldNumber {
correctWrong.text = "You Were Correct!"
score.text = "Score: \(countWin++)"
} else {
correctWrong.text = "You Were Wrong!"
score.text = "Score: \(countLose)"
}
return newNumber
}
randomNumbers.text = "\(newNumber)"
}
Random Number Function
func randomIntBetween(low:Int, high:Int) -> Int {
let range = high - (low - 1)
return (Int(arc4random()) % range) + (low - 1)
}
Variables/Constants Used
var countWin = 0
let countLose = 0
Thanks
As far I can see if your code it's formatted correctly you have a nested function but you aren't calling it inside your function so it's impossible that the function lowerNumber() will be called. You need to call the function like the following code:
#IBAction func lower(sender: AnyObject) {
var newNumber = randomIntBetween(2, high: 26)
var oldNumber: Int?
func lowerNumber() -> Int {
if newNumber <= oldNumber {
correctWrong.text = "You Were Correct!"
score.text = "Score: \(countWin++)"
} else {
correctWrong.text = "You Were Wrong!"
score.text = "Score: \(countLose)"
}
return newNumber
}
let newValue = lowerNumber()
randomNumbers.text = "\(newValue)"
}
Nevertheless, nested functions are closures that have a name and can capture values from their enclosing function so you need to be carefully with its use. I recommend this article of #AirSpeedVelocity to learn more about the closures and its capture values A Basic Tutorial on Functions and Closures in Swift
I hope this help you.