I have an application with sideBarMenu which has five sections. In my storyboard I've created start MainViewController embedded in NavigationController. Then I've created same five VC's and again embed them in NavigationController. So I have 6 VC's, each of which is wrapped in its own NavigationController.
Each VC implements sideBarDelegate with function that has an index of selected menu.
Here is the code:
// SideBarTableViewController
protocol SideBarTableViewControllerDelegate {
func SideBarControlDidSelectRow(indexPath: NSIndexPath)
}
class SideBarTableViewController: UITableViewController {
// ...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
println(indexPath.row)
delegate?.SideBarControlDidSelectRow(indexPath)
}
}
// SideBar class ------------------------------------------------
#objc protocol SideBarDelegate {
func SideBarDidSelectButtonAtIndex(index: Int)
optional func SideBarWillClose()
optional func SideBarWillOpen()
}
class SideBar: NSObject, SideBarTableViewControllerDelegate {
// ...
func SideBarControlDidSelectRow(indexPath: NSIndexPath) {
println(indexPath.row)
delegate!.SideBarDidSelectButtonAtIndex(indexPath.row)
}
}
Then in this function I want to present needed ViewController:
func SideBarDidSelectButtonAtIndex(index: Int) {
presentViewControllerByIndex(index)
}
func presentViewControllerByIndex(index: Int) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
switch index {
case 0:
var viewController = storyBoard.instantiateViewControllerWithIdentifier("SearchVC") as! SearchViewController
viewController.delegate = self
self.presentViewController(viewController, animated: false, completion: nil)
break
// the same code here for other 4 VC's with their StoryboardId's
default: break
}
}
Then in presented view controller if user select another section in menu should I destroy this VC before presenting selected ViewController? Because when I run my application and started switching between controllers the application memory in xCode increases (screenshot).
When I saw that I've started googling and found this answer. So I have created a protocol in my first menu VC just for test.
protocol myProtocol {
func dissmissAndPresend(index: Int) -> Void
}
And when I need to present new VC I just call:
self.delegate?.dissmissAndPresend(index)
Then in my MainVC I implement this protocol and trying to dismiss previous VC and present the new one:
class MainViewController: UIViewController, SideBarDelegate, myProtocol {
func dissmissAndPresend(index: Int) {
self.dismissViewControllerAnimated(false, completion: {
self.presentViewControllerByIndex(index)
})
}
Now when I launch my program and started to click on the first menu item it opens new VC but my memory increases as before. And also the navigationController for presented view has gone but in storyboard presented VC is embedded in navigationController.
What I am doing wrong? Can someone help me?
UPDATE::-------------------------------
Here is what I've tried:
class MainViewController: UIViewController, SideBarDelegate, searchVCProtocol {
var searchVC: SearchViewController!
var searchNavController: ShadowUINavigationController!
override func viewDidLoad() {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
searchVC = mainStoryboard.instantiateViewControllerWithIdentifier("SearchVC") as! SearchViewController
searchVC.delegate = self
searchNavController = ShadowUINavigationController(rootViewController: searchVC)
}
func SideBarDidSelectButtonAtIndex(index: Int) {
switch index {
case 0:
println("asda")
self.presentViewController(searchNavController, animated: false, completion: nil)
break
}
}
Then it takes the user to SearchViewController and what if the user open menu again and click on SearchViewController again. How can I dismiss it and reopen. Here is what I've tried but it doesn't work:
In SeachViewController I've created a protocol:
protocol searchVCProtocol {
func dismissAndPresent(index: Int) -> Void
}
Then I've added a variable for the delegate:
var delegate: searchVCProtocol?
Then when user selected menu item it fires this event:
func SideBarDidSelectButtonAtIndex(index: Int) {
delegate?.dismissAndPresent(index)
}
And in MainViewController I've implemented this protocol and created dismissAndPresent method but I don't know how to restart presented viewController.
class MainViewController: UIViewController, SideBarDelegate, searchVCProtocol {
func dismissAndPresent(index: Int) {
// code to restart this VC
}
}
What should I code for restarting presented VC?
You are calling instantiateViewControllerWithIdentifier every time, this creates ("instantiates") every time a new ViewController. Better would be to do this only once, keep a reference to it and then re-use it on demand.
Here how you can keep a reference. First you create a new variable on class level:
class MyClass {
var viewController: SearchViewController!
Then, in viewDidLoad, you instantiate the view controller and assign it to the variable.
func viewDidLoad() {
viewController = storyBoard.instantiateViewControllerWithIdentifier("SearchVC") as! SearchViewController
}
viewDidLoad is called only once so it will not happen anymore that you are creating multiple instances.
Then you can reuse this viewcontroller again and again:
func presentViewControllerByIndex(index: Int) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
switch index {
case 0:
// Here you can reuse the formerly created viewcontroller
viewController.delegate = self
self.presentViewController(viewController, animated: false, completion: nil)
break
// the same code here for other 4 VC's with their StoryboardId's
default: break
}
}
Related
I am working in Swift and the function categoryPressedFunction is not being called.
Protocol:
protocol categoryPressed: class { //1 create a protocol with function that passes a string
func categoryPressedFunction(category: String)
}
View Controller 2:
//set the delegate
weak var delegate: categoryPressed?
//when cell is selected in tableview, grab the "category" which is a string and then dismiss
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let category = guideCategoryArray[indexPath.row] // gets category
delegate?.categoryPressedFunction(category: category) // sets delegate to know it was pressed
self.presentingViewController?.dismiss(animated: true, completion: nil) //dismisses
}
}
View Controller 1 (Previous)
//set class delegate
...categoryPressed
//add function that sets label to category and looks up items based off category
func categoryPressedFunction(category: String) {
print("categoryPressedFunctionPressed")
resourceArray.removeAll()
resourceLabel.text = category
getItems(item: category, { [self] in
print("got new items for \(category) and refreshed the tableview")
self.resourceTableView.reloadData()
})
}
When returning to ViewController 1, nothing happens. The tableview does not reload, nor does the label change to the category pressed. Am I missing something?
Delegates might be nil. Did you add this line in the ViewDidLoad method?
delegate = self
You might have missed assigning delegate to self while moving from VC1 to VC2.
Hope below code helps you.
//Some Navigation logic
let VC2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "VC2Identifier") as! VC2;
VC2.delegate = self
self.present(VC2, animated: true, completion: nil)
First of all, let me say that I have been using the delegation pattern to pass data back and forward between view controllers for quite some time without any issues but now I have a need to pass data between four (4) view controllers, ViewController1, ViewController2, ViewController3 and CategoriesViewController.
In the code below I'm showing the communication between ViewController1 and the CategoriesViewController, the communication between ViewController2 and CategoriesViewController will be identical as well.
My issue or what I don't quite like is the fact that I don't need to pass any data between ViewController3 and the CategoriesViewController so, my debate is how can I handle the fact that CategoriesViewController is expecting variable categoryTracker which I will not be passing when connecting ViewController3 with the CategoriesViewController.
Is there a way to make the variable categoryTracker in CategoriesViewController optional, here I'm talking in a sense that I wouldn't have to pass it when connecting from ViewController3 and NOT a Swift optional?
How would you guys do such communication in a way that CategoriesViewController becomes more modular/reusable?
ViewController1 and ViewController2
class ViewController1: UIViewController, CategoryDelegate{
#IBAction func showCategories(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "categoriesViewControllerID") as? CategoriesViewController
vc?.delegate = self
vc?.categoryTracker = self.categorySelection
self.present(vc!, animated: true, completion: nil)
}
}
CategoriesViewController
protocol CategoryDelegate {
func selectedCategory(category: Category)
}
class CategoriesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var delegate: CategoryDelegate?
var categoryTracker:Category?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.selectedCategory(category: categoryTracker!)
}
}
Just for reference, here is how ViewController3 would look like.
class ViewController3: UIViewController{
#IBAction func showCategories(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "categoriesViewControllerID") as? CategoriesViewController
self.present(vc!, animated: true, completion: nil)
}
}
Thanks
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I know this question has been asked so many many times. But here the scenario is changed. I am using MMParallaxView and that looks pretty awesome.
But Now at this point, I want to communicate between these two View controllers. Let me tell you that this github code helps us to make a view like ios Map App. I mean you can have Map (as 1st view controller) and you can have a list of places view controller on top of 1st View Controller. Just same like Map app.
Now I want to click on the UITableView cell and want to navigate on map. For this I know how to catch delegated method. And I am successfully getting of taps on the UItableView cell. But How to send that clicked item data to 1stView Controller so that It can show or mark selected area on Map.
I know this can also be done. But how?
To modify the example app for MMParallaxView...
In ChildBottomViewController.swift
Add this at the top (outside of the class):
protocol CellTapDelegate {
func didSelectRow(indexPath: IndexPath)
}
Add this line at the beginning of the class:
var cellTapDelegate: CellTapDelegate?
Change didSelectRowAt function to:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
DispatchQueue.main.async { [weak self] in
self?.cellTapDelegate?.didSelectRow(indexPath: indexPath)
}
}
In MapViewController.swift
Change the class declaration to:
class MapViewController: UIViewController, CellTapDelegate {
and add this function:
#objc func didSelectRow(indexPath: IndexPath) {
print("Delegate got didSelectRow for: \(indexPath)")
}
Then, in SecondViewController.swift
Add this at the end of viewDidLoad():
var mapVC: MapViewController?
var bottomVC: ChildBottomViewController?
for vc in self.childViewControllers {
if vc is MapViewController {
mapVC = vc as? MapViewController
} else if vc is ChildBottomViewController {
bottomVC = vc as? ChildBottomViewController
}
}
// if we found valid child view controllers, set the delegate
if mapVC != nil && bottomVC != nil {
bottomVC?.cellTapDelegate = mapVC
}
Now, selecting a row from the "slide up from bottom" table view will send the selected indexPath to its delegate - the map view controller.
protocol CellTapDelegate {
func didTapOnItem(obj: MyObject)
}
class MyViewControllerContainingTheTableView : UIViewcontroller {
weak var delegate: CellTapDelegate?
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
let item = arrayOfObjects[indexpath.row]
self.delegate.didTapOnItem(item)
}
}
//View Controller where you you will have the listner
class FirstViewController : UIViewController {
func setupParalax() {
// you have to modify this according to your need
let vc = MyViewControllerContainingTheTableView()
vc.delegate = self
}
}
extension FirstViewController: CellTapDelegate {
func didTapOnItem(obj: MyObject) {
// you have your required object here
}
}
This should be possible by implementing your own delegate, but my knowledge of MMParrallaxView is that there is only 1 view controller which displays two views meaning the view controller should be able to pass things between the two views as it is. In this case the view controller would implement the table view delegate methods and add the table view to the bottom view. Then add the map to the top view. This should allow you to catch the table view cell selection and update the map in the top view accordingly.
Example based on the structure I believe you are using:
public class FirstViewController {
public let parallaxView = MMParallaxView()
private var secondViewController: SecondViewController
private var thirdViewController: ThirdViewController
override open func viewDidLoad() {
secondViewController = SecondViewController()
thirdViewController = ThirdViewController()
super.viewDidLoad()
self.view.addSubview(parallaxView)
thirdViewController.delegate = secondViewController
parallaxView.parallaxTopView = secondViewController.view
self.addChildViewController(secondViewController)
secondViewController.didMove(toParentViewController: self)
parallaxView.parallaxBottomView = thirdViewController.view
self.addChildViewController(thirdViewController)
thirdViewController.didMove(toParentViewController: self)
}
}
public class SecondViewController: ThirdViewControllerDelegate {
public func updateMap() {
// Update map
}
}
public class ThirdViewController: UITableViewDelegate {
weak var delegate ThirdViewControllerDelegate?
private var table: UITableView = UITableView()
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
delegate?.updateMap()
}
}
protocol ThirdViewControllerDelegate: class {
public func updateMap() {}
}
if the view controllers are instantiated through the Storyboard then you can try this:
public class MasterViewController {
var secondViewController: SecondViewController?
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "MMParallaxTop", sender: nil)
self.performSegue(withIdentifier: "MMParallaxBottom", sender: nil)
}
override open func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondViewController {
secondViewController = secondVC
}
if let thirdVC = segue.destination as? ThirdViewController, let second = secondViewController {
thirdVC.delegate = second
}
}
}
I have a complicated issue I'd like to explain visually and through code.
Currently, this is how my app is suppose to work visually:
I have a ViewControllerOneclass that contains a UITableView with 9 cells. Any rows selected except rows 2, 6, and 7 will segue to ViewControllerTwo with its own UITableView and number of rows.
If rows 2, 6, or 7 is selected, a push segue will stack another ViewControllerOne onto the existing ViewControllerOne. The reason for doing this is because every row is a category, but rows 2, 6, and 7 also contains sub-categories that looks exactly like ViewControllerOne Main-VC on the left.
Rather than creating another class that contains the exact same code as in Main-VC, I wanted to reuse the ViewControllerOne class.
Now, if any rows in SUB-VC is selected, it will also perform a push segue to ViewControllerTwo.
Since ViewControllerOne and ViewControllerTwo are embedded in a UINavigationController, the issue I'm having is in the 5th step:
I select a row not 2, 6, or 7 in Main-VC, it takes me to ViewControllerTwo (as it should)
I go back to Main-VC via navigation bar back button and select row 2, 6, or 7 in MAIN-VC, it will take me to SUB-VC (as it should)
I select a row not 2, 6, or 7 in Sub-VC, it will take me to ViewControllerTwo (as it should)
I go back to Sub-VC via navigation bar back button on the navigation bar
I select a row 2, 6, or 7 in Sub-VC, it will push segue and stack another Sub-VC on top of the existing Sub-VC instead of doing a push segue to ViewControllerTwo
I have a Manager class that handles the logic and communicates with ViewControllerOne and ViewControllerTwo to display the data.
import UIKit
enum SubGroupState
{
case SubGroup1, None, SubGroup2, SubGroup3
}
class Manager: NSObject
{
public var subGroupState = SubGroupState.None
public var oldSubGroupState = SubGroupState.None
public var showSubGroups = Bool()
override init()
{
super.init()
}
public func initializeGroupState(row: Int) -> UIViewController
{
if showSubGroups == false && oldSubGroupState == .None
{
switch row
{
case 2:
subGroupState = .SubGroup1
break
case 6:
subGroupState = .SubGroup2
break
case 7:
subGroupState = .SubGroup3
break
default:
subGroupState = .None
break
}
}
if (subGroupState != .None && oldSubGroupState == .None)
|| (subGroupState == .None && oldSubGroupState != .None)
{
showSubGroups = true
}
else
{
showSubGroups = false
}
return initializeGroupVC(row: row)
}
fileprivate func initializeGroupVC(row: Int) -> UIViewController
{
let storyboard = UIStoryboard(name: "Main",
bundle: nil)
if showSubGroups == true
&& subGroupState != .None
{
guard let viewControllerOne = storyboard.instantiateViewController(withIdentifier: "ViewControllerOne")
as? ViewControllerOne else {
return UIViewController()
}
viewControllerOne.manager.oldSubGroupState = muscleSubGroupState
viewControllerOne.manager.showSubGroups = showSubGroups
return viewControllerOne
}
else
{
guard let viewControllerTwo = storyboard.instantiateViewController(withIdentifier: "ViewControllerTwo")
as? ViewControllerTwo else {
return UIViewController()
}
return muscleGroupExercisesVC
}
}
}
The purpose of the states is so I can handle displaying the different sub-categories depending on the state of the selected cell.
I create an instance of Manager in ViewControllerOne when the user selects a cell:
extension ViewControllerOne: UITableViewDataSource
{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
tableView.deselectRow(at: indexPath,
animated: true)
let viewController = manager.initializeGroupState(row: indexPath.row)
self.navigationController?.pushViewController(viewController,
animated: true)
}
}
class ViewControllerOne: UIViewController
{
public var manager = Manager()
....
}
The issue is in logic handling in the function initializeGroupState, but I've tried other different combinations and I always get the Sub-VC stacked on top of an existing Sub-VC for rows 2, 6, and 7, which obviously corresponds to the subgroup rows in Main-VC, and that's where the issue I'm having a difficult time handling the logic with.
If I am doing this the wrong way, is there a better alternative to what I'm trying to achieve without repeating code?
NOTE: My Storboard only has the Main-VC ViewControllerOne with a segue to ViewControllerTwo. The added Sub-VC ViewControllerOne is there to visually see what I'm trying to do, but does not actually exist in my Storyboard.
I think we can keep it simple as posible
class ViewControllerOne: UIViewController, UITableViewDelegate {
var isSubVC = false
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
if self.isSubVC {
// Push to ViewControllerTwo
} else {
// MainVC
if indexPath.row == 2 || indexPath.row == 6 || indexPath.row == 7 {
let subVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
subVC.isSubVC = true
self.navigationController?.pushViewController(subVC, animated: true)
} else {
// Push to ViewControllerTwo
}
}
}
}
Here is the code using your manager ideas for navigation without any tests.
enum ViewControllerType {
case main, subGroup1, subGroup2, subGroup3
}
class Manager {
public var currentState = ViewControllerType.main
public func initializeGroupState(row: Int) -> UIViewController {
if self.currentState == .main {
// MainVC, should push to SubVC if match condition
switch row {
case 2:
return self.makeSubViewController(state: .subGroup1)
case 6:
return self.makeSubViewController(state: .subGroup2)
case 7:
return self.makeSubViewController(state: .subGroup3)
default:
return self.makeViewControllerTwo()
}
} else {
// Current is SubVC, dont care kind of row, should push to ViewControllerTwo
return self.makeViewControllerTwo()
}
}
private func makeViewControllerTwo() -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewControllerTwo")
return vc
}
private func makeSubViewController(state: ViewControllerType) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
vc.manager.currentState = state
return vc
}
}
OK! What's happening here is that you have segues connected to your tableView, and that causes issues when you also push a new view controller in your code.
From the code you've posted here, I think just removing the segues from your storyboard would solve the issue. Doing it in only code as you're already doing in tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) is perfectly fine.
You can delete segues by clicking on them and pressing backspace, view controller can float for themselves in the storyboard without being connected by segues.
In Objective-C, but the logic should be the same, that's what I would do:
CustomModel:
#property (nonatomic, strong) NSArray *subdata;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, assing) BOOL isSpecial; //of course wording should be adapted to the real logic and your needs
By default on the init of a CustomModel object, isSpecial is set to NO.
ViewControllers:
#property (nonatomic, strong), NSArray *tableViewDataSource;
The tableViewDataSource of MainViewController should be like this:
- CustomData isSpecial: NO
subdata = [CustomData11, CustomData12...]
- CustomData isSpecial: NO
subdata = [CustomData21, CustomData22...]
- CustomData isSpecial: YES
subdata = [CustomData31, CustomData32...]
- CustomData isSpecial: NO
subdata = [CustomData41, CustomData42...]
- CustomData isSpecial: NO
subdata = [CustomData51, CustomData52...]
- CustomData isSpecial: NO
subdata = [CustomData61, CustomData62...]
- CustomData isSpecial: YES
subdata = [CustomData71, CustomData72...]
- CustomData isSpecial: YES
subdata = [CustomData81, CustomData82...]
- CustomData isSpecial: NO
subdata = [CustomData91, CustomData92...]
Logic for deciding on next viewcontroller:
You can use either Segue or manual instantiation/push, but I'd suggest you keep doing the same method for both of thems
CustomModel *selectedData = tableViewDataSource[indexPath.row]
if ([selectedData isSpecial])
{
//Go to SubVC, ie. another MainVC
SubViewController *nextVC = ... //segue.destinationViewController looping on itself or storyboard.instatianteViewController instantiating another MainViewController
nextVC.data = selectedData.subdata;
}
else
{
//Go VC2
ViewController *nextVC = ... //segue.destinationViewController or storyboard.instatianteViewController...
nextVC.data = selectedData.subdata;
}
You can manage you viewcontroller and manager class as below:
class ViewControllerOne: UIViewController, UITableViewDelegate {
var isSubVC = false
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
let wireframe = WireFrame()
let nextVC = wireframe.getNextVC(forView: self)
self.navigationController?.pushViewController(nextVC, animated: true)
}
}
class WireFrame {
func getNextVC (forView view: UIViewController) -> UIViewController {
if view.isKind(of: ViewControllerOne) {
return ViewControllerTwo
} else {
// MainVC
if indexPath.row == 2 || indexPath.row == 6 || indexPath.row == 7 {
let subVC = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerOne") as! ViewControllerOne
subVC.isSubVC = true
return subVC
} else {
return ViewControllerTwo
}
}
}
}
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
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:
Create a protocol that will notify the cell's controller that the button was pressed.
Create a property in your cell that holds a reference to the delegate that implements your protocol.
Call the protocol method on your delegate inside of the button action.
Implement the protocol method in your view controller.
When configuring your cell, pass the view controller to the cell as the delegate.
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
//...
}
//...
}
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)
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)
}
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.