targetViewController(forAction:sender:)を利用する方法を考えてみました。
以下のアクションを実行できるViewControllerに委譲したいと考えます。
@objc protocol SomeProtocol:NSObjectProtocol {
@objc func someAction(sender:Any?)
}
親のViewControllerが子としてUINavigationViewControllerとSomeProtocolを実装したViewControllerを持っているとします。
その時、targetViewController(forAction:sender:)を使えば階層をたどって実行できるViewControllerを手に入れることができます。
// 子のViewController
class ServiceViewController : UIViewController {
@IBAction func pushed(sender:Any?) {
// ViewController階層の先祖でアクションを実装している人にアクションができる人を探す。
let t = self.targetViewController(forAction: #selector(SomeProtocol.someAction(sender:)), sender: sender)
// アクションを委譲する。
t?.perform(#selector(SomeProtocol.someAction(sender:)), with: sender)
}
}
SomeProtocolを実装しているViewControllerは
class DetailViewController:UIViewController, SomeProtocol {
@IBOutlet weak var label: UILabel!
func someAction(sender: Any?) {
label.text = "someAction done"
}
}
親のViewControllerはtargetViewController(forAction:sender:)を実装し、SomeProtocolを実装しているViewControllerを見つけて渡します。
// 複数のServiceViewControllerが入っているUINavigationViewControllerと
// DetailViewControllerをchildViewControllersにもつとする。
class ModalServiceParentController: UIViewController {
// 特定のアクションを拾う
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(SomeProtocol.someAction(sender:)) {
return true
}
return super.canPerformAction(action, withSender: sender)
}
// そのアクションを実行するViewControllerを渡す。
override func targetViewController(forAction action: Selector, sender: Any?) -> UIViewController? {
if action == #selector(SomeProtocol.someAction(sender:)) {
let vc = self.childViewControllers.first {vc in vc is SomeProtocol}
return vc
}
return super.targetViewController(forAction: action, sender: sender)
}
}