Reputation: 12847
I have a TableViewController, TableViewCell and a ViewController. I have a button in the TableViewCell and I want to present ViewController with presentViewController
(but ViewController doesn't have a view on storyboard). I tried using:
@IBAction func playVideo(sender: AnyObject) {
let vc = ViewController()
self.presentViewController(vc, animated: true, completion: nil)
}
Error: Value of type TableViewCell has no member presentViewController
Then, I tried
self.window?.rootViewController!.presentViewController(vc, animated: true, completion: nil)
Error: Warning: Attempt to present whose view is not in the window hierarchy!
What am I doing wrong? What should I do in order to presentViewController from TableViewCell? Also how can I pass data to the new presenting VC from TableViewCell?
Update:
protocol TableViewCellDelegate
{
buttonDidClicked(result: Int)
}
class TableViewCell: UITableViewCell {
@IBAction func play(sender: AnyObject) {
if let id = self.item?["id"].int {
self.delegate?.buttonDidClicked(id)
}
}
}
----------------------------------------
// in TableViewController
var delegate: TableViewCellDelegate?
func buttonDidClicked(result: Int) {
let vc = ViewController()
self.presentViewController(vc, animated: true, completion: nil)
}
I receive error: Presenting view controllers on detached view controllers is discouraged
(Please note that I have a chain of NavBar & TabBar behind TableView.)
I also tried
self.parentViewController!.presentViewController(vc, animated: true, completion: nil)
Same Error.
Also tried,
self.view.window?.rootViewController?.presentViewController(vc, animated: true, completion: nil)
Same Error
Upvotes: 4
Views: 13121
Reputation: 161
I had the same problem and found this code somewhere on stackoverflow, but I can't remember where so it's not my code but i'll present it here.
This is what I use when I want to display a view controller from anywhere, it gives some notice that keyWindow
is disabled but it works fine.
extension UIApplication
{
class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController?
{
if let nav = base as? UINavigationController
{
let top = topViewController(nav.visibleViewController)
return top
}
if let tab = base as? UITabBarController
{
if let selected = tab.selectedViewController
{
let top = topViewController(selected)
return top
}
}
if let presented = base?.presentedViewController
{
let top = topViewController(presented)
return top
}
return base
}
}
And you can use it anywhere, in my case I used:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let vc = storyboard.instantiateViewController(withIdentifier: "WeekViewController")
UIApplication.topViewController()?.navigationController?.show(vc, sender: nil)
}
Upvotes: 2
Reputation: 61
So self.presentViewController is a method from the ViewController. The reason you are getting this error is because the "self" you are referring is the tableViewCell. And tableViewCell doesn't have method of presentViewController.
I think there are some options you can use: 1.add a delegate and protocol in the cell, when you click on the button, the IBAction will call
self.delegate?.didClickButton()
Then in your tableVC, you just need to implement this method and call self.presentViewController
2.use a storyboard and a segue In the storyboard, drag from your button to the VC you want to go.
Upvotes: 0
Reputation: 176
It seems like you've already got the idea that to present a view controller, you need a view controller. So here's what you'll need to do:
Here's some code:
// 1.
protocol PlayVideoCellProtocol {
func playVideoButtonDidSelect()
}
class TableViewCell {
// ...
// 2.
var delegate: PlayVideoCellProtocol!
// 3.
@IBAction func playVideo(sender: AnyObject) {
self.delegate.playVideoButtonDidSelect()
}
// ...
}
class TableViewController: SuperClass, PlayVideoCellProtocol {
// ...
// 4.
func playVideoButtonDidSelect() {
let viewController = ViewController() // Or however you want to create it.
self.presentViewController(viewController, animated: true, completion: nil)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell {
//... Your cell configuration
// 5.
cell.delegate = self
//...
}
//...
}
Upvotes: 14
Reputation: 1441
You should use protocol
to pass the action back to tableViewController
1) Create a protocol
in your cell class
2) Make the button
action call your protocol
func
3) Link your cell's protocol
in tableViewController
by cell.delegate = self
4) Implement the cell's protocol
and add your code there
let vc = ViewController()
self.presentViewController(vc, animated: true, completion: nil)
Upvotes: 7