ViewControl外部的Swift选择器

时间:2014-10-22 16:21:27

标签: ios swift

我有一个ViewController及其视图:

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*/}
}

它会导致错误的选择器错误。我有两个问题:

1)如何使用带有参数的Selector并从func返回?

2)如何从视图中选择func而不是在VC中创建这样的func?

1 个答案:

答案 0 :(得分:7)

触发错误的原因是您没有提供正确的目标。这就是目标 - 动作范例的工作原理(或者至少,它在Objective C中是如何工作的,我真的希望他们能够更新Swift API以利用整个函数作为对象的哲学):

  • 您有一个名为target的对象,它有一个您希望外部调用者调用的方法,称为action。 (请注意,我简化了这个,它实际上是Objective C运行时的东西)
  • 您的API调用在其签名中的某处具有target: (id)target, selector:(Selector)selector。这应该告诉您,该API计划调用一个方法,您必须在对象上提供您必须提供的方法。
  • 这些将分别是你的目标和行动。 由于您的视图包含您希望NSTimer运行的方法,因此视图将成为您的目标,并且该方法将成为您的操作。当您提供self作为目标时,您告诉NSTimer调用名为" doSomeWork"的方法。在您的View Controller上,它没有实现这样的方法。难怪它会触发关于无法识别的选择器的错误! 此外,如果您确实将视图作为目标传递,那么会触发错误,因为与该方法关联的正确选择器将是"doSomeWork:",如在Objective C方法签名中那样。

这是最接近正确的实施方式:

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*/}
}

根据参数并返回:根据目标 - 动作范例,您不应使用您正在调用的方法的任何返回值。目标操作提供了一种机制,使目标与发送方对象(NSTimer)进行通信:如果您提供的选择器不接受任何参数(即它不以冒号结尾),则调用该方法时不带任何参数;如果选择器确实期望一个参数(即它以冒号结束),则发送方将调用它作为唯一参数传递自己。显然,您必须相应地更新被调用的方法:

class GraphicView: UIView {
   var timerV:NSTimer!
   func doSomeWork(sender: NSTimer!) {
      /* do some work involving sender */
   }
}

关于最后一个问题,如果我理解正确,你要求一种方法来避免从UIViewController内部引用GraphicView中定义的方法。这实际上是可行的,但是这不会让你在不使用目标动作的情况下设置NSTimer(截至今天)。当然,实现整个事情的更方便的方法可能是:

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)
    }
}

在此实现中,将self作为目标传递是有意义的,因为视图正在设置计时器以自行调用方法。这样,您可以通过创建上面的临时常量然后访问其timer属性,从视图控制器内部访问计时器。