Why are the buttons on my UIAlert duplicating? - swift

I'm attempting to build an app using Swift that's similar to the Notes app, but I'd like to have an Alert pop up when the user presses the "+" button to add a new Note - and the Alert would prompt the user to enter a name. The name is then added as a new row in the underlying table. So far, I've been able to do this, but every time i click the "+" sign, the buttons I've added to the Alert get re-added. In other words, the Alert should have an "OK" & a "Cancel" button. But on the 2nd time the Alert pops up, there are 2 "OK" buttons, and 2 "Cancel" buttons, and so on. Any ideas how i can fix this? Thanks for your help!
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var table: UITableView!
var data:[String] = []
//create alert controller
var alert = UIAlertController(title: "Name", message: "Enter a name", preferredStyle: .alert)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.title = "Notes"
//create "add" button
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addAlert))
self.navigationItem.rightBarButtonItem = addButton
//create edit button so we can delete rows
self.navigationItem.leftBarButtonItem = editButtonItem
load()
}
//function to add new note. This code is executed when the "Add" button is clicked
func addNote()
{
//code to prevent being able to add rows while in edit mode
if (table.isEditing) {
return
}
let textField = self.alert.textFields![0] as UITextField
let name:String = textField.text!
data.insert(name, at: 0)
let indexPath:IndexPath = IndexPath(row: 0, section: 0)
table.insertRows(at: [indexPath], with: .automatic)
save()
}
func addAlert()
{
alert.addTextField(configurationHandler: { (textField) -> Void in
textField.placeholder = "Enter Name"})
self.present(alert, animated: true, completion: nil)
//grab value from the text field and print when user clicks OK
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (UIAlertAction) in
let textField = self.alert.textFields![0] as UITextField
print("Text Field: \(textField.text!)")
self.addNote()
}))
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.textLabel?.text = data[indexPath.row]
return cell
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
//puts table in edit mode
table.setEditing(editing, animated: animated)
}
//function to delete rows from table
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
data.remove(at: indexPath.row)
table.deleteRows(at: [indexPath], with: .fade)
save()
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "detail", sender: nil)
}
func save()
{
UserDefaults.standard.set(data, forKey: "notes")
UserDefaults.standard.synchronize()
}
func load()
{
if let loadedData = UserDefaults.standard.value(forKey: "notes") as? [String]
{
data = loadedData
table.reloadData()
}
}

Put alert.addAction before presenting the alert. Try this code in addAlert() method
func addAlert()
{
var alert = UIAlertController(title: "Name", message: "Enter a name", preferredStyle: .alert)
alert.addTextField(configurationHandler: { (textField) -> Void in
textField.placeholder = "Enter Name"})
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (UIAlertAction) in
let textField = self.alert.textFields![0] as UITextField
print("Text Field: \(textField.text!)")
self.addNote()
}))
self.present(alert, animated: true, completion: nil)
}

You have to present the alert only after adding the actions(Buttons).
func addAlert()
{
alert.addTextField(configurationHandler: { (textField) -> Void in
textField.placeholder = "Enter Name"})
//grab value from the text field and print when user clicks OK
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (UIAlertAction) in
let textField = self.alert.textFields![0] as UITextField
print("Text Field: \(textField.text!)")
self.addNote()
}))
self.present(alert, animated: true, completion: nil)
}

Related

How do I call a function when UINavigationController UINavigationTabBar Item is tapped?

I want to be able to present a UIAlertAction when the user taps a UINavigationController UINavigationTabBar Item to remind them they need to enter some information before they can proceed to the next UIViewController that would otherwise be called from such a tap. The code that is called from the tap would check if an array is empty, if so, then display the UIAlertAction.
I've tried to create an instance of the UINavigationController's UINavigationTabBar item, to try and detect it being pushed, but this is messy as the UIViewController to be called has already been setup as a segue when the UINavigationTabBar item is tapped.
Do I need to backtrack and remove the existing segue and create the transition in a different way that allows my desired array checking code to be callable on the user's tap?
If you're pushing the next view controller using a segue, you would intercept that segue, do all your checks, and then inform the segue whether to go on or cancel the pushing. Basically in your view controller:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "NextViewControllerID" {
var isValid = true
// example of validation, do your stuff here
if myTextField.text?.count ?? 0 < 5 {
isValid = false
}
return isValid
}
// otherwise delegates the superclass
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
}
import UIKit
class WordsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
// write button selector method to check if there are any words.
var words = [String]()
var languages = [String]()
var chosenLanguage = String()
var textField: UITextField?
let wordString = "Words"
override func viewDidLoad() {
super.viewDidLoad()
chosenLanguageLoad()
loadInitialValues()
//let titleAttributes = [NSAttributedString.Key.font: UIFont(name: "AmericanTypewriter", size: 22)!]
//navigationController?.navigationBar.titleTextAttributes = titleAttributes
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(test))
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addNewWord))
print("languages array is: \(languages)")
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "Words" {
print("inside identifier == words if")
var isValid = true
if languages.isEmpty == true {
print("languages is empty is true")
isValid = false
}
return isValid
}
// otherwise delegates the superclass
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
}
#objc func test() {
if words.isEmpty == true {
let ac = UIAlertController(title: "Add Some Words", message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
ac.addAction(okAction)
present(ac, animated: true)
} else {
self.performSegue(withIdentifier: "Test", sender: self)
}
}
func chosenLanguageLoad() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
if let loadChosenLanguage = defaults.object(forKey: "languageChosen") as? String {
chosenLanguage = loadChosenLanguage
print("Chosen language is: \(chosenLanguage)")
}
}
}
#objc func loadInitialValues() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
words.removeAll()
tableView.reloadData()
print("Words array after clear all: \(words)")
print("chosenLanguage in loadInitialValues: \(chosenLanguage)")
print("\(chosenLanguage)\(wordString)")
if var savedWords = defaults.object(forKey: "\(chosenLanguage)\(wordString)") as? [String] {
print("savedWords array is: \(savedWords)")
words = savedWords
savedWords.removeAll()
print("savedWords array after clear all: \(savedWords)")
} else {
saveInitialWordValues(to: defaults)
}
print("Words is : \(words)")
print("Number of words: \(words.count)")
tableView.reloadData()
}
}
func saveInitialWordValues(to defaults: UserDefaults) {
switch chosenLanguage {
case "german":
words.append("Bear::Baissespekulant::0")
words.append("Camel::Kamel::0")
words.append("Cow::Rind::0")
words.append("Fox::Fuchs::0")
words.append("Goat::Geiß::0")
words.append("Monkey::Affe::0")
words.append("Pig::Schwein::0")
words.append("Rabbit::Karnickel::0")
words.append("Sheep::Schaf::0")
//words.append("::")
print(words)
defaults.set(words, forKey: "germanWords")
print("At end of saveInitialGermanValues")
case "french":
words.append("Bear::l'ours::0")
words.append("Camel::le chameau::0")
words.append("Cow::la vache::0")
words.append("Fox::le renard::0")
words.append("Goat::la chèvre::0")
words.append("Monkey::le singe::0")
words.append("Pig::le cochon::0")
words.append("Rabbit::le lapin::0")
words.append("Sheep::le mouton::0")
//words.append("::")
print(words)
defaults.set(words, forKey: "frenchWords")
print("At end of saveInitialFrenchValues")
default:
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
chosenLanguageLoad()
loadInitialValues()
}
override func viewDidAppear(_ animated: Bool) {
navigationController?.setToolbarHidden(true, animated: false)
super.viewDidAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
}
#objc func addNewWord() {
// create our alert controller
let ac = UIAlertController(title: "Add new word", message: nil, preferredStyle: .alert)
// add two text fields, one for English and one for French or German.
ac.addTextField { textField in
textField.placeholder = "English"
}
ac.addTextField { (textField) in
textField.placeholder = "\(self.chosenLanguage.capitalized)"
}
// create an "Add Word" button that submits the user's input
let submitAction = UIAlertAction(title: "Add Word", style: .default) { [unowned self, ac] (action: UIAlertAction!) in
// pull out the English and French words, or an empty string if there was a problem
let firstWord = ac.textFields?[0].text ?? ""
let secondWord = ac.textFields?[1].text ?? ""
let zeroTimesWrong = "0"
// submit the English and French word to the insertFlashcard() method
self.insertFlashcard(first: firstWord, second: secondWord, third: zeroTimesWrong)
}
// add the submit action, plus a cancel button
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
// present the alert controller to the user
present(ac, animated: true)
}
func insertFlashcard(first: String, second: String, third: String) {
guard first.count > 0 && second.count > 0 else { return }
let newIndexPath = IndexPath(row: words.count, section: 0)
words.append("\(first)::\(second)::\(third)")
tableView.insertRows(at: [newIndexPath], with: .automatic)
saveWords()
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .default, title: "Delete") { action, indexPath in
self.words.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .left)
tableView.endUpdates()
// delete item at indexPath
}
let edit = UITableViewRowAction(style: .normal, title: "Edit") { (action, indexPath) in
let ac = UIAlertController(title: "Edit word", message: nil, preferredStyle: .alert)
// add two text fields, one for English and one for French
ac.addTextField { textField in
let word = self.words[indexPath.row]
let split = word.components(separatedBy: "::")
let englishWord = split[0]
textField.placeholder = "\(englishWord)"
}
ac.addTextField { (textField) in
let word = self.words[indexPath.row]
let split = word.components(separatedBy: "::")
let foreignWord = split[1]
textField.placeholder = "\(foreignWord)"
}
// create an "Add Word" button that submits the user's input
let submitAction = UIAlertAction(title: "Edit Word", style: .default) { [unowned self, ac] (action: UIAlertAction!) in
// pull out the English and French words, or an empty string if there was a problem
let firstWord = ac.textFields?[0].text ?? ""
let secondWord = ac.textFields?[1].text ?? ""
guard firstWord.count > 0 && secondWord.count > 0 else { return }
// edit item at indexPath
self.words.remove(at: indexPath.row)
self.words.insert("\(firstWord)::\(secondWord)", at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .automatic)
tableView.insertRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
self.saveWords()
}
// add the submit action, plus a cancel button
ac.addAction(submitAction)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
// present the alert controller to the user
self.present(ac, animated: true)
}
edit.backgroundColor = UIColor.blue
return [delete, edit]
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return words.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//print("In cellForRowAt function")
let cell = tableView.dequeueReusableCell(withIdentifier: "Word", for: indexPath)
let word = words[indexPath.row]
if word != "::" {
let split = word.components(separatedBy: "::")
print(split[0])
cell.textLabel?.text = split[0]
cell.detailTextLabel?.text = ""
//print(cell)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
if let cell = tableView.cellForRow(at: indexPath) {
if cell.detailTextLabel?.text == "" {
let word = words[indexPath.row]
let split = word.components(separatedBy: "::")
cell.detailTextLabel?.text = split[1]
} else {
cell.detailTextLabel?.text = ""
}
}
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
words.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
saveWords()
}
func saveWords() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.polyglot") {
defaults.set(words, forKey: "\(chosenLanguage)\(wordString)")
}
}
}

Select a single cell in a UITableView to go to a UITableView to display different student lists data

Updated
I want to select each row to go to ViewController that has a TableView to display different student lists data, but for my code, it will display the same list as I created a new student object, How do I fix this?
The problem is I don't have a list of student data ahead until I click a single class cell to go to next ViewController, then I'll have a plus button to add student into a TableView.
ManageViewController Class:
class ManageViewController : UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var tableView: UITableView!
var classList = [Class]()
var selectedClass: StudentData?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Manage Classes"
tableView.dataSource = self
let fetchRequest: NSFetchRequest<Class> = Class.fetchRequest()
do {
let classList = try PersistenceService.context.fetch(fetchRequest)
self.classList = classList
self.tableView.reloadData()
} catch {}
// Do any additional setup after loading the view.
}
Add class button action:
#IBAction func onAddTapped(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Add Class", message: nil, preferredStyle: .alert)
alert.addTextField { (classListTF) in
classListTF.placeholder = "Enter Class"
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
let action = UIAlertAction(title: "Add", style: .default) { (_) in
guard let list = alert.textFields?.first?.text else { return }
print(list)
let subject = Class(context: PersistenceService.context)
subject.class_name = list
PersistenceService.saveContext()
self.classList.append(subject)
self.tableView.reloadData()
}
alert.addAction(action)
present(alert, animated: true)
}
Code for class cell selection:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
// Unwrap that optional
if let label = cell?.textLabel?.text {
print("Tapped \(label)")
}
self.performSegue(withIdentifier: "studentInfoView", sender: self)
}
StudentListViewController class:
class StudentListViewController: UIViewController, UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var studentList = [StudentData]()
var selectedStudent: StudentData?
override func viewDidLoad() {
super.viewDidLoad()
let fetchRequest: NSFetchRequest<StudentData> = StudentData.fetchRequest()
do {
let studentList = try PersistenceService.context.fetch(fetchRequest)
self.studentList = studentList
self.tableView.reloadData()
} catch {}
}
#IBAction func addStudentTapped(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Add Student", message: nil, preferredStyle: .alert)
alert.addTextField { (studentListTF) in
studentListTF.placeholder = "Enter name"
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
let action = UIAlertAction(title: "Add", style: .default) { (_) in
guard let student = alert.textFields?.first?.text else { return }
print(student)
let person = StudentData(context: PersistenceService.context)
person.student_name = student
PersistenceService.saveContext()
self.studentList.append(person)
self.tableView.reloadData()
}
alert.addAction(action)
present(alert,animated: true)
}
I have updated my additional code for ManageViewController class and StudentInfoViewContrller class. Sorry I'm a new Ios programmer.
You should add in an additional condition to be able to go to a different view based on the selected row like the ff:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
if indexPath.row == 0 {
self.performSegue(withIdentifier: "toViewA", sender: self)
} else {
self.performSegue(withIdentifier: "toViewA", sender: self)
}
}
You can pass the student data to your next view UIViewController using prepare(for segue: UIStoryboardSegue, sender: Any?) delegate.
In your UIViewController which has the UITableView with all the students listed, have a variable declared to keep the student from the table, along with your studentList variable.
var selectedStudent: StudentData?
After that, in the UITableView delegate method didSelectRowAt, set the value for the selectedStudent.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Make sure that the selected inexPath.row is within the bounds of the studentsList array.
if indexPath.row < studentList.count {
selectedStudent = studentList[indexPath.row]
performSegue(withIdentifier: "studentInfoView", sender: self)
}
}
Then, in the same UIViewController, assign the student property of your UIViewController which shows the student's details.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "studentInfoView" {
// Replace 'StudentInfoViewController' with your detail UIViewController class name.
let destinationVC = segue.destination as? StudentInfoViewController
destinationVC?.student = selectedStudent
}
}
Make sure that you have the student property declared in your StudentInfoViewController (or, whatever you are calling it).
class StudentInfoViewController: UIViewController {
var student: StudentData?
// .......
// .......
}
Then, in the viewDidLoad() or any other setup method in the StudentInfoViewController, you can setup the details of the student on the screen.

Long press gesture action sheet delete cell

I trying to code long press gesture on the cell in collection view but I haven't found about similar... I suppose I do to code something in the 'didSelectItemAt'.
Now works only if I tap in the first cell...
And then I have found on web to different solution based on swift 3.
Every one help me please? Thank you so much!
The image show when tap long on the first cell the action sheet. But don't work when I tap to other cell...
import UIKit
class RecipeCollViewController: UICollectionViewController, UITextFieldDelegate
{
struct Storyboard
{
static let leftAndRightPaddings: CGFloat = 2.0
static let numberOfItemsPerRow: CGFloat = 2.0
}
override func viewDidLoad() {
super.viewDidLoad()
RecipeDataManager.shared.recipeController = self
title = loc("TITLE_RECIPECOLL")
navigationController?.navigationBar.prefersLargeTitles = true
let collectionViewWidth = collectionView?.frame.width
let itemWidth = (collectionViewWidth! - Storyboard.leftAndRightPaddings) / Storyboard.numberOfItemsPerRow
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: itemWidth, height: 250)
}
override func numberOfSections(in collectionView: UICollectionView) -> Int
{
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return RecipeDataManager.shared.recipes.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RecipeCell", for: indexPath) as! RecipeViewCell
let recipe = RecipeDataManager.shared.recipes[indexPath.item]
cell.labelNameRecipe.text = recipe.titleRecipe
cell.imageViewRecipe.image = recipe.imageRecipe
cell.labelPrepareTime.text = String(recipe.recipeTimeInt)
cell.labelPeopleFor.text = recipe.peopleRecipe
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
{
}
// MARK: - NAVIGAZIONE
// Metodo che scatta quando l'utente tocca una delle celle della collectionView e apre il dettaglio
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "RecipeDetail"
{
if let indexPath = self.collectionView!.indexPathsForSelectedItems?.first
{
let recipeDetailVC = segue.destination as! DetailRecipeViewController
recipeDetailVC.recipe = RecipeDataManager.shared.recipes[indexPath.item]
}
}
}
// MARK: - UILongPressGestureRecognizer function for the cell recipe
#IBAction func popUpActionCell(longPressGesture : UILongPressGestureRecognizer)
{
let alertActionCell = UIAlertController(title: "Action Recipe Cell", message: "Choose an action for the selected recipe", preferredStyle: .actionSheet)
// Configure Remove Item Action
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { action in
// Delete selected Cell
let deleteRecipe: [RecipeDataManager] = []
if let indexPath = self.collectionView?.indexPathsForSelectedItems?.first
{
RecipeDataManager.shared.recipes.remove(at: indexPath.item)
RecipeDataManager.shared.salva()
self.collectionView?.deleteItems(at: [indexPath])
}
print("Cell Removed")
})
// Configure Cancel Action Sheet
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { acion in
print("Cancel actionsheet")
})
alertActionCell.addAction(deleteAction)
alertActionCell.addAction(cancelAction)
self.present(alertActionCell, animated: true, completion: nil)
self.collectionView!.reloadData()
}
}
With my pleausure, I have found the finally solution at my problem.
Post the code for other new developer needs help!
// MARK: - Long Press Gesture Action Sheet
#IBAction func popUpActionCell(longPressGesture : UILongPressGestureRecognizer)
{
// Delete selected Cell
let point = longPressGesture.location(in: self.collectionView)
let indexPath = self.collectionView?.indexPathForItem(at: point)
// let cell = self.collectionView?.cellForItem(at: indexPath!)
if indexPath != nil
{
let alertActionCell = UIAlertController(title: "Action Recipe Cell", message: "Choose an action for the selected recipe", preferredStyle: .actionSheet)
// Configure Remove Item Action
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { action in
RecipeDataManager.shared.recipes.remove(at: indexPath!.row)
print("Cell Removed")
self.collectionView!.reloadData()
})
// Configure Cancel Action Sheet
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { acion in
print("Cancel actionsheet")
})
alertActionCell.addAction(deleteAction)
alertActionCell.addAction(cancelAction)
self.present(alertActionCell, animated: true, completion: nil)
}
}

Save Alert-textfieldinput in Swift

I want to have a table view, where you can add a new element when you click on the last cell. I can't find out, how I save whats put in the textfield, after you click "ok".
As you can probably see from my code, I'm very new to programming and it is likely way to complicated. It would be super cool, if someone could help me out.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var companyTableView: UITableView!
var psgTextField: UITextField?
var emTextField: UITextField?
var passengers = [""]
var button: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
configureButton()
companyTableView.delegate = self
companyTableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return passengers.count
}
#objc func buttonTapped(sender: UITapGestureRecognizer) {
let alertController = UIAlertController(title: "Enter name and email",
message: nil,
preferredStyle: .alert)
alertController.addTextField(configurationHandler: psgTextField)
alertController.addTextField(configurationHandler: emTextField)
let okAction = UIAlertAction(title: "Submit", style: .default, handler: self.okHandler)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true)
}
func psgTextField(textField: UITextField!) {
psgTextField = textField
psgTextField?.placeholder = "Name"
}
func emTextField(textField: UITextField!) {
emTextField = textField
emTextField?.placeholder = "Email"
}
func okHandler(alert: UIAlertAction!) {
let psgStr = (psgTextField?.text)!
passengers.removeLast()
passengers.append(psgStr)
passengers.append("")
}
func configureButton() {
button = UILabel()
let frame = CGRect(x: 0, y: 5, width: 300, height: 30)
button?.frame = frame
button?.text = " Add Passenger"
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(buttonTapped(sender:)))
button?.addGestureRecognizer(tapGesture)
button?.isUserInteractionEnabled = true
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let passenger = passengers[indexPath.row]
if let cell = companyTableView.dequeueReusableCell(withIdentifier: "cell") {
cell.textLabel?.text = passenger
if cell.textLabel?.text == "" {
cell.contentView.addSubview(button!)
} else {
// Nothing
}
return cell
}
return UITableViewCell()
}
}
When I now click on "Submit", the alert just goes of and nothing changed.
You need to call companyTableView.reloadData() in your okHandler method.

performSegueWithIdentifier not always working in Swift

I've got a UITableView with a few cells on there and I want to segue to another screen whenever I push one of these cells. My code works, sort of.... But sometimes I've to push the cell twice in order to segue!
Does anyone know why?
func tableView(gamesListTableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row < GamesList.count) {
self.gameState = GamesList[indexPath.row];
performSegueWithIdentifier("presentGame", sender: self);
} else {
let alert = UIAlertController(title: "Swipe!", message: "Swipe Invite To The Left", preferredStyle: UIAlertControllerStyle.Alert)
let alertAction = UIAlertAction(title: "OK!", style: UIAlertActionStyle.Default) { (UIAlertAction) -> Void in }
alert.addAction(alertAction)
presentViewController(alert, animated: true) { () -> Void in }
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "presentGame") {
var presentGame = segue.destinationViewController as! Game;
presentGame.gameState = self.gameState;
}
}
There is known issue in the wild that require dummy dispatch_async call as workaround - http://openradar.appspot.com/19563577 likely in future versions it will go away. Here is how code with a workaround will looks like:
func tableView(gamesListTableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row < GamesList.count) {
self.gameState = GamesList[indexPath.row];
dispatch_async(dispatch_get_main_queue(), {}); //http://openradar.appspot.com/19563577
performSegueWithIdentifier("presentGame", sender: self);
} else {
let alert = UIAlertController(title: "Swipe!", message: "Swipe Invite To The Left", preferredStyle: UIAlertControllerStyle.Alert)
let alertAction = UIAlertAction(title: "OK!", style: UIAlertActionStyle.Default) { (UIAlertAction) -> Void in }
alert.addAction(alertAction)
presentViewController(alert, animated: true) { () -> Void in }
}
}