UIView
へのポインターがあります。 UIViewController
にアクセスするにはどうすればよいですか? [self superview]
は別のUIView
ですが、UIViewController
ではありませんよね?
はい、superview
はビューを含むビューです。ビューは、MVCの原則を破るので、ビューコントローラーを正確に知る必要はありません。
一方、コントローラーは、どのビューに責任があるか(self.view = myView
)を知っており、通常、このビューはコントローラーに処理するためのメソッド/イベントを委任します。
通常、ビューへのポインターの代わりに、コントローラーへのポインターが必要です。コントローラーは、制御ロジックを実行するか、ビューに何かを渡すことができます。
UIResponder
のnextResponder
ドキュメントから:
UIResponderクラスは、次のレスポンダーを自動的に保存または設定せず、代わりにデフォルトでnilを返します。サブクラスはこのメソッドをオーバーライドして、次のレスポンダーを設定する必要があります。 IViewは、それを管理するUIViewControllerオブジェクト(ある場合)またはスーパービュー(ない場合)を返すことでこのメソッドを実装します; UIViewControllerは、ビューのスーパービューを返すことでメソッドを実装します。 UIWindowはアプリケーションオブジェクトを返し、UIApplicationはnilを返します。
したがって、ビューのnextResponder
がUIViewController
型になるまで再帰する場合、ビューの親viewControllerがあります。
ただし、親View Controllerがnotある可能性があります。ただし、ビューにviewControllerのビューのビュー階層の一部がない場合のみ。
Swift 3およびSwift 4.1拡張機能:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Swift 2拡張機能:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.nextResponder()
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
Objective-Cカテゴリ:
@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end
@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
responder = [responder nextResponder];
return (UIViewController *)responder;
}
@end
このマクロはカテゴリー汚染を回避します:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
@ andrey 1行で回答(Swift 4.1でテスト済み):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
使用法:
let vc: UIViewController = view.parentViewController
デバッグのみを目的として、ビューで_viewDelegate
を呼び出してビューコントローラーを取得できます。これはプライベートAPIであるため、App Storeにとって安全ではありませんが、デバッグには役立ちます。
その他の便利な方法:
_viewControllerForAncestor
-スーパービューチェーン内のビューを管理する最初のコントローラーを取得します。 (n00neimp0rtantに感謝)_rootAncestorViewController
-ビュー階層が現在ウィンドウに設定されている先祖コントローラーを取得します。UIViewを持つUIViewControllerへの参照を取得するには、UIResponder(UIViewおよびUIViewControllerのスーパークラス)の拡張を作成します。
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.nextResponder() is UIViewController {
return self.nextResponder() as? UIViewController
} else {
if self.nextResponder() != nil {
return (self.nextResponder()!).getParentViewController()
}
else {return nil}
}
}
}
//Swift 3
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.next is UIViewController {
return self.next as? UIViewController
} else {
if self.next != nil {
return (self.next!).getParentViewController()
}
else {return nil}
}
}
}
let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc
Swift 3の高速で汎用的な方法3:
extension UIResponder {
func parentController<T: UIViewController>(of type: T.Type) -> T? {
guard let next = self.next else {
return nil
}
return (next as? T) ?? next.parentController(of: T.self)
}
}
//Use:
class MyView: UIView {
...
let parentController = self.parentController(of: MyViewController.self)
}
コードに精通しておらず、特定のビューに対応するViewControllerを見つけたい場合は、次を試してください。
po(UIView *)0x7fe523bd3000 po [(UIView *)0x7fe523bd3000 nextResponder] po [[(UIView *)0x7fe523bd3000 nextResponder] nextResponder] po [[ [(UIView *)0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
ほとんどの場合、UIViewを取得しますが、時々UIViewControllerベースのクラスがあります。
タップをView Controllerに伝播して処理できると思います。これはより受け入れられるアプローチです。ビューからView Controllerにアクセスする場合は、別の方法がないため、View Controllerへの参照を維持する必要があります。次のスレッドをご覧ください。役立つかもしれません: ビューからView Controllerにアクセスする
少し遅れましたが、ViewControllerを含むあらゆるタイプのレスポンダーを見つけることができる拡張機能があります。
extension NSObject{
func findNext(type: AnyClass) -> Any{
var resp = self as! UIResponder
while !resp.isKind(of: type.self) && resp.next != nil
{
resp = resp.next!
}
return resp
}
}
残念ながら、これは、ビューをサブクラス化して、インスタンスプロパティなどを提供しない限り不可能です。ビューがシーンに追加されると、内部にView Controller参照を保存します。
ほとんどの場合-ほとんどのView Controllerは、ViewControllerのViewへのサブビューの追加を担当したプログラマーによく知られたエンティティであるため、この投稿の元の問題を回避することは非常に簡単です;-)Appleは、そのプロパティを追加することを気にしませんでした。
Swift 3.0のより多くのタイプセーフコード
extension UIResponder {
func owningViewController() -> UIViewController? {
var nextResponser = self
while let next = nextResponser.next {
nextResponser = next
if let vc = nextResponser as? UIViewController {
return vc
}
}
return nil
}
}