へのポインタがありUIView
ます。どうすればアクセスできますUIViewController
か? [self superview]
は別UIView
ですが、ではありませんUIViewController
か?
へのポインタがありUIView
ます。どうすればアクセスできますUIViewController
か? [self superview]
は別UIView
ですが、ではありませんUIViewController
か?
回答:
はい、これsuperview
はビューを含むビューです。MVCの原則に反するため、ビューはどちらがビューコントローラーであるかを正確に知る必要はありません。
一方、コントローラーは、それが担当するビュー(self.view = myView
)を認識しており、通常、このビューは処理するメソッド/イベントをコントローラーに委任します。
通常、ビューへのポインターの代わりに、コントローラーへのポインターが必要です。コントローラーは、制御ロジックを実行するか、ビューに何かを渡すことができます。
のUIResponder
ドキュメントからnextResponder
:
UIResponderクラスは、次のレスポンダーを自動的に保存または設定せず、代わりにデフォルトでnilを返します。次のレスポンダを設定するには、サブクラスでこのメソッドをオーバーライドする必要があります。UIViewは、それを管理するUIViewControllerオブジェクト(ある場合)またはそのスーパービュー(ない場合)を返すことにより、このメソッドを実装します。UIViewControllerは、ビューのスーパービューを返すことでメソッドを実装します。UIWindowはアプリケーションオブジェクトを返し、UIApplicationはnilを返します。
したがって、ビューのnextResponder
タイプがになるまでビューを再帰する場合UIViewController
、ビューの親のviewControllerがあります。
まだ親View Controller がない場合があることに注意してください。ただし、ビューが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; \
})
UIResponder
;)の拡張にする必要があります。非常に教育的な投稿。
@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
-ビュー階層が現在ウィンドウに設定されている祖先コントローラーを取得します。_viewControllerForAncestor
ビューコントローラーに属する最初のスーパービューが見つかるまで、スーパービューをたどります。
UIViewを持つUIViewControllerへの参照を取得するには、UIResponder(UIViewとUIViewControllerのスーパークラス)の拡張を作成します。これにより、レスポンダーチェーンを上に移動し、UIViewController(それ以外の場合はnilを返す)に到達できます。
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の高速で一般的な方法:
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に伝播してそれを処理させることができると思います。これはより許容できるアプローチです。ビューからビューコントローラーにアクセスする場合は、他に方法がないため、ビューコントローラーへの参照を維持する必要があります。このスレッドを参照してください、それが役立つかもしれません: ビューからビューコントローラーにアクセスする
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
}
}
ブレークポイントを設定した場合、これをデバッガーに貼り付けて、ビュー階層を印刷できます。
po [[UIWindow keyWindow] recursiveDescription]
その混乱のどこかにビューの親を見つけることができるはずです:)
recursiveDescription
ビューコントローラーではなく、ビュー階層のみを出力します。