Reputation: 4522
I have a UITableViewController
, that as a ViewModel class. I am trying to build out my application using the MVVM pattern.
My tableView has a cell that display an image, that image has a gesture recogniser that calls a method in the view model on press.
At this point I would like to present a ViewController
modally, with some embedded content.
However my TableView cell
conforms to UITableViewCell
so I cannot call present from here.
My ViewModel does not conform to anything, so I cannot call present from there either.
How can I trigger a modal to appear, from within a UITableViewCell
?
Upvotes: 1
Views: 1597
Reputation: 1147
Add a UIWindow extension
extension UIWindow {
static var top: UIViewController? {
get {
return topViewController()
}
}
static var root: UIViewController? {
get {
return UIApplication.shared.delegate?.window??.rootViewController
}
}
static func topViewController(from viewController: UIViewController? = UIWindow.root) -> UIViewController? {
if let tabBarViewController = viewController as? UITabBarController {
return topViewController(from: tabBarViewController.selectedViewController)
} else if let navigationController = viewController as? UINavigationController {
return topViewController(from: navigationController.visibleViewController)
} else if let presentedViewController = viewController?.presentedViewController {
return topViewController(from: presentedViewController)
} else {
return viewController
}
}
}
than call this from anywhere like:
guard let topController = UIWindow.top else { return } // UIWindow.root
let youVC = theStoryboard.instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
youVC.modalTransitionStyle = .crossDissolve
youVC.modalPresentationStyle = .overCurrentContext
topController.present(youVC, animated: true, completion: nil)
Upvotes: 1
Reputation: 3305
You have couple of options but I will cover solution with delegate.
The idea is to define protocol
and property of that protocol in MyViewModel
and make MyViewController
conforming to it.
Here is how the MyViewModel
could look like:
protocol MyViewModelDelegate: class {
func didTapOnCell()
}
class MyViewModel {
// Please note the delegate is weak and optional
weak var delegate: MyViewModelDelegate?
// This function handle gesture recognizer taps
@objc func handleImageViewTap() {
delegate?.didTapOnCell()
}
// Here is the rest of the ViewModel class...
}
Then in the MyViewController
you set viewModel's delegate property to self
and conforms to the protocol function (I'm assuming view controller references the view model instance).
class MyViewController: UITableViewController {
func setup() {
// ...
// When MyViewModel is initialised, set the delegate property to self
myViewModel.delegate = self
}
}
extension MyViewController: ViewModelDelegate {
func didTapOnCell() {
// ...
// Allocate instance of anotherViewController here and present it
self.present(anotherViewController, animated: true, completion: .none)
}
}
This way you can let know MyViewController
something happened in MyViewModel
and act accordingly.
Please note it's necessary to make delegate
property optional to avoid retain cycles.
Upvotes: 1