Dissmiss UITableViewController from button in TableViewCell - swift

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
}

Related

How to dismiss UIViewController from UITableViewCell

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.

peresent UIAlertController from UITableViewCell

I didn't succeed to perform UIAlertController from UITableViewCell
I have already tried to create a segue programmatically and then perform it(code num 1), but it crash at appDelegate (the crash happened at appDelegate)
I tried to use code 2 and it did nothing(that printed :" whose view is not in the window hierarchy!" maybe it connected?),
I tried to call function in viewController.Swift to present and/or segue and it didn't succeed as well
the alert should appear only when a specific button in a cell is pressed
code 1:
UIApplication.shared.keyWindow?.rootViewController?.present(refreshAlert, animated: true, completion: nil)
code 2:
var segue = UIStoryboardSegue.init(identifier: "goAlart", source: ViewController(), destination: refreshAlert)
segue.perform()
the exception that I got at appDelegate is: Thread 1: signal SIGABRT
when I delete "segue.perform()" I got no errors
As mentioned in comments, good place to show the alert would be didSelectRow at.
I can't see the rest of the code, but I assume you have your UIAlertController initialised and property refreshAlert point's to it. If refreshAlert is stored in ViewController, then this should work:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
present(refreshAlert, animated: true, completion: nil)
}
EDIT:
Since you need to show the alert on button press in cell, good way to do so, would be a delegate protocol:
protocol MyCusomCellDelegate: NSObjectProtocol {
func didPressAlertButton()
}
In your cell, you declare delegate property:
weak var delegate: MyCusomCellDelegate?
On pressing the button, you call delegate method:
delegate?.didPressAlertButton()
In ViewController you conform to the protocol:
extension ViewController: MyCusomCellDelegate {
func didPressAlertButton() {
present(refreshAlert, animated: true, completion: nil)
}
}
And lastly, in cellForRowAt indexPath
cell.delegate = self
if you need to show different alert for different cells, then you should pass some data in the delegate call.
In "tableViewCell.swift," put:
weak var delegate:yourViewController!
and then you have access to all variables from viewController and all features, like:
present(refreshAlert, animated: true, completion: nil)
And, in "cellForRowAt indexPath," put:
cell.delegate = self

go to VC from button inside xib file that is used as a tableHeaderView

I have a TableSectionHeader xib file with a button in it (and some other stuff). This xib file is used as the custom header of a UItableview inside my PostViewController.
I want to be able to click on that button to show the detail about the cell. However, as the button is INSIDE the xib file, the IBAction is inside the TableSectionHeader.Swift (which inherits from UITableViewHeaderFooterView). This means that I can not segue or instantiate a VC.
How could I go from this button that is inside the xib file to another VC?
You need to get an outlet of your button into your xib class not an action , and when you create and return the header in your view controller , you will have a reference of your button before you return the header, then addTarget to your button and tag with selector to the method that is going to handle going to your next view controller .
In your xib class drag and connect the button :
class YearSectionHeader : UITableViewHeaderFooterView {
#IBOutlet car button :UIButton!
}
In your view controller table view header method (example) don't forget to change the class to and identifier to the correct one your using:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header : YearSectionHeader = tableView.dequeueReusableHeaderFooterViewWithIdentifier("TableHeader") as! YearSectionHeader
header.button.addTarget(self, action: #selector(self.handlingMethodName(_:)), forControlEvents: UIControlEvents.TouchUpInside)
header.button.tag = section
return header
}
sender.tag is your section number, you can perform the segue here :
func handlingMethodName(sender:UIButton){
print(sender.tag)
}
Just piggybacking off of Aaoli's answer here - this code works. I couldn't believe it. You need to connect the IBAction to the XIB file, and also have the exact same IBAction line but without the connection in the View Controller.
XIB File (connect to the cell, but do not add any code:
#IBAction func completePressed(_ sender: UIButton) {
}
View Controller:
#IBAction func completePressed(_ sender: UIButton) {
print("holler")
}
Now, in your View controller where your table view is, put the following code:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header : TestCell = itemTableView.dequeueReusableCell(withIdentifier: "testCell") as! TestCell
header.compLabel.addTarget(self, action: #selector(self.completePressed(_:)), for: UIControl.Event.touchUpInside)
header.compLabel.tag = section
return header
}
The fact that this works is amazing, and i have no idea why

How would I unhide a button if user performs segue in swift?

I have a button inside a cell (PFQueryTableViewController) that is hidden and I want to unhide it when the user performs a certain segue that I call programatically.
When the user taps the cell it segues to a view controller which displays the contents of the cell full screen... I want the button to unhide in this cell when the segue is called so when the user goes back to the table of cells they can see it on the cell they just tapped.
How can I do this?
Edit after questions:
inside cellRowForIndexPath I have the following for the button
cell.myButton.tag = indexPath.row
cell.myButton.addTarget(self, action: "pressed:", forControlEvents: UIControlEvents.TouchUpInside)
cell.myButton.hidden = true
And the segue itself carries information from the cell (stored in Parse backend) across to FullPostViewController from AllPostsTableViewController. The code for that is this (would I call the unhide in here somewhere?):
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showFullPost", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showFullPost" {
let indexPath = self.tableView.indexPathForSelectedRow
let fullPostVC = segue.destinationViewController as! FullPostViewController
let object = self.objectAtIndexPath(indexPath)
fullPostVC.post = object?.objectForKey("postContent") as? String
let likeCount = object!.objectForKey("likedBy")!.count
fullPostVC.likesCounted = String(likeCount)
self.tableView.deselectRowAtIndexPath(indexPath!, animated: true)
}
}
(Answer thoroughly edited after thorough edit of question)
One possible solution follows below.
Since you mention table cells (each containing a button; I'll assume UIButton), I assume you populate your table view cells with UITableViewCell objects; some fancy subclass to the latter. In this class:
If you haven't already, create an #IBOutlet from your button as a property in this class.
Overload the method setSelected(...) to un-hide your button in case the UITableViewCell is selected (which will precede the segue)
Hence, in your UITableViewCell subclass, you should be able to do something along the lines:
// ...TableViewCell.swift
Import UIKit
// ...
class ...TableViewCell: UITableViewCell {
// Properties
#IBOutlet weak var button: UIButton!
// button contained in UITableViewCell
// ...
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// If table cell is selected (after which segue will follow),
// un-hide button.
if (selected) {
button.hidden = false
}
}
}
Hopefully this will achieve your goal.

Passing values between ViewControllers based on list selection in Swift

I'm trying to pass the selected index number of a listView selection from one ViewController to another but am running into an issue with the tableView didSelectRowAtIndexPath delegate runs slightly later than the prepareForSegue function.
Basically, in didSelectRowAtIndexPath, I seta variable, which is then picked up in the prepareForSegue. The issue is that prepareForSegue seems to run the second that the cell is selected and before the didSelectRowAtIndexPath function is called so my variable is not passed.
My main bits of code are:
tableView didSelectRowAtIndexPath delegate, which sets 'selectedResult'...
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
selectedResult = indexPath.item
txtNameSearch.resignFirstResponder() //get rid of keyboard when table touched.
println("saved result")
//tableView.deselectRowAtIndexPath(indexPath, animated: false)
//var tappedItem: ToDoItem = self.toDoItems.objectAtIndex(indexPath.row) as ToDoItem
//tappedItem.completed = !tappedItem.completed
//tableView.reloadData()
}
prepareForSegue function which sends the variable as 'toPass' to the other ViewController ('detailViewController'):
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
if (segue.identifier == "detailSeque") {
println("preparing")
var svc = segue!.destinationViewController as detailViewController
svc.toPass = selectedResult
//println(tableView.indexPathsForSelectedRows().
//svc.toPass = tableView.indexPathForSelectedRow()
}
}
Thanks in advance
If you have an outlet to your tableView in your ViewController, you can just call indexPathForSelectedRow in prepareForSegue.
If your ViewController is a subclass of UITableViewController, then you can do:
let row = self.tableView.indexPathForSelectedRow().row
println("row \(row) was selected")
If your ViewController is not a subclass of UITableViewController, set up an IBOutlet to the UITableView in your view controller:
#IBOutlet var tableView: UITableView!
and wire that up in Interface Builder and call it from prepareForSegue as show above.
Instead of triggering your segue directly from a storyboard action, why don't you try programmatically calling performSegueWithIdentifier in your didSelectRowAtIndexPath method, after selectedResult is set?