So I have a label that counts 1 every second using an NSTimer. I made an app that has a few moving UIImages. However every time the timer counts one up, the view seemingly reloads and the UIImages go back to their original positions. Furthermore, the UIImages are not where I had placed them in the storyboard (I placed them outside the view so they could move inwards, but when I start the app it just shows them right there already. They move but only for one second then go back to their original positions). Same code works fine on the iPhone but doesn't work on an iPad. I think it has something to do with the constraints because the code is the same. Here's the timer code:
func counting() {
timerCount = timerCount + 1
timerLabel.text = "\(timerCount)"
}
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("counting"), userInfo: nil, repeats: true)
// Do any additional setup after loading the view, typically from a override nib.
}
Here's the code for moving my UIImages:
func MoveWalls() {
FirstTimer = NSTimer.scheduledTimerWithTimeInterval(0.07, target: self, selector: Selector("FirstMoving"), userInfo: nil, repeats: true)
SecondTimer = NSTimer.scheduledTimerWithTimeInterval(0.007, target: self, selector:
Selector("SecondMoving"), userInfo: nil, repeats:true)
ThirdTimer = NSTimer.scheduledTimerWithTimeInterval(0.006, target: self, selector:
Selector("ThirdMoving"), userInfo: nil, repeats:true)
FourthTimer = NSTimer.scheduledTimerWithTimeInterval(0.005, target: self, selector:
Selector("FourthMoving"), userInfo: nil, repeats:true)
}
func FirstMoving() {
First.center = CGPointMake(First.center.x + 1, First.center.y)
}
func SecondMoving() {
Second.center = CGPointMake(Second.center.x - 1, Second.center.y)
}
func ThirdMoving() {
ThirdMoving.center = CGPointMake(Third.center.x, Third.center.y + 1)
}
func FourthMoving() {
Fourth.center = CGPointMake(Fourth.center.x, Fourth.center.y 1)
}
My constraints:
Two buttons that start and end the game (Centered horizontally).
The four UIImages (size ratio)
A timer (Top left) which for some reason resets the view with each count.
I had the exact same problem. I solved it by adding this code for each of the moveable views:
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.translatesAutoresizingMaskIntoConstraints = true
self.imageView2.translatesAutoresizingMaskIntoConstraints = true
}
Related
I want to use below lines but where should I write it?
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(function), userInfo: nil, repeats: true)
It depends what you want to achieve. If you want that slider automatically start when app loads, then put it in viewDidLoad().
If you want to start slider when you press some button, then you need to put this code inside the #IBAction function for that button.
Note: #objc function which is determining work of the slider is written separately, like in the example above.
var i=Int()
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(imageChange), userInfo: nil, repeats: true)
// Do any additional setup after loading the view.
}
#objc func imageChange(){
self.imageView.image=images[i]
if i<images.count-1{
i+=1
}
else{
i=0
}
}
I have the following code to animate the typewriter text in UITextView.
The problem is after the animation starts from the beginning character by character, I want it able to show full text after a button is pressed.
What is the best way to show up the full text after animation starts?
var myText = Array("".characters)
var myCounter = 0
var timer:Timer?
var TextNumber = 0
func fireTimer(){
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(Main1.typeLetter), userInfo: nil, repeats: true)
}
func typeLetter(){
if myCounter < myText.count {
myTypeWriter.text = myTypeWriter.text! + String(myText[myCounter])
let randomInterval = Double((arc4random_uniform(8)+1))/190
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: randomInterval, target: self, selector: #selector(Main1.typeLetter), userInfo: nil, repeats: false)
} else {
timer?.invalidate()
}
myCounter += 1
}
#IBAction func showFullText(){
//What should i put here?
}
You can do something like this, i.e invalidate the time and convert array into string and place this text
#IBAction func showFullText(){
timer?.invalidate()
let strFromArray = myText.joinWithSeparator(" ")
//In Swift 3, joinWithSeparator becomes joined(separator:)
myTypeWriter.text = strFromArray
}
I'm struggling with the following piece of code:
backgroundTimer = NSTimer.scheduledTimerWithTimeInterval(3, target: gvc, selector: #selector(GameViewController.addNext), userInfo: nil, repeats: true)
I can't seem to get the #selector working. This code results to:
Type 'GameViewController' has no member 'addNext'
Even though the member is right there... Here's the full code:
class GameViewController: GAITrackedViewController, AVAudioPlayerDelegate {
var sceneCanvas: SKSpriteNode?
override func viewDidLoad() {
skView.presentScene(WelcomeScene(size: view.bounds.size, gvc: self))
}
func createBackground(boundsSize: CGRect, gvc: GameViewController) -> SKSpriteNode {
addUILabels(gvc)
return sceneCanvas!
}
func addUILabels(gvc: GameViewController) {
backgroundTimer = NSTimer.scheduledTimerWithTimeInterval(3, target: gvc, selector: #selector(GameViewController.addNext), userInfo: nil, repeats: true)
}
public func addNext() {
let backgroundLabel = SKSpriteNode(imageNamed: "label1.png")
sceneCanvas!.addChild(backgroundLabel!)
backgroundLabel?.runAction(SKAction.moveByX(-screenSize.width , y: 0, duration: 12))
}
}
class WelcomeScene: SKScene {
init(size: CGSize, gvc: GameViewController){
super.init ()
let bounds = CGRect(origin: CGPoint(x: 0,y: 0), size: size)
sceneCanvas = createBackground(bounds, gvc: gvc)
self.addChild(sceneCanvas!)
}
}
I was running into this issue with UILongPressGestureRecognizer and Swift 3.
I had my method in an extension for a long press on a table view cell:
// Long press table view extension
extension MyViewController: UIGestureRecognizerDelegate {
public func handleLongPress(longPressGesture: UILongPressGestureRecognizer) {
let p = longPressGesture.location(in: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: p)
if indexPath == nil {
print("Long press on table view, not row.")
}
else if (longPressGesture.state == UIGestureRecognizerState.began) {
print("Long press on row, at \(indexPath!.row)")
}
}
}
Then, in my viewDidLoad():
// Configure long press on tableviewcell
let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(MyViewController.handleLongPress))
longPressGesture.minimumPressDuration = 1.0 // 1 second press
longPressGesture.delegate = self
self.tableView.addGestureRecognizer(longPressGesture)
For me, the issue was fixed by using this syntax for the selector:
action: #selector(MyViewController.handleLongPress)
I tried the following alternatives without any luck. These do not work:
action: #selector(self.handleLongPress)
action: #selector(MyViewController.handleLongPress(_:))
action: #selector(self.handleLongPress(_:))
action: #selector("handleLongPress(_:)")
Additionally, even though I don't seem to be passing any arguments to MyViewController.handleLongPress, the selected row prints out to the console just fine on long press selection:
Long press on row, at 5
backgroundTimer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("addNext"), userInfo: nil, repeats: true)
Probably your code is in different modules. If so you should make your func addNext() public:
public func addNext() {
...
}
Have you tried this.
backgroundTimer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: #selector(GameViewController.addNext), userInfo: nil, repeats: true)
or
backgroundTimer = NSTimer.scheduledTimerWithTimeInterval(3, target: gvc, selector: #selector(gvc.addNext), userInfo: nil, repeats: true)
Turns out that two things were the problem. 1) I had defined the method outside the class, 2) However also the following syntax was needed for it work:
selector: #selector(addNext as () -> ())
i want to set timer and change the background color every 5 seconds.
I wrote code of random color and its work perfect , but i tried to put the function in NSTimer and i getting crush.
2016-03-05 14:46:48.774 Boo Adventure[6782:365555] *** Terminating app
due to uncaught exception 'NSInvalidArgumentException', reason:
'-[Boo_Adventure.GameScene update]: unrecognized selector sent to
instance 0x7fe2cb741ac0'
Game Scene :
extension CGFloat {
static func random() -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UInt32.max)
}
}
extension UIColor {
static func randomColor() -> UIColor {
let r = CGFloat.random()
let g = CGFloat.random()
let b = CGFloat.random()
// If you wanted a random alpha, just create another
// random number for that too.
return UIColor(red: r, green: g, blue: b, alpha: 2.5)
}
}
override func didMoveToView(view: SKView) {
Timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update", userInfo: nil, repeats: false)
func update() {
self.view!.backgroundColor = UIColor.randomColor()
}
}
Thanks!
Few issues here:
1) You want to change color every 5 seconds, but you set repeats parameter to false, so your custom update() method will be executed only once. Change repeats to true.
2) You are trying to change the background color of a view (SKView) instead of a background color of the scene.
Here is how you can do it with NSTimer:
override func didMoveToView(view: SKView) {
let timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "update", userInfo: nil, repeats: true)
}
func update(){
backgroundColor = UIColor.randomColor()
}
But NSTimer is not affected by the scene's or view's paused state, so it can take you into troubles in certain situations. To avoid this, you can use SKAction. The same thing done with SKAction looks like this:
override func didMoveToView(view: SKView) {
let wait = SKAction.waitForDuration(5)
let block = SKAction.runBlock({
[unowned self] in
self.backgroundColor = UIColor.randomColor()
})
let sequence = SKAction.sequence([wait,block])
runAction(SKAction.repeatActionForever(sequence), withKey: "colorizing")
}
This way, if you pause your scene or a view, colorizing action will be paused automatically (and unpaused when scene/view are unpaused).
your function update looks like it's in a closure for the timer - but it's not. take the function out so that it looks like this
override func didMoveToView(view: SKView) {
Timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update", userInfo: nil, repeats: false)
}
func update() {
self.view!.backgroundColor = UIColor.randomColor()
}
Don't know if this fixes the problem but according to the NSTimer Class Reference the selector should have the signature:
timerFireMethod:
Try updating your update function signature to
func update(timer: NSTimer)
And update this line:
Timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update", userInfo: nil, repeats: false)
to:
Timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update:", userInfo: nil, repeats: false)
So I've got two timers, one that increases the score and one that spawns enemies. I used a notification to invalidate the timers, and then I'm using another one to recreate the timers. When I quit and then open the app, there are two sets of enemies being spawned on top of each other. I think timerRecreate = true and also the regular timers in GameScene are also being called.
GameViewController.swift file:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseTimers:"), name:UIApplicationWillResignActiveNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("startTimers:"), name:UIApplicationDidBecomeActiveNotification, object: nil)
}
func pauseTimers(notification : NSNotification) {
println("Observer method called")
timer.invalidate()
scoretimer.invalidate()
}
func startTimers(notification : NSNotification) {
println("Observer method called")
timerRecreate = true
}
Code for timers in GameScene.swift
override func didMoveToView(view: SKView) {
//Spawn timer for enemy blocks
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("spawnEnemies"), userInfo: nil, repeats: true)
//Timer for keeping score
scoretimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("scoreCounter"), userInfo: nil, repeats: true)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if timerRecreate == true {
//Spawn timer for enemy blocks
timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("spawnEnemies"), userInfo: nil, repeats: true)
//Timer for keeping score
scoretimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("scoreCounter"), userInfo: nil, repeats: true)
timerRecreate = false
}
}
I think the problem is when you initially open the app, be that after quitting out of it or opening it for the first time, timerRecreate is set to true as well as the regular spawning of blocks so two sets of blocks are spawned at the same time. How can I fix this?
Fixed it! I hope...
Anyway heres what I did. I created another boolean called timerz and set it to false when the DidBecomeActive notification was made. At the same time, timeRecreate is set to true. This guarantees that both timer sets arent running at the same time. In the update function inside the if statement on whether timRecreate was true, I set timeRecreate to false and timerz to true. So the timers are recreated and then it switches back to to old way of spawning them. I also put this old method of spawning them inside an if statement on whether timerz was true or false.