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.
Related
I'm making an app which asks to keep touch 2 things at same time, but sometimes only one.
So my problem is: GetTouch(1) depends on GetTouch(0) and touchCount.
If I release 1 time the getTouch(0) : getTouch(1) will not be called anymore.
I don't know if I'm clear so here is my code:
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
}
if (touch.phase == TouchPhase.Ended)
{
}
if (Input.touchCount == 2)
{
Touch touch_ = Input.GetTouch(1);
if (touch_.phase == TouchPhase.Began)
{
}
if (touch_.phase == TouchPhase.Ended)
{
}
}
}
How can I do to still keep the Touch(1) even without Touch(0) Pressed please ? Just keep doing my stuff during "touch_.phase == TouchPhase.Began".
Thank you in advance.
you will only be able to access Input.GetTouch(1) when the touchCount is 2 or greater. You are probably looking for some sort of state variable
bool firstPress = false;
bool secondPress = false;
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
firstPress = true;
}
if (touch.phase == TouchPhase.Ended)
{
firstPress = false;
}
if (Input.touchCount == 2)
{
Touch touch_ = Input.GetTouch(1);
if (touch_.phase == TouchPhase.Began)
{
secondPress = true;
}
if (touch_.phase == TouchPhase.Ended)
{
secondPress = false;
}
}
}
if (firstPress == true && secondPress == false)
{
...
}
if (firstPress == true && secondPress == true)
{
...
}
While the GetTouch has no guarantee that the same index returns the same touch in the next frame what you can use use the Touch.fingerIndex which is unique for each touch.
So instead of using GetTouch you can access the touches via Input.touches and then try to filter out the one for your stored fingerId e.g. like
foreach (Touch touch in Input.touches)
{
if (touch.fingerIndex == storedFingerIndex)
{
// Do something
}
}
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 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 created AVAudioPlayer, Now I want to mute it when user click button.
Here what I have tried :
player.volume = 1.0 //when first time i initiate my player
- (IBAction)speakerOnOff:(id)sender {
if (player.volume == 1.0) {
[player setVolume: 0.0];
NSLog(#"1volume is:%f",player.volume);
}else if (player.volume == 0.0) {
[player setVolume: 1.0];
NSLog(#"2volume is:%f",player.volume);
}
}
if (player.volume = 1.0) and if (player.volume = 0.0) are erroneous at least on two levels. First, C is not Pascal - the = operator is an assignment, probably you meant if (player.volume == 1.0) instead.
Two, even this wouldn't be any good - comparing floating-point numbers does not do what you think it does. You better set a Boolean flag to indicate the state of the player (and omit the else if part since it's redundant):
- (IBAction)speakerOnOff:(id)sender
{
static BOOL muted = NO;
if (muted) {
[player setVolume:1.0];
} else {
[player setVolume:0.0];
}
muted = !muted;
}
swift 2+ solution
let musicPlayer:AVAudioPlayer = AVAudioPlayer()
var IsMuted:Bool = false
if IsMuted == false {
IsMuted = true
musicPlayer.volume = 0
} else {
IsMuted = false
musicPlayer.volume = 1
}
-> Don't forget to setCategory to AVAudioSessionCategoryAmbient
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
try AVAudioSession.sharedInstance().setActive(true)
} catch _ {
return print("couldn't set session")
}
I am using the accelerometer to scroll multiple subViews in a UIScrollVIew. I want the view (portrait orientation) to scroll to the right when the user flicks the iPhone to the right, and scroll to the left when the device is flicked to the left.
I thought I could do that just by noting positive or negative x acceleration values, but I see that the values are usually a mixture of positive and negative values. I have set the floor at 1.5g to eliminate non-shake movement, and am looking at the x values over the duration of .5 seconds.
I'm sure there is a trigonometrical method for determining the overall direction of a flick, and that you have to measure values over the duration of the flick motion. I'm also sure that someone has already figured this one out.
Any ideas out there?
Thanks
I developed a solution which gives me better feedback than the proposed solution (only for left and right shake).
The way I did it here is quite sensitive (recognizes a small shake), but sensitivity can be adapted by changing tresholdFirstMove and tresholdBackMove (increase for lower sensitivity)
In Swift :
(in your viewController. And add "import CoreMotion")
var startedLeftTilt = false
var startedRightTilt = false
var dateLastShake = NSDate(timeIntervalSinceNow: -2)
var dateStartedTilt = NSDate(timeIntervalSinceNow: -2)
var motionManager = CMMotionManager()
let tresholdFirstMove = 3.0
let tresholdBackMove = 0.5
override func viewDidLoad() {
// your code
motionManager.gyroUpdateInterval = 0.01
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
motionManager.startGyroUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler: { (gyroData, error) -> Void in
self.handleGyroData(gyroData.rotationRate)
})
}
private func handleGyroData(rotation: CMRotationRate) {
if fabs(rotation.z) > tresholdFirstMove && fabs(dateLastShake.timeIntervalSinceNow) > 0.3
{
if !startedRightTilt && !startedLeftTilt
{
dateStartedTilt = NSDate()
if (rotation.z > 0)
{
startedLeftTilt = true
startedRightTilt = false
}
else
{
startedRightTilt = true
startedLeftTilt = false
}
}
}
if fabs(dateStartedTilt.timeIntervalSinceNow) >= 0.3
{
startedRightTilt = false
startedLeftTilt = false
}
else
{
if (fabs(rotation.z) > tresholdBackMove)
{
if startedLeftTilt && rotation.z < 0
{
dateLastShake = NSDate()
startedRightTilt = false
startedLeftTilt = false
println("\\\n Shaked left\n/")
}
else if startedRightTilt && rotation.z > 0
{
dateLastShake = NSDate()
startedRightTilt = false
startedLeftTilt = false
println("\\\n Shaked right\n/")
}
}
}
}
OK, worked out a solution. When I detect a shake motion (acceleration greater than 1.5 on the x axis), I start a timer and set a BOOL to true. While the BOOL is true I add acceleration values. When the timer expires, I stop adding acceleration values and determine direction of the shake by the sign of the total acceleration.
- (void)accelerometer:(UIAccelerometer *)acel didAccelerate:(UIAcceleration *)aceler {
if (fabsf(aceler.x) > 1.5)
{
shake = YES;
NSTimeInterval myInterval = .75;
[NSTimer scheduledTimerWithTimeInterval:myInterval target:self selector:#selector(endShake) userInfo:nil repeats:NO];
return;
}
if(shake)
{
totalG += aceler.x;
}
}
- (void) endShake {
shake = NO;
int direction;
if (totalG isLessThan 0) direction = 1;
if(totalG isGreaterThan 0) direction = -1;
[self changePageByShake:direction];
totalG = 0;
}
Note: I couldn't get the < and > symbols to format correctly in the codeblock above, so I substituted isLessThan and isGreaterThan for the symbols.