I need to dismiss ViewController from the corresponding TableViewCell, but I'm getting an error message, "Value of type 'TableViewCell' has no member 'dismiss'"
self.dismiss(animated: true, completion: nil)
How can I dismiss the ViewController from the corresponding TableViewCell
If you just want the solution and don't really care about the structure of the application, the following will work. As the other guy mentioned, this probably isn't the best way to structure your application.
Make a delegate to the TableViewCell.
protocol TableViewDismissDelegate {
func dismissViewController()
}
class YourTableViewClass {
var delegate: TableViewDismissDelegate?
...
}
In your table view delegate:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = mainTableView.dequeueReusableCell(withIdentifier: YourTableViewClass.identifier, for: indexPath) as! YourTableViewClass
cell.delegate = self
return cell
}
Make sure your view controller conforms to your protocol:
extension YourViewController: TableViewDismissDelegate {
func dismissViewController() {
self.dismiss(animated: true, completion: nil)
}
}
You cannot say self.dismiss in a UITableViewCell, as dismiss is a UIViewController command, and a UITableViewCell is not a UIViewController.
What I like to do in this situation is get a reference to the UIViewController so that I can tell it to dismiss. To do so, I create a UIResponder extension, like this:
extension UIResponder {
func next<T:UIResponder>(ofType: T.Type) -> T? {
let r = self.next
if let r = r as? T ?? r?.next(ofType: T.self) {
return r
} else {
return nil
}
}
}
That extension just walks up the responder chain looking for an instance of any class we care to name. So now self.next(ofType: UIViewController.self) is the view controller, and we can tell it to dismiss.
(There are plenty of other solutions, but that's just a solution that I happen to like.)
It may be argued, however, that you should never have gotten yourself in this situation in the first place. It is no business of a UITableViewCell to be telling anyone to dismiss anything. This is a violation of model-view-controller principles. You should probably be looking at a completely different architecture here.
Related
I have 2 viewcontrollers, firstVC and secondVC. I want to send data from the first one to update the secondVC's variable and UI.
I want to send tableView's indexPath.row with delegate when it is tapped in didSelectRowAt.
This is what I try, but when selecting a row the app crashes with error:
Unexpectedly found nil while implicitly unwrapping an Optional value
I tried to debug and saw that the delegate is nil, even if I put a method to make the delegate firstVC.
SecondVC:
func setupDelegate() {
let FirstVC = storyboard?.instantiateViewController(identifier: "FirstVC") as! FirstVC
FirstVC.selectionDelegate = self
}
}
extension SecondVC: setSelectionDelegate {
//this is never executed
func didChoose(index: Int) {
lbl.text = String(index)
}
}
FirstVC:
protocol setSelectionDelegate {
func didChoose(index: Int)
}
var selectionDelegate: setSelectionDelegate!
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let secondVC = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "secondVC") as? secondVC
secondVC?.setupDelegate()
self.selectionDelegate.didChoose(index: indexPath.row)
}
Each time you call instantiateViewController explicitly, you get a new instance of the view controller and this is NOT the same instance that you are seeing in your device/simulator.
With storyboard segues ( embed or present modally... ) you have override prepare(...) function to get a reference to desired view controller instance.
I have a UITableViewController that has a header view class with a UIButton in it that I've linked up in interface builder. I've got it working fine so that when you tap the button the code fires.
However, I have no idea what to write to dismiss the table view when you tap this button.
class headerCell: UITableViewCell {
#IBAction func exit () {
print("got pressed")
// tried: MyTableView().dismiss(animated: true, completion: nil)
// But did not work, did nothing?
}
}
class MyTableView: UITableViewController {
// All the code for the tableView is here...
}
MyTableView().dismiss(animated: true, completion: nil)
This makes a brand new table view controller and dismisses it. Since it was never even presented, this does nothing
You need to dismiss the one that contains the cell.
This is one way: https://stackoverflow.com/a/50334803/3937
But I recommend adding a weak reference to the header cell to the MyTableView and setting it on construction
Also, MyTableView is a ViewController, not a view. So, MyTableViewController is a better name.
After experimenting with the various answers, I've found that creating an IBAction is not the best way to do this, and instead I've simply added an IBOutlet for the button, then done this in the TableViewController:
#objc func exit () {
dismiss(animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell") as! headerCell
cell.exitButton.addTarget(self, action: #selector(exit), for: .touchUpInside)
return cell
}
I have a slide in/out side menu which is called when leftBarButtonItem in nab bar is tapped.
And I coded the slide menu with NSObject and I know NSObject doesn't have the pushViewController method.
navigationController?.pushViewController
I have a menu in UITableView in the NSObject and I want push viewController.
How can I make this work? Thank you.
import UIKit
class SlideMenuLauncher: NSObject, UITableViewDelegate,
UITableViewDataSource {
...
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC: UIViewController
switch indexPath.row {
case 0:
destinationVC = ControllerA()
case 1:
destinationVC = ControllerB()
default:
destinationVC = HomeController()
}
self.navigationController?.pushViewController(destinationVC, animated: false)
}
...
1.
You can make a #property of your parentVC in SlideMenuLauncher class.
var parentVC: UIViewController?
and then you can use this instead of self.
parentVC!.navigationController?.pushViewController(destinationVC, animated: false)
2.
You can post a notification instead of pushing from SlideMenuLauncher class and pass the destinationVC as object in notification. Observe this notification in your parentVC then fetch the destinationVC from notification object and push the controller.
3.
You can make a block or delegate of didSelectRowAt event.
Block Example:
/// Declare a block in `SideMenuLauncher`
typealias TableEventBlock = (_ controller: UIViewController) -> Void
var tableEventBlock: TableEventBlock?
/// In table did select method
if tableEventBlock != nil {
tableEventBlock!(destinationVC)
}
You need to define its call back in parentVC (You can define it anywhere, Do it in viewDidLoad:) and will have to use SideMenuLAuncher's instance.
sideMenuLauncherInstance.tableEventBlock = { controller in
self.navigationController?.pushViewController(controller, animated: false)
}
I have a View1 when i click on textbox i am going to view2(table view) to pick a value. I want to send the picked value to view1 for that textbox.
The controls are created programmatically so i am not using segue,IBActions.
I am trying to use the protocol methods still no success. Here is what i have tried.
class DynamicSuperView: UIViewController,UITableViewDelegate,UITableViewDataSource,dropdownDelegate,UITextFieldDelegate
{
func setValue(value:AnyObject)
{
print("dynamic view delegate method executed")
self.labelText = value as! String
//return selectedValue;
}
override func viewDidLoad() {
}
}
The second class is here with delegate method..
protocol dropdownDelegate {
func setValue(value: AnyObject);
}
class testClass: UIViewController,UITableViewDataSource,UITableViewDelegate,UISearchBarDelegate {
var delegate:dropdownDelegate! = nil
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vcName = names[indexPath.row]
print ("Table view cell clicked and value passed: \(vcName)")
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
}
self.navigationController?.popViewController(animated: true)
delegate.setValue(value: "Testing delegate")
}
}
Once i select the value in the tableview i am calling the delegate method and trying to pass that value but no success.
I don't want to create the new instance of the previous view controller because i will lose the data already entered by the user, so i am popping the current view controller and going to the previous view controller.
Please suggest if my approach i correct or not?
ERROR:
fatal error: unexpectedly found nil while unwrapping an Optional value
Thank you in advance
Try this:
view func ButtonPressed() {
print("Button Pressed!!") // This will create the new instance of the view controller.
let vc = UIStoryboard(name:"Main", bundle:nil).instantiateViewController(withIdentifier: "Storage") as! testClass
vc.delegate = self
self.navigationController?.pushViewController(vc, animated:true)
}
You are not setting the delegate to first view controller.
I am trying to pass information over from a collection view cell to another view controller, then dismiss the view controller. I am trying to do this with delegation as I think this is the only way, so far I have set up my protocol like so:
#objc protocol CollectionViewImageDelegate {
optional func selectedCell(row: NSIndexPath, data: UIImage)
}
I have added the property:
var delegate : CollectionViewImageDelegate?
and then called it in the didSelectItemAtIndexPath
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let imageData = self.sources[indexPath.row]
self.delegate?.selectedCell?(indexPath, data: imageData)
self.dismissViewControllerAnimated(true, completion: nil)
}
Then how do I call it other View Controller so far I have done:
class ProfileViewController: UIViewController, CollectionViewImageDelegate {
var colViewImage : CollectionViewVC
in the view did load:
colViewImage.delegate = self
and this but nothing happens:
func selectedCell(row: NSIndexPath, data: UIImage) {
println(data) //Nothing prints
}
Nothing is printing I don't know why?
I'm new to protocol and delegates etc and can't seem to work out why it isnt passing over the image??
Thanks
Your CollectionViewController is calling this selectedCell method but you haven't linked the delegate yet.
Make your other ViewController conform to collectionViewImageDelegate (which should conventionally be spelled with a capital C) and implement your method selectedCell in it.
Set the ViewController as delegate of the CollectionViewController.
Also, change self.delegate?.selectedCell!(indexPath, data: imageData) into self.delegate?.selectedCell?(indexPath, data: imageData) to call the optional method selectedCell only if it was implemented by the delegate.