πŸš€ UllrichLumina

Get to UIViewController from UIView

Get to UIViewController from UIView

πŸ“… | πŸ“‚ Category: Programming

In iOS development, understanding the intricate relationship between a UIView and its managing UIViewController is fundamental for building robust and interactive applications. While a view controller inherently manages its views, scenarios often arise where a specific UIView instance, particularly a custom one, needs to communicate with or access its containing view controller. This necessity typically stems from a view needing to trigger navigation, present an alert, or update data managed by the controller. Directly accessing the UIViewController from a UIView might seem counter-intuitive at first glance, given the typical top-down flow of control. However, leveraging the UIKit framework’s built-in mechanisms, such as the responder chain, provides elegant and safe solutions to bridge this communication gap, ensuring your architecture remains clean and maintainable.

Understanding the UIResponder Chain for View-Controller Communication

The UIResponder chain is a core concept in iOS that dictates how events are handled and passed through an application’s interface. Every object that can respond to events, including UIView and UIViewController instances, inherits from UIResponder. When an event occurs, like a touch or a shake gesture, UIKit starts by sending it to the “first responder.” If this responder doesn’t handle the event, it passes it along its next responder in the chain. This chain typically goes from a subview to its superview, and eventually from the root view of a view controller to the view controller itself, and then potentially up to the window and the application delegate.

This hierarchical event-handling mechanism is precisely what allows a UIView to locate its managing UIViewController. By traversing this chain, a view can effectively “ask” its hierarchy if there’s a view controller capable of handling a specific request or providing necessary context. It’s a powerful, albeit often overlooked, method for communication without creating tight coupling that can complicate your codebase. Apple’s UIResponder documentation provides a deep dive into how this chain functions and its various applications beyond just finding a view controller.

Leveraging the responder chain is considered a best practice because it respects the natural hierarchy of your UI. Instead of creating direct, hard-coded references that can lead to retain cycles or fragile code, the responder chain offers a dynamic and flexible way for UI elements to interact with their surroundings. This method is particularly useful for generic custom views that might be used in multiple view controllers, as it doesn’t require them to know the specific type of their parent controller.

Infographic here
Method 1: Traversing the Responder Chain to Get to UIViewController from UIView -------------------------------------------------------------------------------

The most common and recommended approach to find a UIViewController from a UIView is to traverse the responder chain. This method involves iterating through the next property of UIResponder until an object of type UIViewController is found. This technique is robust because it relies on the fundamental structure of UIKit’s event delivery system, which every view and view controller participates in. It does not create strong references, thus avoiding potential memory leaks or circular dependencies.

Here’s a Swift extension that encapsulates this logic, making it reusable and clean:

extension UIView { var parentViewController: UIViewController? { var responder: UIResponder? = self while let currentResponder = responder { if let viewController = currentResponder as? UIViewController { return viewController } responder = currentResponder.next } return nil } } 

This extension adds a computed property, parentViewController, to all UIView instances. When called, it starts from the view itself, checks if it’s a view controller, and if not, moves to its next responder. This process continues until a UIViewController is found or the end of the responder chain is reached. This approach is highly efficient for most scenarios, as the responder chain is typically short in terms of the number of elements between a view and its immediate managing controller. It’s also forward-compatible with future UIKit updates, as the underlying responder chain mechanism is unlikely to change significantly.

Method 2: Leveraging Delegation and Callbacks for Custom Views

While traversing the responder chain is effective, for custom UIView subclasses that need to communicate specific actions or data back to their managing UIViewController, delegation is often a superior design pattern. Delegation establishes a clear, decoupled communication channel where the custom view (the “delegate”) can inform its controller (the “delegator”) about events without needing direct knowledge of the controller’s type or implementation. This enhances modularity and reusability, making your custom views more versatile.

Consider a custom UserProfileView that has a “Save Profile” button. Instead of the view trying to find its view controller to handle the save logic, it can define a protocol:

protocol UserProfileViewDelegate: AnyObject { func userProfileViewDidTapSave(_ view: UserProfileView, with data: UserData) } class UserProfileView: UIView { weak var delegate: UserProfileViewDelegate? // ... other UI elements and setup ... @objc private func saveButtonTapped() { let userData = UserData(name: "John Doe", email: "john@example.com") // Example data delegate?.userProfileViewDidTapSave(self, with: userData) } } class UserProfileViewController: UIViewController, UserProfileViewDelegate { let profileView = UserProfileView() override func viewDidLoad() { super.viewDidLoad() profileView.delegate = self // ... add profileView to hierarchy ... } func userProfileViewDidTapSave(_ view: UserProfileView, with data: UserData) { print("Saving user data: \(data.name)") // Handle saving logic, e.g., to a database or API } } 

In this example, the UserProfileView simply informs its delegate that the save button was tapped, passing any relevant data. The UserProfileViewController, by conforming to the UserProfileViewDelegate protocol and setting itself as the delegate, receives this message and can then perform the necessary business logic. This pattern is foundational in iOS development, used extensively by UIKit Question & Answer :

Is there a built-in way to get from a UIView to its UIViewController? I know you can get from UIViewController to its UIView via [self view] but I was wondering if there is a reverse reference?

Using the example posted by Brock, I modified it so that it is a category of UIView instead UIViewController and made it recursive so that any subview can (hopefully) find the parent UIViewController.

@interface UIView (FindUIViewController) - (UIViewController *) firstAvailableUIViewController; @end @implementation UIView (FindUIViewController) - (UIViewController *) firstAvailableUIViewController { UIResponder *responder = [self nextResponder]; while (responder != nil) { if ([responder isKindOfClass:[UIViewController class]]) { return (UIViewController *)responder; } responder = [responder nextResponder]; } return nil; } @end 

To use this code, add it into an new class file (I named mine “UIKitCategories”) and remove the class data… copy the @interface into the header, and the @implementation into the .m file. Then in your project, #import “UIKitCategories.h” and use within the UIView code:

// from a UIView subclass... returns nil if UIViewController not available UIViewController * myController = [self firstAvailableUIViewController];