peresent UIAlertController from UITableViewCell - swift

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

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.

Dissmiss UITableViewController from button in TableViewCell

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
}

issue pushing a new view controller from a UiView programmatically

I am trying to present a message viewController after pressing a button that is inside of a UiView. When I press the button, the message viewController presents itself, but it is missing some data. I have a separate viewController that allows me to push the controller. I will leave pictures below to show what I am talking about.
This is what the message view controller is supposed to look like, and this what the code looks like to get there
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
dismiss(animated: true) {
print("Dismiss completed")
let user = self.user[indexPath.row]
self.messagesController?.showChatControllerForUser(user)
}
}
This is what happens when I push the view from the UiView, and this is what the code looks like to get there.
#objc func handleNewMessage() {
let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
chatLogController.user = user
let navVC = UINavigationController(rootViewController:chatLogController)
self.window?.rootViewController?.present(navVC, animated: true, completion: nil)
}
All of the data pushes through, but the back button is missing, and I am not allowed to actually send a message. I am using two different methods to get to the same controller and I am expecting to see the same result. I was wondering if someone possibly new how to fix this issue?
Back button is not displaying because you are presenting the chatLogController using below function.
#objc func handleNewMessage() {
let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
chatLogController.user = user
let navVC = UINavigationController(rootViewController:chatLogController)
self.window?.rootViewController?.present(navVC, animated: true, completion: nil)
}
if you are push to chatLogController from messagesController then you are display a back button on chatLogController.
Note: back button is automatically display when you push to another viewController, it is not display when you present the viewController.

Swift - How to use pushViewController in NSObject

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)
}

Swift: Segue Error when calling

I have a segue setup in my swift class called ViewController and I am calling from a tableView didSelectRowAtIndexPath. I am using this code in my segue
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
var svc = segue!.destinationViewController as Homework;
svc.subject = subject_name
}
To tell it to set a varible called subject which is declared like this var subject:NSString! to a varible called subject_name. I then call it from my tableView didSelectRowAtIndexPath using this code prepareForSegue(UIStoryboardSegue(), sender: AnyObject?()).
This is my didSelectRowAtIndex
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
let indexPath = tableView.indexPathForSelectedRow();
var currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell
println(currentCell.textLabel!.text)
subject_name = currentCell.textLabel.text
// // Showing new storyboard
performSegueWithIdentifier("Homework", sender: self)
let vc : AnyObject! = self.storyboard.instantiateViewControllerWithIdentifier("Homework")
self.showViewController(vc as UIViewController, sender: vc)
}
But when I go and run the app, and click the table view cell I get this error "fatal error: unexpectedly found nil while unwrapping an Optional value", and a green arrow points towards var svc = segue!.destinationViewController as Homework;. I tested the exact same code out on another app expect with the segue being called when a button is clicked and that worked perfectly, I also tried answers from Calling segue programatically not working, and Preparing for segue in embedded tableView in Swift. Both of these answers did not work.
If I understood it correctly, you are calling prepareForSegue(). You shouldn't, that is automatically invoked.
What you should do instead is calling performSegueWithIdentifier(identifier: String, sender: AnyObject?). That triggers a segue invocation, which automatically executes prepareForSegue().
The identifier parameter is the one you set from IB: select the segue and look at the attributes inspector.
Besides that, #AnthonyKong's answer is a safer way to deal with optionals (a segue in this case) - that ensures that no runtime exception is thrown.
Addendum
Looking at your updated question, specifically at the implementation of didSelectRowAtIndexPath. The last 2 lines:
let vc : AnyObject! = self.storyboard.instantiateViewControllerWithIdentifier("Homework")
self.showViewController(vc as UIViewController, sender: vc)
are redundant - if you perform a segue, that will instantiate the destination view controller, so you don't have to do it manually. Remove those lines.
You should do this instead:
if let svc = segue!.destinationViewController as? Homework {
svc.subject = subject_name
}
It is because you might get passed other segues which do not have Homework as desination VC