ios - ViewControl 之外的 Swift 选择器

标签 ios swift

我有一个 ViewController 及其 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*/}
}

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

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

2) 如何从 View 中选择函数而不是在 VC 中创建这样的函数?

最佳答案

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

  • 您有一个名为 target 的对象,它有一个名为 action 的方法,您希望外部调用者调用该方法。 (请注意,我正在对此进行简化,它实际上是 Objective C 运行时的东西)
  • 您有一个 API 调用,其签名中某处具有 target: (id)target, selector:(Selector)selector。这应该告诉您,该 API 计划调用您必须提供的对象上的方法,您也必须提供该对象。
  • 它们分别是您的目标和您的行动。 由于您的 View 包含您希望 NSTimer 运行的方法,因此 View 将成为您的目标,而方法将成为您的操作。当您提供 self 作为目标时,您是在告诉 NSTimer 在您的 View Controller 上调用名为“doSomeWork”的方法,而该方法并未实现此类方法。难怪它会触发有关无法识别的选择器的错误! 此外,如果您确实将 View 作为目标传递,那仍然会触发错误,因为与该方法关联的正确选择器将是 “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*/}
}

根据参数和返回:根据目标-操作范例,您不应使用所调用方法的任何返回值。 Target-action 为目标提供了一种与发送者对象(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 作为目标传递是有意义的,因为 View 正在设置计时器以在其自身上调用该方法。这样您就可以像上面那样创建一个临时常量,然后访问它的 timer 属性,从而从 View Controller 内部访问计时器。

关于ios - ViewControl 之外的 Swift 选择器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26512256/

相关文章:

ios - Parse.com iOS queryWithClassName 适用于某些人,但不适用于其他人,为什么?

ios - 触摸并按住 SpriteKit SKSpriteNode 速度

ios - ImageView 渐变表现异常

ios - 如何设置所有 View Controller 导航栏的默认导航栏颜色、文本颜色

android - 在 iOS 应用程序和 Firebase/Google Analytics 中配置多个 Firebase 项目运行时

javascript - 延迟隐藏移动地址栏

swift - 当传入的 json 不完整时,我的 swift 应用程序崩溃。我该如何处理?

ios - 更改导航栏按钮项的标题

swift - xcode swift 3 锁屏 Remote 不工作?

ios - 将 NSString 转换为 String 时,我的结果发生了变化