I had a function for setting up a timer, how can I perform an action every 3 times in each(1,2,3 and 4 = 1 ,5 = 2, 6 = 3) still 6 or more in for loop?
func duration(interval:Double,rep:Int){
let queue = DispatchQueue.main
let timer = DispatchSource.makeTimerSource(queue: queue)
var num = 0
for i in 0...12{
if (i%3 == 0){
perform something
num ++
}else if (i%2 == 0){
perform something
num ++
}else{
perform something
num ++
}
if num == 12{
timer.cancel()
}
}
}```
The SwiftUI way to do this is a Timer publisher.
Example:
struct ContentView: View {
#State private var number = 0
#State private var multiple: Int?
private let startDate = Date()
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack {
Text("Number: \(number)")
Text("Multiple: \(multiple != nil ? String(multiple!) : "no")")
}
.onReceive(timer) { date in
let interval = Int(date.timeIntervalSince(startDate))
print(interval)
number = interval
if interval.isMultiple(of: 3) {
// multiple of 3
multiple = 3
} else if interval.isMultiple(of: 2) {
// multiple of 2 (careful - 6 will be ignored because it is already a multiple of 3)
multiple = 2
} else {
// neither multiple of 2 or 3
multiple = nil
}
if interval == 12 {
timer.upstream.connect().cancel()
}
}
}
}
Result:
Related
I am new to Swift. I am trying to animate an object within a while loop, but the screen doesn't update until after the while loop is completed. If I run the function that the while loop is in by repeatedly clicking a button, it does work, but I want it run on its own. I have tried loading the screen every loop, but it didn't work.
func moveBall() {
while loopnumb <= 100 {
loopnumb += 1
print("animation loop cycled \(loopnumb) times")
print("inside of moveball() before startanimation, scratchX is \(scratchX), scratchY is \(scratchY), and the direction is \(direction).")
let animator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) {
self.direckEffeck()
self.scratch.frame = CGRect(x: self.scratchX, y: self.scratchY, width: 240, height: 128)
print("after and inside of animation command, scratchX is \(self.scratchX), scratchY is \(self.scratchY), and the direction is \(self.direction).")
}
animator.startAnimation()
if checkForOOBX() == true && checkForOOBY() == false {
direckCheckX()
}
if checkForOOBY() == true && checkForOOBX() == false {
direckCheckY()
}
if checkForOOBX() == true && checkForOOBY() == true {
if direction == 0 && done == false {
direction = 2
done = true
}
if direction == 1 && done == false {
direction = 3
done = true
}
if direction == 2 && done == false {
direction = 0
done = true
}
if direction == 3 && done == false {
direction = 1
done = true
}
cornerTouches += 1
print("ebic win, gamer's! the amount of corner touches is \(cornerTouches)")
}
done = false
DispatchQueue.main.async() {
self.scratch.setNeedsDisplay()
}
}
loopnumb = 0
}
Note that the object I am trying to move is called "scratch", and its coordinates are scratchX, scratchY. Thank you in advance.
I'm creating a game in Spritekit where a player has to collect coins. While collecting them, they have to avoid bombs. Basically, I want to increase the number of bombs that appear as the score gets higher... so I used an if statement - but it isn't working. The number of bombs that appear tends to stay the same:
if self.score >= 0 && self.score < 20 {
bombTimer = Timer.scheduledTimer(withTimeInterval: 4, repeats: true, block: { (timer) in
self.createBomb()
})
} else if self.score >= 20 && self.score < 35 {
bombTimer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block: { (timer) in
self.createBomb()
})
} else if self.score >= 35 && self.score < 50 {
bombTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true, block: { (timer) in
self.createBomb()
})
} else if self.score >= 50 && self.score < 150 {
bombTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in
self.createBomb()
})
}
If you are writing code inside of a SKScene class I like to use SKActions rather than NSTimers since the actions will pause when the scene is paused.
Paste this function inside your scene class somewhere.
Using a switch statement is nicer to use when working with a buttload of conditions
func getBombInterval() -> Double? {
switch true {
case score >= 0 && score < 20: return 4
case score < 35: return 3
case score < 50: return 2
case score < 150: return 1
default: return nil
}
}
Then replace the code you posted above with this:
guard let interval = getBombInterval() else { print("Score is less than 0") ; return }
let waitAction = SKAction.wait(forDuration: interval)
run(waitAction, completion: { self.createBomb() } )
I have made a mess of my translation from Obj-C to Swift so I'd really appreciate a refactor/code layout review. The curly braces are really throwing me. Are there any Xcode plugins or something to help me better manage my code blocks?
Some of my functions and calculations may not be so efficient as well so if you have any suggestions for those areas that would be great too. For example if you have used or seen better filter algorithms etc.
p.s. thanks Martin.
import UIKit
import Foundation
import AVFoundation
import CoreMedia
import CoreVideo
let minFramesForFilterToSettle = 10
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
let captureSession = AVCaptureSession()
// If we find a device we'll store it here for later use
var captureDevice : AVCaptureDevice?
var validFrameCounter: Int = 0
var detector: Detector!
var filter: Filter!
// var currentState = CurrentState.stateSampling // Is this initialized correctly?
override func viewDidLoad() {
super.viewDidLoad()
self.detector = Detector()
self.filter = Filter()
// startCameraCapture() // call to un-used function.
captureSession.sessionPreset = AVCaptureSessionPresetHigh
let devices = AVCaptureDevice.devices()
// Loop through all the capture devices on this phone
for device in devices {
// Make sure this particular device supports video
if (device.hasMediaType(AVMediaTypeVideo)) {
// Finally check the position and confirm we've got the back camera
if(device.position == AVCaptureDevicePosition.Front) {
captureDevice = device as? AVCaptureDevice
if captureDevice != nil {
//println("Capture device found")
beginSession()
}
}
}
}
} // end of viewDidLoad ???
// configure device for camera and focus mode // maybe not needed since we dont use focuc?
func configureDevice() {
if let device = captureDevice {
device.lockForConfiguration(nil)
//device.focusMode = .Locked
device.unlockForConfiguration()
}
}
// start capturing frames
func beginSession() {
// Create the AVCapture Session
configureDevice()
var err : NSError? = nil
captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &err))
if err != nil {
println("error: \(err?.localizedDescription)")
}
// Automatic Switch ON torch mode
if captureDevice!.hasTorch {
// lock your device for configuration
captureDevice!.lockForConfiguration(nil)
// check if your torchMode is on or off. If on turns it off otherwise turns it on
captureDevice!.torchMode = captureDevice!.torchActive ? AVCaptureTorchMode.Off : AVCaptureTorchMode.On
// sets the torch intensity to 100%
captureDevice!.setTorchModeOnWithLevel(1.0, error: nil)
// unlock your device
captureDevice!.unlockForConfiguration()
}
// Create a AVCaptureInput with the camera device
var deviceInput : AVCaptureInput = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &err) as! AVCaptureInput
if deviceInput == nil! {
println("error: \(err?.localizedDescription)")
}
// Set the output
var videoOutput : AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()
// create a queue to run the capture on
var captureQueue : dispatch_queue_t = dispatch_queue_create("captureQueue", nil)
// setup ourself up as the capture delegate
videoOutput.setSampleBufferDelegate(self, queue: captureQueue)
// configure the pixel format
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_32BGRA)] // kCVPixelBufferPixelFormatTypeKey is a CFString btw.
// set the minimum acceptable frame rate to 10 fps
captureDevice!.activeVideoMinFrameDuration = CMTimeMake(1, 10)
// and the size of the frames we want - we'll use the smallest frame size available
captureSession.sessionPreset = AVCaptureSessionPresetLow
// Add the input and output
captureSession.addInput(deviceInput)
captureSession.addOutput(videoOutput)
// Start the session
captureSession.startRunning()
// we're now sampling from the camera
enum CurrentState {
case statePaused
case stateSampling
}
var currentState = CurrentState.statePaused
func setState(state: CurrentState){
switch state
{
case .statePaused:
// what goes here? Something like this?
UIApplication.sharedApplication().idleTimerDisabled = false
case .stateSampling:
// what goes here? Something like this?
UIApplication.sharedApplication().idleTimerDisabled = true // singletons
}
}
// we're now sampling from the camera
currentState = CurrentState.stateSampling
// stop the app from sleeping
UIApplication.sharedApplication().idleTimerDisabled = true
// update our UI on a timer every 0.1 seconds
NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("update"), userInfo: nil, repeats: true)
func stopCameraCapture() {
captureSession.stopRunning()
captureSession = nil
}
// pragma mark Pause and Resume of detection
func pause() {
if currentState == CurrentState.statePaused {
return
}
// switch off the torch
if captureDevice!.isTorchModeSupported(AVCaptureTorchMode.On) {
captureDevice!.lockForConfiguration(nil)
captureDevice!.torchMode = AVCaptureTorchMode.Off
captureDevice!.unlockForConfiguration()
}
currentState = CurrentState.statePaused
// let the application go to sleep if the phone is idle
UIApplication.sharedApplication().idleTimerDisabled = false
}
func resume() {
if currentState != CurrentState.statePaused {
return
}
// switch on the torch
if captureDevice!.isTorchModeSupported(AVCaptureTorchMode.On) {
captureDevice!.lockForConfiguration(nil)
captureDevice!.torchMode = AVCaptureTorchMode.On
captureDevice!.unlockForConfiguration()
}
currentState = CurrentState.stateSampling
// stop the app from sleeping
UIApplication.sharedApplication().idleTimerDisabled = true
}
// beginning of paste
// r,g,b values are from 0 to 1 // h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
func RGBtoHSV(r : Float, g : Float, b : Float, inout h : Float, inout s : Float, inout v : Float) {
let rgbMin = min(r, g, b)
let rgbMax = max(r, g, b)
let delta = rgbMax - rgbMin
v = rgbMax
s = delta/rgbMax
h = Float(0.0)
// start of calculation
if (rgbMax != 0) {
s = delta / rgbMax
}
else{
// r = g = b = 0
s = 0
h = -1
return
}
if r == rgbMax {
h = (g - b) / delta
}
else if (g == rgbMax) {
h = 2 + (b - r ) / delta
}
else{
h = 4 + (r - g) / delta
h = 60
}
if (h < 0) {
h += 360
}
}
// process the frame of video
func captureOutput(captureOutput:AVCaptureOutput, didOutputSampleBuffer sampleBuffer:CMSampleBuffer, fromConnection connection:AVCaptureConnection) {
// if we're paused don't do anything
if currentState == CurrentState.statePaused {
// reset our frame counter
self.validFrameCounter = 0
return
}
// this is the image buffer
var cvimgRef:CVImageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)
// Lock the image buffer
CVPixelBufferLockBaseAddress(cvimgRef, 0)
// access the data
var width: size_t = CVPixelBufferGetWidth(cvimgRef)
var height:size_t = CVPixelBufferGetHeight(cvimgRef)
// get the raw image bytes
let buf = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(cvimgRef))
var bprow: size_t = CVPixelBufferGetBytesPerRow(cvimgRef)
var r = 0
var g = 0
var b = 0
for var y = 0; y < height; y++ {
for var x = 0; x < width * 4; x += 4 {
b+=buf[x](UnsafeMutablePointer(UInt8)) // fix
g+=buf[x + 1](UnsafeMutablePointer(Float)) // fix
r+=buf[x + 2](UnsafeMutablePointer(Int)) // fix
}
buf += bprow()
}
r /= 255 * (width*height)
g /= 255 * (width*height)
b /= 255 * (width*height)
}
// convert from rgb to hsv colourspace
var h = Float()
var s = Float()
var v = Float()
RGBtoHSV(r, g, b, &h, &s, &v)
// do a sanity check for blackness
if s > 0.5 && v > 0.5 {
// increment the valid frame count
validFrameCounter++
// filter the hue value - the filter is a simple band pass filter that removes any DC component and any high frequency noise
var filtered: Float = filter.processValue(h)
// have we collected enough frames for the filter to settle?
if validFrameCounter > minFramesForFilterToSettle {
// add the new value to the detector
detector.addNewValue(filtered, atTime: CACurrentMediaTime())
}
} else {
validFrameCounter = 0
// clear the detector - we only really need to do this once, just before we start adding valid samples
detector.reset()
}
}
You can actually do that
RGBtoHSV(r: r, g: g, b: b, h: &h, s: &s, v: &v)
i cant figure out this problem. Im deleting a certain SpriteNode, than re-adding it sometimes on a condition, however it crashes every time im calling addChild(). I know a SpriteNode can only have one parent so i dont understand this. Here is the relevant code:
override func touchesBegan(touches: NSSet, withEvent event:UIEvent) {
var touch: UITouch = touches.anyObject() as UITouch
var location = touch.locationInNode(self)
var node = self.nodeAtPoint(location)
for var i=0; i < tileNodeArray.count; i++
{
if (node == tileNodeArray[i]) {
flippedTilesCount++;
flippedTilesArray.append(tileNodeArray[i])
let removeAction = SKAction.removeFromParent()
tileNodeArray[i].runAction(removeAction)
if flippedTilesCount == 2
{
var helperNode1 = newMemoLabelNode("first",x: 0,y: 0,aka: "first")
var helperNode2 = newMemoLabelNode("second",x: 0,y: 0,aka: "second")
for var k = 0; k < labelNodeArray.count ;k++
{
if labelNodeArray[k].position == flippedTilesArray[0].position
{
helperNode1 = labelNodeArray[k]
}
if labelNodeArray[k].position == flippedTilesArray[1].position
{
helperNode2 = labelNodeArray[k]
}
}
if helperNode1.name == helperNode2.name
{
erasedTiles = erasedTiles + 2;
}
else
{
for var j = 0; j < flippedTilesArray.count ;j++
{
let waitAction = SKAction.waitForDuration(1.0)
flippedTilesArray[j].runAction(waitAction)
//self.addChild(flippedTilesArray[j]);
}
}
flippedTilesCount = 0;
flippedTilesArray = []
println("erased tiles:")
println(erasedTiles)
}
}
}
}
Appreciate your help!
I would recommend you not to use SKAction.removeFromParent but remove the node itself by calling:
tileNodeArray[i].removeFromParent()
instead of:
let removeAction = SKAction.removeFromParent()
tileNodeArray[i].runAction(removeAction)
The problem might be, that the SKActions don't wait for each other to finish. For example if you call the waitAction, the other actions will keep running.
I have a tic tac toe game here in Swift and I need a way to disable tap gesture recognition on spaces that have been played so that on their turn, the user cannot just tap places that have been played.
I have tried putting the line
ticTacImages[spot].removeGestureRecognizer(UITapGestureRecognizer(target: self, action: "imageClicked:"))
in my imageClicked and setImageForSpot functions and nothing happens. What am I doing wrong here?
The code involved:
for imageView in ticTacImages {
imageView.userInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "imageClicked:"))
}
}
//Gesture Reocgnizer method
func imageClicked(reco: UITapGestureRecognizer) {
var imageViewTapped = reco.view as UIImageView
println(plays[imageViewTapped.tag])
println(aiDeciding)
println(done)
opening1.hidden = true
opening2.hidden = true
opening3.hidden = true
if plays[imageViewTapped.tag] == nil && !aiDeciding && !done {
setImageForSpot(imageViewTapped.tag, player:.UserPlayer)
}
checkForWin()
let delay = 1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
//During delay
for imageView in ticTacImages {
imageView.userInteractionEnabled = false
}
dispatch_after(time, dispatch_get_main_queue(), {
//After delay
for imageView in self.ticTacImages {
imageView.userInteractionEnabled = true
}
self.aiTurn()
})
}
var varChanger: Int?
var playerMark: String?
func setImageForSpot(spot:Int,player:Player){
if varChanger == 1 {
playerMark = player == .UserPlayer ? "blue_x" : "blue_o"
}
else if varChanger == 2 {
playerMark = player == .UserPlayer ? "green_x" : "green_o"
}
else if varChanger == 3 {
playerMark = player == .UserPlayer ? "purple_x" : "purple_o"
}
else if varChanger == 4 {
playerMark = player == .UserPlayer ? "pink_x" : "pink_o"
}
else if varChanger == 5 {
playerMark = player == .UserPlayer ? "yellow_x" : "yellow_o"
}
else {
playerMark = player == .UserPlayer ? "red_x" : "red_o"
}
println("setting spot \(player.toRaw()) spot \(spot)")
plays[spot] = player.toRaw()
ticTacImages[spot].image = UIImage(named: playerMark)
}
You can only removeGestureRecognizer() for a gesture recognizer that has already been added. In your example you're creating a new one before removing it — instead you should keep track of the old one, or call ticTacImages[spot].gestureRecognizers to get an array of the ones which have been added.