I want to delete a Unit from my Core Data Application. When user presses delete button on the Cell, I present a warning.
the code was
let alert = UIAlertController(title: "Warning!", message: "Are you sure to delete \"\(currentUnit)\" and all of its containing objects?", preferredStyle: .Alert)
I wanted to update the warning message with the objects count so I wrote
currentUnitObjectsCount = getSelectedUnitObjectsCount() //Which connects to database and returns just an integer
let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete",handler: { (action, indexPath) -> Void in
let currentUnit = self.units[indexPath.row].title!
let alert = UIAlertController(title: "Warning!", message: "Are you sure to delete \"\(currentUnit)\" and all of its \(currentUnitObjectsCount) containing objects?", preferredStyle: .Alert)
let yesAction = UIAlertAction(title: "Yes", style: .Default) /* handler: { (action : UIAlertAction) -> Void in */ { (action : UIAlertAction) in
if let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)?.managedObjectContext{
let unitToDelete = self.fetchResultController.objectAtIndexPath(indexPath) as! Unit
managedObjectContext.deleteObject(unitToDelete)
do{
try managedObjectContext.save()
}catch{
print(error)
}
}
self.checkUnits()
}
let noAction = UIAlertAction(title: "No", style: UIAlertActionStyle.Default, handler: {
(action : UIAlertAction!) -> Void in
tableView.reloadData() //to return the tableView to its normal view
})
alert.addAction(yesAction)
alert.addAction(noAction)
self.presentViewController(alert, animated: true, completion: nil)
})
deleteAction.backgroundColor = UIColor.redColor()
return [editAction]
}
Now when i swipe the tableViewCell, it swipes but the delete button does not appear but when I tap the Delete button position in the swiped cell, the warning appears which means the button is there but I cant see it.
returning the code to the first style every thing works fine, but the second one acts very strange.
When I put this line of code
currentUnitObjectsCount = getSelectedUnitObjectsCount() //Which connects to database and returns just an integer
just after the the
let deleteAction = ...
currentUnitObjectsCount = getSelectedUnitObjectsCount()
let currentUnit = self.units[indexPath.row].title!
.
.
.
and swipe a cell, rows of the table disappear
I dont find why these happen.
Related
I want to have an alert action change the text of a UILabel in my View. Code below:
#IBAction func statusChange(_ sender: UIButton){
let playalert = UIAlertController(title: "OPERATION: \(sender.accessibilityIdentifier!) NOT POSSIBLE", message: "Convert op to PLAY mode to proceed", preferredStyle: .alert)
let recordalert = UIAlertController(title: "OPERATION: \(sender.accessibilityIdentifier!) NOT POSSIBLE", message: "Convert op to STOP mode to proceed", preferredStyle: .alert)
let statechangealert = UIAlertController(title: "OPERATION: \(sender.accessibilityIdentifier!) WILL NOW PROCEED", message: "", preferredStyle: .alert)
let stopAction = UIAlertAction(title: "Convert", style: .default, handler: { action in
self.statusDVR.text = "STOP"//"\(sender.accessibilityIdentifier!)"
})
let continueAction = UIAlertAction(title: "Continue", style: .default, handler: { action in
self.statusDVR.text = ""//"\(sender.accessibilityIdentifier!)"
})
let returnAction = UIAlertAction(title: "Return", style: .default, handler: { action -> Void in
})
playalert.addAction(stopAction)
playalert.addAction(returnAction)
recordalert.addAction(stopAction)
recordalert.addAction(returnAction)
statechangealert.addAction(continueAction)
//record was tapped, check if DVR stopped first
if (sender.tag == 5 && pwrStat == true ) {
//if not stopped send alert
if statusDVR.text != "STOP" {
self.present(recordalert, animated:true, completion: nil)
//if user tapped returned action
if statusDVR.text != "STOP" {
self.present(statechangealert, animated:true, completion: nil)
} else {
return
}
} else {
statusDVR.text = "RECORD"
}
The stopAction should convert a UILAbel called statusDVR in the view controller, but it doesn't do it.
I haven't tried implementing the other Actions yet, because I am stuck trying to figure out why my UILabel's text won't change. Thank you for any help :)
First of all, this code makes no sense:
if statusDVR.text != "STOP" {
self.present(recordalert, animated:true, completion: nil)
if statusDVR.text != "STOP" {
self.present(statechangealert, animated:true, completion: nil)
That code tries to present two alerts at the same time. That is illegal.
I think you think that when you say
self.present(recordalert, animated:true, completion: nil)
...your code magically comes to a stop while the user interacts with the alert and then proceeds after the user dismisses the alert. That’s not the case. Your code never magically stops; it just keeps right on going.
As for the actual question you asked about, the problem is simply that what you're doing is illegal:
playalert.addAction(stopAction)
playalert.addAction(returnAction)
recordalert.addAction(stopAction)
recordalert.addAction(returnAction)
No! You cannot take one UIAlertAction and somehow magically "share" it between two different UIAlertControllers. Every UIAlertController needs UIActions that belong to it alone. In other words, do not call addAction on the same UIAlertAction twice.
Just to demonstrate more simply, try running this code:
let action1 = UIAlertAction(title: "Test", style: .default) {
_ in print("test")
}
let alert1 = UIAlertController(title: "Hello", message: nil, preferredStyle: .alert)
let alert2 = UIAlertController(title: "Hello2", message: nil, preferredStyle: .alert)
alert1.addAction(action1)
alert2.addAction(action1)
self.present(alert1, animated: true, completion: nil)
The alert appears, you tap the Test button, the alert disappears — but nothing prints in the console. Now comment out the next to last line:
// alert2.addAction(action1)
... and run the code again. This time, when you tap Test, "test" appears in the console.
Basically, the question says it all. I thought about an alert but i need to actually have a text box inside and be able to save the text to a label. Any ideas on what I should use in this? I don't want to create a new view controller into a navigation. I just simply want a pop up view to open and close as needed.
All suggestions are greatly appreciated. Thanks!
You can add a textfield in your alertController.
Please refer to this answer https://stackoverflow.com/a/33000328/1754559
let alertController = UIAlertController(title: "Your alert", message: "", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default, handler: {
alert -> Void in
guard let textField = alertController.textFields?[0] as UITextField else { return }
yourLabel.text = textField.text
})
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: {
(action : UIAlertAction!) -> Void in })
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Whatever"
//Other textField configurations
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
If a textfield is empty, I would like to make an UIAlertController to remind, that something has to be filled out in the textfield. Now I have the following code:
#IBAction func saveDetails(segue: UIStoryboardSegue) {
let dController = segue.source as? EntryTableViewController
if let text = dController?.bezeichnungTextField.text, text.isEmpty {
let alert = UIAlertController(title: "No description", message: "Please fill out description", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "OK", style: .default)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}else {
guard let menge = dController?.mengeTextField.text, let bezeichnung = dController?.bezeichnungTextField.text, let kategorie = dController?.categoryButtonOutlet.titleLabel?.text else {return}
self.saveItem(menge: menge, bezeichnung: bezeichnung, kategorie: kategorie)
self.tableView.reloadData()
}
}
Now actually it works when I press the button to return to the first controller. But the UIAlertController only appears for a very short moment and then disappears automatically. Is there a mistake in my code or isn't it possible to call the UIAlertController on a unwind segue?
Thank you for your help
You just show the alert controller and then immediately unwind. The solution is to check for the empty textfield before unwinding.
Make IBAction for your button then check the textfield if it is empty and then perforn your segue programmatically.
I had created a normal segment controller which has two category, category 1 and 2 respectively. Now I have add button, which push me to the new view controller to add an item. When clicking on done button for adding an item I have an alert controller which show the category in which I have to save the item. But I don't know how to get that item in particular segment. If anyone can help.
Thanks
#IBAction func done(sender: AnyObject) {
if let item = itemToEdit {
item.text = textField.text!
item.dateTime = dateTime
textField.becomeFirstResponder()
//item.text = textAreaDescription.text!
//textAreaDescription.becomeFirstResponder()
delegate?.itemDetailViewController(self, didFinishEditingItem: item)
} else {
let alertController = UIAlertController(title: "Choose Category", message: "Choose Category To Save Your Item.", preferredStyle: .Alert)
let toDo = UIAlertAction(title: "Category 1", style: .Default) { (action) in
let item = NoToDoItem()
item.text = self.textField.text!
//item.text = textAreaDescription.text!
item.dateTime = self.dateTime
self.delegate?.itemDetailViewController(self, didFinishAddingItem: item)
}
alertController.addAction(toDo)
let notSure = UIAlertAction(title: "Category 2", style: .Default){ (action) in
let notSureItem = NotSureItem()
notSureItem.text = self.textField.text!
//item.text = textAreaDescription.text!
notSureItem.dateTime = self.dateTime
self.delegate?.itemDetailViewController(self, didFinishAddingNotSureItem: notSureItem)
}
alertController.addAction(notSure)
presentViewController(alertController, animated: true, completion: nil)
}
}
use commitEditingstyle, then your cells will still display the Delete button when you swipe. Use editingStyleForRowAtIndexPath: instead. Put an if statement in to test which segment is selected, and return UITableViewCellEditingStyle.None (to disable swipe to delete) or .Delete (to enable swipe to delete) accordingly. If you want to be able to delete the cells by putting the table view into editing mode, then also test tableView.editing to determine whether to use .Delete or .None.
I've been looking up a lot of tutorials on UIAlertController. Thus far, the way I found was to activate a UIAlertController by linking it to a button or label and then call a IBAction.
I tried to replicate the code to automatically pop an alert when user enters the app (I wanted to ask the user if they want to go through the tutorial). However, I keep getting the error:
Warning: Attempt to present UIAlertController on MainViewController whose view is not in the window hierarchy!
Then I tried to add the UIAlertController to the MainViewController via addChildViewController and addSubview. However, I get the error:
Application tried to present modally an active controller
I figured that I cannot use the presentViewController function and commented it out.
The UIAlertController is displayed BUT when I tried to click on the cancel or the never button, this error occurs.
Trying to dismiss UIAlertController with unknown presenter.
I am really stumped. Can someone share what I am doing wrong? Thank you so much. Here is the code.
func displayTutorial() {
alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
self.addChildViewController(alertController)
self.view.addSubview(alertController.view)
alertController.didMoveToParentViewController(self)
alertController.view.frame.origin.x = self.view.frame.midX
alertController.view.frame.origin.y = self.view.frame.midY
//alertController.popoverPresentationController?.sourceView = self.view*/
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Destructive) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
//self.presentViewController(alertController, animated: false) {}
}
I found the solution. Apparently, I cannot call the UIAlertController from the func viewDidLoad. I must call the function from viewDidAppear. So my code now is
override func viewDidAppear(animated: Bool) {
if tutorial == 0 {
displayTutorial(self.view)
}
}
func displayTutorial(sender:AnyObject) {
let alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Default) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
self.presentViewController(alertController, animated: true, completion: nil)
if let pop = alertController.popoverPresentationController {
let v = sender as UIView
pop.sourceView = view
pop.sourceRect = v.bounds
}
}
Thanks to this posting: Warning: Attempt to present * on * whose view is not in the window hierarchy - swift
Below UIAlertController with extension would help you show alert with dynamic number of buttons with completion handler for selected index
extension UIViewController {
func displayAlertWith(message:String) {
displayAlertWith(message: message, buttons: ["Dismiss"]) { (index) in
}
}
func displayAlertWith(message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
displayAlertWithTitleFromVC(vc: self, title: Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String, andMessage: message, buttons: buttons, completion: completion)
}
func displayAlertWithTitleFromVC(vc:UIViewController, title:String, andMessage message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for index in 0..<buttons.count {
let action = UIAlertAction(title: buttons[index], style: .default, handler: {
(alert: UIAlertAction!) in
if(completion != nil){
completion(index)
}
})
alertController.addAction(action)
}
DispatchQueue.main.async {
vc.present(alertController, animated: true, completion: nil)
}
}
}
If you need to auto dismiss the alert you can call dismiss on presented view controller after some delay.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
vc.dismiss(animated: true, completion: nil)
}
Hope this might help you.