TableViewCell is not showing alertView - ios

In OrderFormViewController I've a tableView and the tableViewCell I've a cameraButton that will show an alert when tapped.
Now when OrderFormViewController is presented (not pushed) it loads tableView and it's cell (Row count is 1 hard coded).
I've the following code under the IBAction of cameraButton:
#IBAction func cameraButtonPressed(_ sender: Any) {
//DispatchQueue.main.async {
let alert = UIAlertController(title: "Choose Image From", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera))
{
self.imagePicker.sourceType = UIImagePickerControllerSourceType.camera
self.imagePicker.allowsEditing = true
UIApplication.shared.keyWindow?.rootViewController?.present(self.imagePicker, animated: true, completion: nil)
}
else
{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
}
}))
alert.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { _ in
self.imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
self.imagePicker.allowsEditing = false
UIApplication.shared.keyWindow?.rootViewController?.present(self.imagePicker, animated: true, completion: nil)
}))
//alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
//}
}
I've searched for this so this is why I tried UIApplication.shared.keyWindow?.rootViewController and also tried to run it from DispatchQueue.main.async but some how UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil) is not working.
UPDATE If I push the view controller then it works. But I need a solution for presented view controller.

I would strongly recommend refactoring the whole solution to use delegates - delegate the action of presenting the alert from UITableViewCell to OrderFormViewController, and there simply use
self.present(alert, animated: true, completion: nil)
Because using UIApplication.shared.keyWindow?.rootViewController is really fragile.
So just to sketch it up for you, I believe the best approach is this:
class OrderFormViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomCell
// set the delegate
cell.delegate = self
return cell
}
}
extension OrderFormViewController: CustomCellDelegate {
func customCellDelegateButtonPressed(_ customCell: CustomCell) {
let alert = UIAlertController(title: "Choose Image From", message: nil, preferredStyle: .alert)
// configure the alert
// now the presentation of the alert is delegated here to OrderFormViewController, so it can simply use self to present
self.present(alert, animated: true, completion: nil)
}
}
protocol CustomCellDelegate: class {
func customCellDelegateButtonPressed(_ customCell: CustomCell /*, potentially more parameters if needed */)
}
class CustomCell: UITableViewCell {
// rest omitted for brevity
weak var delegate: CustomCellDelegate?
#IBAction func cameraButtonPressed(_ sender: Any) {
delegate?.customCellDelegateButtonPressed(self)
}
}

Related

Action Sheet Takes 10+ seconds to appear

In my app I have table view controller. When the user types on the last row of tableview the action sheet should appears to ask for sign out. Here is my code for this action:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
//..
case 1:
//..
case 2:
//..
case 3:
let logOutMenu = UIAlertController(title: nil, message: "Are you sure want to logout?", preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let logOutAction = UIAlertAction(title: "Log out", style: .default, handler: { (UIAlertAction) in
print("sign out")
})
logOutMenu.addAction(cancelAction)
logOutMenu.addAction(logOutAction)
self.present(logOutMenu, animated: true, completion: nil)
default: break
}
}
Everything works fine, however there is strange behaviour of action sheet. It takes approximately 10 seconds (or even more) to show action sheet. The same behaviour I have noticed on real device as well. What I'm doing wrong?
You have to call deselect row at index path without animation otherwise there two animations at the same time which confuses and gets longer time
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
//..
case 1:
//..
case 2:
//..
case 3:
let logOutMenu = UIAlertController(title: nil, message: "Are you sure want to logout?", preferredStyle: .actionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let logOutAction = UIAlertAction(title: "Log out", style: .default, handler: { (UIAlertAction) in
print("sign out")
})
logOutMenu.addAction(cancelAction)
logOutMenu.addAction(logOutAction)
self.present(logOutMenu, animated: true, completion: nil)
// Deselect your row it will fix it
tableView.deselectRow(at: indexPath, animated: false)
default: break
}
}

Saving the tableview text after edited swift

I have managed to create a tableview with an array or messages and when i tapped a tableview cell it shows an alertview and a uitextfield that you can change the text of the tableview. What i'm trying to do now is to save the tableview text ("message") when it changed. Because when I go to another view controller and go back it does not save. I am new on swift programming and wondering how can i achieve that. I have read some articles that i need to use nsuserdefaults but i am not familiar with that. here is my code below. Thanks guys.
class Messages: UITableViewController {
#IBOutlet var uitableView: UITableView!
var tField: UITextField!
var index: Int?
var msgArray: [String]!
var messages = ["Message 1", "Message 2", "Message 3", "Message 4", "Message 5", "Message 6"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let messagesCell = messages[indexPath.row]
cell.textLabel?.text = messagesCell
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = tableView.indexPathForSelectedRow
index = path?.row
changeMessage()
}
func changeMessage() {
let alert = UIAlertController(title: "Message", message: "Enter new preset message", preferredStyle: UIAlertControllerStyle.alert)
alert.addTextField(configurationHandler: configurationTextField)
let doneAction = UIAlertAction(title: "Done", style: UIAlertActionStyle.default, handler:{ (UIAlertAction) in
let modelString = self.tField.text
self.messages[self.index!] = modelString!
self.tableView.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(doneAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
func configurationTextField(textField: UITextField!){
if (textField) != nil {
tField = textField
textField.text = messages[index!]
}
}
}
Try to use nsuserdefaults. In SWIFT 3
override func viewWillAppear(_ animated: Bool) {
//Get the array
if let array = UserDefaults.standard.object(forKey:"messages") as? Array<String> {
self.messages = array
}
}
func changeMessage() {
let alert = UIAlertController(title: "Message", message: "Enter new preset message", preferredStyle: UIAlertControllerStyle.alert)
alert.addTextField(configurationHandler: configurationTextField)
let doneAction = UIAlertAction(title: "Done", style: UIAlertActionStyle.default, handler:{ (UIAlertAction) in
let modelString = self.tField.text
self.messages[self.index!] = modelString!
UserDefaults.standard.set(self.messages, forKey: "messages") // Set the array with changes
self.tableView.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(doneAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
let defaults = UserDefaults.standard
defaults.set("123", forKey: "oldString")
print("\(UserDefaults.standard.value(forKey: "oldString")!)")
This may help you.

How can I get the text from the alert's textfield and make it the cell's text label?

class CoursesTableViewController: UITableViewController {
var nameOfCourse = "";
#IBAction func newCourse(_ sender: AnyObject) {
//1. Create the alert controller.
// var nameOfCourse = "";
let alert = UIAlertController(title: "New Course", message: "Enter the course name.", preferredStyle: .alert);
//2. Add the text field. You can configure it however you need.
alert.addTextField(configurationHandler: { (textField) -> Void in
textField.placeholder = ""
})
//3. Grab the value from the text field, and print it when the user clicks OK.
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
let textField = alert.textFields![0] as UITextField
self.nameOfCourse = textField.text!
print(self.nameOfCourse)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
}))
// 4. Present the alert.
self.present(alert, animated: true, completion: nil)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cells = tableView.dequeueReusableCell(withIdentifier: "cells", for: indexPath) as? CoursesTableViewCell
cells?.textLabel.text = self.nameOfCourse
return cells!
}
Reload the table view after you've successfully set nameOfCourse (below print(self.nameOfCourse)).

Displaying action sheet when tableview cell is tapped

Then I am trying to call action sheet when cell is tapped, and this is what I did
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let alertController = UIAlertController(title: "Action Sheet", message: "What do you like to do", preferredStyle: .alert)
let okButton = UIAlertAction(title: "Done", style: .default, handler: { (action) -> Void in
print("Ok button tapped")
})
let deleteButton = UIAlertAction(title: "Skip", style: .destructive, handler: { (action) -> Void in
print("Delete button tapped")
})
alertController.addAction(okButton)
}
When i am tapping cell, alert controller is not showing up. What am I missing?
You're almost there, make sure you add your deleteButton-action as well and present the alertController using present(alertController, animated: true, completion: nil)
Your action sheet does not show, because you are not presenting it.
present(alertController, animated: true /** or false */, completion: nil)
We create a function didSelectContact, and use func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) to handle selected Cell in tableview. If each cell tapped this action sheet will open.
func didSelectContact(){
let alert = UIAlertController(title: "Choose Once", message: "What you want do with contact", preferredStyle: UIAlertControllerStyle.actionSheet)
let call = UIAlertAction(title: "Call", style: UIAlertActionStyle.default) { (UIAlertAction) in
}
let sms = UIAlertAction(title: "SMS", style: UIAlertActionStyle.default) { (UIAlertAction) in
}
let cancel = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)
alert.addAction(call)
alert.addAction(sms)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
self.didSelectContact()
}

Can not change to another view controller with multiple segues. How can I resolve it?

I'm using multiple segues in Swift and I have a UIAlertView in a table view. The UIAlertView shows perfectly. When I click the first option (Ver Mapa), it changes perfectly to another view controller (Mapa) with segue (go_to_mapa). But when I press the second option (Ver detalle) with segue (go_to_detalle) to change to the other view controller, it doesn't work. It doesn't do anything. How can I resolve this problem?
override func prepareForSegue(segue: (UIStoryboardSegue!), sender: AnyObject!) {
var refreshAlert = UIAlertController(title: "Menu", message: "Seleccione una opcion", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Ver Mapa", style: .Default, handler: { (action: UIAlertAction!) in
if (segue.identifier == "go_to_mapa") {
var svc = segue!.destinationViewController as Mapa;
svc.cuenta = self.cuenta
svc.user = self.user
svc.password = self.password
let indexPath = self.tableView.indexPathForSelectedRow();
let currentCell = self.tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
svc.Device = currentCell.detailTextLabel!.text!
svc.desc = currentCell.textLabel!.text!
self.navigationController?.pushViewController(svc, animated: false)
}
}))
refreshAlert.addAction(UIAlertAction(title: "Ver Vehiculo", style: .Default, handler: { (action: UIAlertAction!) in
if (segue.identifier == "go_to_detalle") {
let vc = segue.destinationViewController as Detalle_Vehiculo
}
}))
refreshAlert.addAction(UIAlertAction(title: "Ejecutar comandos", style: .Default, handler: { (action: UIAlertAction!) in
println("Ejecutar comandos")
}))
presentViewController(refreshAlert, animated: true, completion: nil)
}
prepareForSegue: calls shorty before presenting new controller. From the doc:
Notifies the view controller that a segue is about to be performed.
So you shouldn't show any alerts, pop-ups whatever in that method. Instead, you should take destinationViewController and adjust it properly before presenting.
Instead, you should show alerts in tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) like the following:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var refreshAlert = UIAlertController(title: "Menu", message: "Seleccione una opcion", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "Ver Mapa", style: .Default, handler: { (action: UIAlertAction!) in
self.performSegueWithIdentifier("go_to_mapa", sender:self)
}))
refreshAlert.addAction(UIAlertAction(title: "Ver Vehiculo", style: .Default, handler: { (action: UIAlertAction!) in
self.performSegueWithIdentifier("go_to_detalle", sender:self)
}))
refreshAlert.addAction(UIAlertAction(title: "Ejecutar comandos", style: .Default, handler: { (action: UIAlertAction!) in
println("Ejecutar comandos")
}))
presentViewController(refreshAlert, animated: true, completion: nil)
}
And prepare it:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "go_to_mapa" {
var svc = segue!.destinationViewController as Mapa
svc.cuenta = self.cuenta
svc.user = self.user
svc.password = self.password
let indexPath = self.tableView.indexPathForSelectedRow();
let currentCell = self.tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
svc.Device = currentCell.detailTextLabel!.text!
svc.desc = currentCell.textLabel!.text!
}
else if segue.identifier == "go_to_detalle" {
let vc = segue.destinationViewController as Detalle_Vehiculo
}
}

Resources