New to Swift here- I'm trying to achieve a simple segue operation after predefined times lapses. But for some reason my Xcode isn't recognizing the Timer and gives error "Timer module has no member named scheduledTimer". I couldn't find help anywhere.
CODE:
import UIKit
class ViewController: UIViewController {
let emptystring = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var time = Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(changeview), userInfo: nil, repeats: false)
}
func changeview (){
self.performSegueWithIdentifier("GoToMain", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "GoToMain"){
let destination = (segue.destinationViewController as! UINavigationController).viewControllers[0] as! SecondView
destination.emptyString = emptystring
}
print("Segue Performed")
}
}
Picture also shows my entire code, including the error. NOTE: It's not my own code. I only followed the answer to question in following link:
Xcode Swift 3: Timer and Segue View Controller Error
Timer is a Swift 3 type, but judging from the rest of your method signatures, you appear to be using Swift 2. Use NSTimer in Swift 2.
Also, for future reference, in addition to the timeInterval typo (which you've now fixed), the third parameter is selector, not selecter.
So, in Swift 2:
NSTimer.scheduledTimerWithTimeInterval(8.0, target: self, selector: #selector(changeview), userInfo: nil, repeats: false)
Or in Swift 3:
Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(changeview), userInfo: nil, repeats: false)
You have declared the first parameter as timerInterval but it should be timeInterval instead.
So change:
var time = Timer.scheduledTimer(timerInterval: 8.0, target: self, selecter: #selector(changeview), userInfo: nil, repeats: false
To:
var time = Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(changeview), userInfo: nil, repeats: false)
Also note that with Swift 4, you have to add #objc to the selector you call after the timer expires, because the code that calls this selector is apparently still written in Objective-C.
So change:
func changeview()
to this:
#objc func changeview()
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 Button in VC in storyboard and I am able to use outlet of it and it is working fine. when it comes to action, it is not working.
find the images for reference.
#IBAction func actionStart(_ sender: Any) {
if (isStarted == false)
{
isStarted = true
if CLLocationManager.locationServicesEnabled()
{
self.tripTimer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(LandingViewController.displayTripTimer), userInfo: nil, repeats: true)
locationManager.startUpdatingLocation()
}
}
}
View Hierarchy:
IBOutlet Connection:
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've seen this a couple of times before, but it never occured to me what might be wrong.
Firstly, I want to create the effect of scrambling numbers like they do in those hacking scenes in movies. So, I made an NSTimer to make my delays such that every 0.2 seconds, the numbers change. Then, I made another timer to tell my first timer to
invalidate()
after two seconds. My code is as follows:
import UIKit
class MainPage: UIViewController {
#IBOutlet var genericDeviceName: UITextField!
#IBOutlet var hackButton: UIButton!
#IBOutlet var rightNumber: UILabel!
#IBOutlet var leftNumber: UILabel!
#IBOutlet var detectionText: UILabel!
#IBAction func deviceNameEnter(sender: AnyObject) {
detectionText.text = "Device detected: " + genericDeviceName.text!
if genericDeviceName.text == "" {
detectionText.text = "Error"
}
hackButton.alpha = 1
}
#IBAction func hackDevice(sender: AnyObject) {
var tries = 0
var timer = NSTimer()
var timerStop = NSTimer()
timer = NSTimer (timeInterval: 0.2, target: self, selector: "update", userInfo: nil, repeats: true)
timerStop = NSTimer (timeInterval: 2, target: self, selector: "endTimer", userInfo: nil, repeats: true)
let diceRoll = Int(arc4random_uniform(9) + 1)
let diceRollSecond = Int(arc4random_uniform(9) + 1)
UIView.animateWithDuration(0.25, animations:{
self.hackButton.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))})
func update() {leftNumber.text = String(diceRoll)
rightNumber.text = String(diceRoll)
print("it worked!")}
func endTimer() {
timer.invalidate()
detectionText.text = "Access Granted!"
timerStop.invalidate()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
}
So... what went wrong? The last few times I tried using NSTimers, they didn't work either. Is my concept of an NSTimer wrong? Or is there an error in my code? There was no error message triggered, it was just that the timer did not trigger and the numbers did not change. Not even "it worked!" was printed to the logs. Please help by suggesting some code. Thank you in advance!
UPDATE
I've updated my code. Here it is:
import UIKit
class MainPage: UIViewController {
#IBOutlet var genericDeviceName: UITextField!
#IBOutlet var hackButton: UIButton!
#IBOutlet var rightNumber: UILabel!
#IBOutlet var leftNumber: UILabel!
#IBOutlet var detectionText: UILabel!
#IBAction func deviceNameEnter(sender: AnyObject) {
detectionText.text = "Device detected: " + genericDeviceName.text!
if genericDeviceName.text == "" {detectionText.text = "Error"}
hackButton.alpha = 1
}
let diceRoll = Int(arc4random_uniform(9) + 1)
let diceRollSecond = Int(arc4random_uniform(9) + 1)
func update(timer: NSTimer) {leftNumber.text = String(diceRoll)
rightNumber.text = String(diceRoll)
print("it worked!")}
func endTimer(timerStop: NSTimer) {
timer.invalidate()
detectionText.text = "Access Granted!"
timerStop.invalidate()}
#IBAction func hackDevice(sender: AnyObject) {
var timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: "update:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
var timerStop = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "endTimer:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timerStop, forMode: NSRunLoopCommonModes)
UIView.animateWithDuration(0.25, animations:{
self.hackButton.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))})
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
Currently, it seems that the function "endTimer" does not work, due to the variable "timer" not being recognised. Please help. Thank you all so much for your time!
A few things: The selector for an NSTimer should end in a colon (e.g. "update:" or "endTimer:" And the function should take a single parameter: An NSTimer.
Second, the function that the timer calls must be a top-level function of the target. Your update method is a local function of your hackDevice, function, which won't work.
Third, you need to use scheduledTimerWithTimeInterval, as in ShahiM's answer:
var timer = NSTimer.scheduledTimerWithTimeInterval(
0.4,
target: self,
selector: "update:",
userInfo: nil,
repeats: true)
That code crashes if the function in your selector is a nested function because it's not visible to the timer.
Finally, it looks like you need to move the variables diceRoll and diceRollSecond out of your hackDevice function and make them instance variables of your class.
You should move your functions out of hackDevice. Nested functions like this are generally not used in Swift.
For example:
let diceRoll = Int(arc4random_uniform(9) + 1)
let diceRollSecond = Int(arc4random_uniform(9) + 1)
var timer = NSTimer()
#IBAction func hackDevice(sender: AnyObject) {
var tries = 0
var timer = NSTimer()
var timerStop = NSTimer()
timer = NSTimer (timeInterval: 0.2, target: self, selector: "update", userInfo: nil, repeats: true)
timerStop = NSTimer (timeInterval: 2, target: self, selector: "endTimer", userInfo: nil, repeats: true)
UIView.animateWithDuration(0.25, animations:{
self.hackButton.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))})
}
func update() {
leftNumber.text = String(diceRoll)
rightNumber.text = String(diceRoll)
print("it worked!")
}
func endTimer() {
timer.invalidate()
detectionText.text = "Access Granted!"
timerStop.invalidate()
}
Try using this :
var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: "update", userInfo: nil, repeats: true)
Also move your update and endTimer methods outside the hackDevice method.
Explanation :
From Apple docs :
Use the timerWithTimeInterval:invocation:repeats: or timerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer object without scheduling it on a run loop. (After creating it, you must add the timer to a run loop manually by calling the addTimer:forMode: method of the corresponding NSRunLoop object.)
.
Use the scheduledTimerWithTimeInterval:invocation:repeats: or scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: class method to create the timer and schedule it on the current run loop in the default mode.
So in your code, you only create the timer but it does not start running. You have to either call the addTimer(_ timer: NSTimer,forMode mode: String) to start the timer or you can simply use scheduledTimerWithTimeInterval to launch the timer right away.
You don't nest this kind of function, selector will not find them because they will be exposed after the method exit, after the function leave the last } there will be no a update and endTiemr
Your timer should look like this let timer = NSTimer(timeInterval: 0.2, target: self, selector: Selector("update:"), userInfo: nil, repeats: true)
and on the other side func update(timer: NSTimer) {
Also try adding the timer to the run loop after initialisation:
let timer = NSTimer(timeInterval: 0.2, target: self, selector: Selector("update:"), userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
In your update you redeclared the variable timer, this way you created a local variable which exists just in the method hackDevice: , remove the var before the timer = NSTimer.scheduledTimer...
Edit:
I rather edit this answer, because here i can add insert code snippet with proper newlines and indents:
class MainPage: UIViewController{
// Your IBOutlets
#IBOutlet var ...
var timer= NSTimter()
// Your methods
}
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
}