The view's window property is non-nil if a view is currently visible, so check the main view in the view controller: Invoking the view method causes the view to load (if it is not loaded) which is unnecessary and may be undesirable. It would be better to check first to see if it is already loaded.
Views are for display, model objects are for data, controllers are the glue in between them.
To create a new view controller, select File->New->File and select a Cocoa Touch Class. Choose whether to create it with Swift or Objective-C and inherit from UIViewController . Don't create it with a xib (a separate Interface Builder file), as you will most likely add it to an existing storyboard.
From the UIResponder
documentation for nextResponder
:
The UIResponder class does not store or set the next responder automatically, instead returning nil by default. Subclasses must override this method to set the next responder. UIView implements this method by returning the UIViewController object that manages it (if it has one) or its superview (if it doesn’t); UIViewController implements the method by returning its view’s superview; UIWindow returns the application object, and UIApplication returns nil.
So, if you recurse a view’s nextResponder
until it is of type UIViewController
, then you have any view’s parent viewController.
Note that it still may not have a parent view controller. But only if the view has not part of a viewController’s view’s view hierarchy.
Swift 3 and Swift 4.1 extension:
extension UIView {
var parentViewController: UIViewController? {
// Starts from next (As we know self is not a UIViewController).
var parentResponder: UIResponder? = self.next
while parentResponder != nil {
if let viewController = parentResponder as? UIViewController {
return viewController
}
parentResponder = parentResponder?.next
}
return nil
}
}
Swift 2 extension:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self.nextResponder()
while parentResponder != nil {
if let viewController = parentResponder as? UIViewController {
return viewController
}
parentResponder = parentResponder!.nextResponder()
}
return nil
}
}
Objective-C category:
@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end
@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
UIResponder *responder = [self nextResponder];
while (responder != nil) {
if ([responder isKindOfClass:[UIViewController class]]) {
return (UIViewController *)responder;
}
responder = [responder nextResponder];
}
return nil;
}
@end
This macro avoids category pollution:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
@andrey answer in one line (tested in Swift 4.1):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
usage:
let vc: UIViewController = view.parentViewController
Yes, the superview
is the view that contains your view. Your view shouldn't know which exactly is its view controller, because that would break MVC principles.
The controller, on the other hand, knows which view it's responsible for (self.view = myView
), and usually, this view delegates methods/events for handling to the controller.
Typically, instead of a pointer to your view, you should have a pointer to your controller, which in turn can either execute some controlling logic, or pass something to its view.
For debug purposes only, you can call _viewDelegate
on views to get their view controllers. This is private API, so not safe for App Store, but for debugging it is useful.
Other useful methods:
_viewControllerForAncestor
- get the first controller that manages
a view in the superview chain. (thanks n00neimp0rtant)_rootAncestorViewController
- get the ancestor controller whose
view hierarchy is set in the window currently.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With