I have a ViewController and its View :
class GraphicViewController: UIViewController {
var timerVC!:NSTimer
override func viewDidLoad() {
(view as GraphicView).timerV = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: Selector("doSomeWork"), userInfo: nil, repeats: true)
}
}
class GraphicView: UIView {
var timerV:NSTimer!
func doSomeWork(someParam:NSString)->Double {/*code*/}
}
It causes error about wrong Selector. I have 2 questions:
1) How to use Selector with arguments and returning from func?
2) How to choose the func from a view instead avoiding creating such func in VC?
The reason why the error is triggered is that you are not providing the right target. That's how the target-action paradigm works (or at least, how it works in Objective C, I really hope they will update the Swift APIs to take advantage of the whole functions-as-objects philosophy):
target: (id)target, selector:(Selector)selector somewhere in its signature. This should tell you that that API is planning to call a method, that you have to provide, on an object, that you also have to provide.selfas the target, you are telling the NSTimer to call a method named "doSomeWork" on your View Controller, which does not implement such method. No wonder it triggers an error about an unrecognized selector!
Furthermore, if you did pass the view as the target, that would have triggered an error nonetheless, as the correct selector associated with that method would be "doSomeWork:", as in Objective C method signatures.Here's the closest correct implementation:
class GraphicViewController: UIViewController {
var timerVC!:NSTimer
override func viewDidLoad() {
let gView = view as GraphicView
gView.timerV = NSTimer.scheduledTimerWithTimeInterval(interval, target: gView, selector: Selector("doSomeWork"), userInfo: nil, repeats: true)
}
}
class GraphicView: UIView {
var timerV:NSTimer!
func doSomeWork() {/*code*/}
}
As per the arguments and returning: according to the target-action paradigm, you are expected not to use any return value from the method you're calling. Target-action provides a mechanism for the target to communicate with the sender object (the NSTimer): If the selector you provide accepts no arguments (i. e. it does not end with a colon), the method is called without any argument; if the selector does expect an argument (i. e. it ends with a colon), the sender will call it passing itself as the only argument. Obviously, you would have to update the called method accordingly:
class GraphicView: UIView {
var timerV:NSTimer!
func doSomeWork(sender: NSTimer!) {
/* do some work involving sender */
}
}
Regarding the final question, if I understand it correctly you are asking for a way to avoid having to refer to the method defined inside GraphicView from inside UIViewController. That is actually possible, but this won't let you setup an NSTimer without using target-action (as of today). Sure, a more convenient way to implement the whole thing could be:
class GraphicViewController: UIViewController {
override func viewDidLoad() {
let gView = view as GraphicView
gView.setupTimer()
}
}
class GraphicView: UIView {
var timer: NSTimer!
func doSomeWork(sender: NSTimer!) {
/* code */
}
func setupTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: Selector("doSomeWork:"), userInfo: nil, repeats: true)
}
}
And in this implementation it makes sense to pass selfas the target, because the view is setting up the timer to call the method on itself. This way you could access the timer from inside the view controller by creating a temporary constant like above and then accessing its timer property.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With