I'm working on a simple card game, in which after a player presses a button, three AI computers will take their turns one after another. However, I need there to be a pause between each turn.
This is what I need:
playerButton > PAUSE > computer1Goes > PAUSE > computer2Goes > PAUSE > computer3Goes
Code:
#IBAction func placeCardAction(sender: UIButton) {
// playerButton does this action
var playerCardOnTop = game!.player.deck.placeCard()
middleDeck.addSingleCard(playerCardOnTop)
updateCardCount()
// Start computer actions
let delay = 2.0 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
self.game?.computer1PlacesCard(&self.middleDeck)
self.updateCardCount()
}
let delay2 = 2.0 * Double(NSEC_PER_SEC)
let time2 = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time2, dispatch_get_main_queue()) {
self.game?.computer2PlacesCard(&self.middleDeck)
self.updateCardCount()
}
let delay3 = 2.0 * Double(NSEC_PER_SEC)
let time3 = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time3, dispatch_get_main_queue()) {
self.game?.computer3PlacesCard(&self.middleDeck)
self.updateCardCount()
}
}
Unfortunately, all the delays start/end at the same time, so what ends up happening is that all of the computer functions run at the same time instead of taking turns, one after another.
If anyone can help solve this problem, I would appreciate it!
Easiest solution.... change delay2 to 4 and delay3 to 6. As it stands right now, of course they all go off at the same time, the all have the same delay.
Alternatively, stack them like:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay))) {
// step one
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay))) {
// step two
...
}
}
I suggest using array of Computer objects which can perform placeCard function
#IBAction func placeCardAction(sender: UIButton) {
// playerButton does this action
var playerCardOnTop = game!.player.deck.placeCard()
middleDeck.addSingleCard(playerCardOnTop)
updateCardCount()
self.computersPlaceCards(0)
}
private func computersPlaceCards(i: Int) {
if self.game == nil || i >= self.game!.computers.count {
return
}
let delay = 2.0 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
self.game?.computers[i].placeCards(&self.middleDeck)
self.updateCardCount()
self.computersPlaceCards(i+1)
}
}
You can use sleep() inside a dispatch_async:
dispatch_async( dispatch_get_global_queue( QOS_CLASS_USER_INTERACTIVE, 0 ) ) {
dispatch_async(dispatch_get_main_queue()) {
print("first")
}
sleep(1)
dispatch_async(dispatch_get_main_queue()) {
print("second")
}
sleep(1)
dispatch_async(dispatch_get_main_queue()) {
print("third")
}
sleep(1)
dispatch_async(dispatch_get_main_queue()) {
print("fourth")
}
}
(My answer previously used NSOperationQueue--either will work)
Related
Is there a way to run the code simultaneously on different devices? Let's say I want that when I click on a button on one of the devices, the function starts simultaneously on both the first and the second device? I tried to use a timer with a time check for 3 seconds ahead, but the function is triggered with a delay of 0.5 seconds on the second device
func getEventTime() -> UInt64{
let now = Date()
let interval = now.timeIntervalSince1970
let result = (UInt64(interval) + (3)) * 1000
return result
}
func getCurrentTime() -> UInt64{
let now = Date()
let interval = now.timeIntervalSince1970
let result = UInt64(interval * 1000)
return result
}
func startTimer(time : UInt64){
Timer.scheduledTimer(withTimeInterval: 0.0001, repeats: true) { timer in
switch getCurrentTime() {
case time - 1000 :
DispatchQueue.main.async {
countdownImageTimer.image = UIImage(named: "Start")
}
break
case time :
DispatchQueue.main.async {
countdownImageTimer.removeFromSuperview()
}
self.setShips()
timer.invalidate()
break
default:
break
}
}
}
Simply, I have a button that adds to a queue, or I believe that's how it works - I've only just started to use GCD. If I press the button twice in my app, it runs these two processes in parallel and the progress bar races back and forth between them, I'd like to stop all the other instances of the queue and execute a new one if that makes sense, I've not got to grips with the lingo...
#IBAction func makePi(_ sender: UIButton) {
piProgress.progress = 0.0
let queue1 = DispatchQueue(label: "com.appcoda.queue1", qos: DispatchQoS.userInitiated)
queue1.async {
let currentAmount = Int(self.randNum.value)
var gcdCounter = 0
for n in 1...currentAmount {
if self.gcd(self.random(max: Int(self.randRange.value)), self.random(max: Int(self.randRange.value))) == 1{
gcdCounter += 1
}
if n % 1000 == 0 {
DispatchQueue.main.async {
self.piProgress.setProgress(Float(Double(n)/Double(currentAmount)), animated: true)
}
} else if n == currentAmount {
DispatchQueue.main.async {
self.piProgress.setProgress(1.0, animated: true)
}
}
}
let piFinal = sqrt(Double((6/Double((Double(gcdCounter)/Double(currentAmount))))))
let roundedAccuracy: Double = ((piFinal-Double.pi)/Double.pi*100).roundTo(places: 6)
DispatchQueue.main.async {
self.piLabel.text = String(piFinal)
self.piAccuracy.text = "Accuracy: \(roundedAccuracy)%"
}
}
}
I just need all the other queue1.async ones to stop...
P.s. If I've done anything obviously wrong, please help ^^ Thanks.
So basically I'm trying to print the word "yo" 20 times with a 2 second time delay between each iteration. This is what I came up with which doesn't work
var j = 0
while(j < 20){
print("yo")
let seconds = 2.0
let delay = seconds * Double(NSEC_PER_SEC)//nanoseconds per seconds
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) {
j+=1
}
}
Who knows the right way to go about this? Thanks in advance.
Try this. It creates 20 print yo closures at one time instead of serially delaying between each one.
let delay = 2.0 * Double(NSEC_PER_SEC)
(1...20).map {
iteration in
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(iteration)))
dispatch_after(time, dispatch_get_main_queue()) {
print("yo")
}
}
Your code is close. You need to put the print statement inside the dispatch_after:
var j: UInt64 = 0
let seconds: UInt64 = 1
while(j < 10)
{
let delay = seconds * j * NSEC_PER_SEC //nanoseconds per seconds
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue())
{
print("yo")
}
j += 1
}
print("Should start \"yo'ing\" soon")
Also your math was off. the delay value to dispatch_time is a UInt64, not a double.
Note that the code above probably won't work in a playground, since as soon as the main code path finishes, it terminates.
You may try this function
func repeatedPrint(count: Int, withDelay delay: Double)
{
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue())
{
if count < 1 {return}
print("yo")
self.repeatedPrint(count - 1, delay: delay)
}
}
repeatedPrint(20, delay: 2)
I try to use dispatch_time to periodically call doTask() recursively with a interval of 600 seconds. Here is my simple code:
private func doTask() -> Void {
var interval : NSTimeInterval = someService.getInterval() //it is 600
NSLog("interval = \(interval)"); //Like I said, it is 600, because I see the log
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC)))
//recursively call doTask() after 600 seconds
dispatch_after(dispatchTime,
GlobalBackgroundQueue,
{self.doTask()}
)
}
Here is the GlobalBackgroundQueue:
var GlobalBackgroundQueue: dispatch_queue_t {
return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
}
But when I call the doTask() function, at runtime, the interval between each call on doTask() is 15 seconds. Why? Why the interval is not 600 seconds?
=====UPDATE=====
I also tried NSTimer:
NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: "doTask", userInfo: nil, repeats: true)
But at runtime, the doTask() get called every 20 seconds. Still not 600 seconds.
try this
let delay: UInt64 = 600
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * NSEC_PER_SEC))
This works:
let delay: NSTimeInterval = 600
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) * delay))
dispatch_after(time, dispatch_get_main_queue(), { () -> Void in
})
I've got a Collection View and have arranged it in such a way that there is a single column on one side of the screen and content that is replaced based on what collection item is in focus.
I'd like to be able to on swap the content out if focus on an item has been held for more then .5 seconds.
Here is what I currently have, and it swaps the data out instantly.
if self.focused {
self.label.alpha = 1
self.priceLabel.alpha = 1
if self.representedDataItem?.imageUrl! == "https://s3-us-west-2.amazonaws.com/random/image.png" ||
self.representedDataItem?.imageUrl! == "" {
self.backgroundImage.image = UIImage(named: "titleImage")
}
else {
ImageCache.sharedLoader.imageForUrl((self.representedDataItem?.imageUrl!)!, completionHandler:{(image: UIImage?, url: String) in
self.backgroundImage.image = image!
})
}
}
else {
self.label.alpha = 0.2
self.priceLabel.alpha = 0.2
}
Don't know the self.focused how to determine, assume you already took care of it. Therefore, you could use GCD to delay the execution while focused.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// code to be executed after 0.5 sec
}
if self.focused {
self.label.alpha = 1
self.priceLabel.alpha = 1
}
else {
self.label.alpha = 0.2
self.priceLabel.alpha = 0.2
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// code to be executed after 0.5 sec
if self.focused {
if self.representedDataItem?.imageUrl! == "https://s3-us-west-2.amazonaws.com/random/image.png" ||
self.representedDataItem?.imageUrl! == "" {
self.backgroundImage.image = UIImage(named: "titleImage")
}
else {
ImageCache.sharedLoader.imageForUrl((self.representedDataItem?.imageUrl!)!, completionHandler:{(image: UIImage?, url: String) in
self.backgroundImage.image = image!
})
}
}
}