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

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

Related

how to call/show action sheet for taking or choose photo from gallery on Main VC after tapped on TableViewCell Image(swift)

Here I Try MVVM pattern to achieve TableView but for showing alert I face problems , it compiles successfully but not showing alert.
[Result] on the tapped on profile pic which is in tableview cell, I want to show alert
TableViewCell
import Foundation
import UIKit
class ParentsImageCell : UITableViewCell {
weak var myVC : ProfileOfParentsDetailsViewController?
var parentProfileVC = ProfileOfParentsDetailsViewController()
#IBOutlet weak var imageProfile : UIImageView!
var items : ParentProfileViewModelItem? {
didSet {
guard let items = items as? ParentProfileViewModelProfileItem else {
return
}
imageProfile?.image = UIImage(named: items.profileImg)
}
}
static var nib:UINib {
return UINib(nibName: identifier, bundle: nil)
}
static var identifier: String {
return String(describing: self)
}
override func awakeFromNib() {
super.awakeFromNib()
imageProfile?.layer.cornerRadius = 62
imageProfile?.clipsToBounds = true
imageProfile?.contentMode = .scaleAspectFill
imageProfile?.backgroundColor = UIColor.lightGray
//Add Tapped Gesture
imageProfile.isUserInteractionEnabled = true
let gesture = UITapGestureRecognizer(
target: self,
action: #selector(didTappedChangeProfilePic))
gesture.numberOfTapsRequired = 1
gesture.numberOfTouchesRequired = 1
imageProfile.addGestureRecognizer(gesture)
}
#objc private func didTappedChangeProfilePic(){
print("tapped on imageView")
presentPhotoActionSheet()
}
override func prepareForReuse() {
super.prepareForReuse()
imageProfile?.image = nil
}
}
extension ParentsImageCell : UIImagePickerControllerDelegate ,UINavigationControllerDelegate {
func presentPhotoActionSheet(){
let actionSheet = UIAlertController(title: "Profile Picture", message: "How would you write to select a picture", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
actionSheet.addAction(UIAlertAction(title: "Take Photo", style: .default, handler: {[weak self] _ in
self?.presentCamera()
}))
actionSheet.addAction(UIAlertAction(title: "Choose Photo", style: .default, handler: { [weak self]_ in
self?.presentPhotoPicker()
}))
myVC?.present(actionSheet , animated: true)
}
func presentCamera(){
let vc = UIImagePickerController()
vc.sourceType = .camera
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func presentPhotoPicker(){
let vc = UIImagePickerController()
vc.sourceType = .photoLibrary
vc.delegate = self
vc.allowsEditing = true
myVC?.present(vc , animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info:
[UIImagePickerController.InfoKey : Any]) {
guard let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage else {
return
}
self.imageProfile.image = selectedImage
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
ViewModel
class ParentProfileViewModel: NSObject {
var items = [ParentProfileViewModelItem]()
var reloadSections: ((_ section: Int) -> Void)?
override init() {
super.init()
guard let data = dataFromFile("ServerData"),
let profile = Profile(data: data) else {
return
}
// initialization code will go here
if let profile = profile.pictureUrl {
let profileItem = ParentProfileViewModelProfileItem(profileImg: profile)
items.append(profileItem)
}
if let name = profile.fullName {
let nameItem = ParentProfileViewModelNameItem(name: name)
items.append(nameItem)
}
if let email = profile.email {
let emailItem = ParentProfileViewModelEmailItem(email: email)
items.append(emailItem)
}
let coach = profile.coach
if !coach.isEmpty {
let coachItem = ParentProfileViewModelCoachItem(coach: coach)
items.append(coachItem)
}
let candidate = profile.candidate
if !candidate.isEmpty {
let candidateItem = ParentProfileViewModelCandidateItem(candidate: candidate)
items.append(candidateItem)
}
}
}
//MARK:- TableviewDatasource & Delegates
extension ParentProfileViewModel: UITableViewDataSource {
//Number of section
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
//Number of RowInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let item = items[section]
guard item.isCollapsible else {
return item.rowCount
}
if item.isCollapsed {
return 0
} else {
return item.rowCount
}
}
//Cell for row at indexpath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// we will configure the cells here
let item = items[indexPath.section]
switch item.type {
case .profileImg:
let vc = ProfileOfParentsDetailsViewController()
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsImageCell.identifier, for: indexPath) as? ParentsImageCell {
cell.items = item
cell.myVC = vc
return cell
}
case .fullName:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsFulNameCell.identifier, for: indexPath) as? ParentsFulNameCell {
cell.items = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: ParentsEmailCell.identifier, for: indexPath) as? ParentsEmailCell {
cell.items = item
return cell
}
case .candidate:
if let item = item as? ParentProfileViewModelCandidateItem, let cell = tableView.dequeueReusableCell(withIdentifier: CandidatCell.identifier, for: indexPath) as? CandidatCell {
let candidate = item.candidate[indexPath.row]
cell.item = candidate
return cell
}
case .coach:
if let item = item as? ParentProfileViewModelCoachItem, let cell = tableView.dequeueReusableCell(withIdentifier: ParentCoachCell.identifier, for: indexPath) as? ParentCoachCell {
cell.item = item.coach[indexPath.row]
return cell
}
}
return UITableViewCell()
}
}
ViewController
import Foundation
import UIKit
class ProfileOfParentsDetailsViewController: UIViewController {
let viewModel = ParentProfileViewModel()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
viewModel.reloadSections = { [weak self] (section: Int) in
self?.tableView?.beginUpdates()
self?.tableView?.reloadSections([section], with: .fade)
self?.tableView?.endUpdates()
}
tableView?.dataSource = viewModel
tableView.delegate = viewModel
tableView?.estimatedRowHeight = 250
tableView?.rowHeight = UITableView.automaticDimension
tableView?.register(ParentsImageCell.nib, forCellReuseIdentifier: ParentsImageCell.identifier)
tableView?.register(ParentsEmailCell.nib, forCellReuseIdentifier: ParentsEmailCell.identifier)
tableView?.register(ParentsFulNameCell.nib, forCellReuseIdentifier: ParentsFulNameCell.identifier)
tableView?.register(CandidatCell.nib, forCellReuseIdentifier: CandidatCell.identifier)
tableView?.register(ParentCoachCell.nib, forCellReuseIdentifier: ParentCoachCell.identifier)
tableView?.register(ParentsHeaderView.nib, forHeaderFooterViewReuseIdentifier: ParentsHeaderView.identifier)
}
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM
I try to get called alert Sheet , but I Failed and also comment about my approach towards MVVM
I try to called the tableview data source and delegate in VM

Cannot call value of non-function type 'UITableView?' in UITapGestureRecognizer method

My table tap gesture recogniser calls a method that displays a message informing that no row values are present, and some should be added. This happens when user touches anywhere inside the table view.
The error is in tableTapped inside the if let statement.
I've tried deleting and recreating the tableView in story builder. Also tried 'homework' instead of 'tableView' and the parameter name. Also tried removing 'self.' before 'tableView'.
I have this exact same method on another class, and it works fine so I guess there's something different about the class it's not working in. I just can't pinpoint it!
import UIKit
class homeworkViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var clear: UIButton!
#IBOutlet weak var all: UIButton!
#IBOutlet weak var chosen: UIButton!
var languages = [String]()
var chosenLanguage = String()
var chosenLanguageRemoved = false
var homeworkWords = [String]()
var chosenLanguageEnabled = false
let wordString = "words"
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(test))
let tap = UITapGestureRecognizer(target: self, action: #selector(tableTapped))
self.tableView.backgroundView = UIView()
self.tableView.backgroundView?.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
}
#objc func tableTapped(tap:UITapGestureRecognizer) {
let location = tap.location(in: self.tableView)
let path = self.tableView.indexPathForRow(at: location)
if let indexPathForRow = path {
self.tableView(self.tableView, didSelectRowAt: indexPathForRow)
} else {
if homeworkWords.isEmpty == true {
let ac = UIAlertController(title: "Add Homework!", message: nil, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
ac.addAction(okAction)
present(ac, animated: true)
}
}
}
#IBAction func chosen(_ sender: UIButton) {
let ac = UIAlertController(title: "Use Chosen Language", message: "Add or clear choosen language words to quickly set homework.", preferredStyle: .alert)
// add user actions, "Cancel" to abort
if chosenLanguageEnabled == false {
ac.addAction(UIAlertAction(title: "Set", style: .default, handler: chosenLanguage))
}
if chosenLanguageEnabled == true {
ac.addAction(UIAlertAction(title: "Clear", style: .default, handler: clearChosenLanguage))
}
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
}
func chosenLanguage(action: UIAlertAction!) {
homeworkWords.removeAll()
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
if var savedWords = defaults.object(forKey: "\(chosenLanguage)\(wordString)") as? [String] {
print("savedWords in chosenLanguage in homeworkViewController are: \(savedWords)")
homeworkWords = savedWords
saveWords()
savedWords.removeAll()
chosenLanguageEnabled = true
print("chosenLanguageEnabled in chosenLanguage method in homeworkViewController is: \(chosenLanguageEnabled)")
print("homeworkWords in chosenLanguage method in homeworkViewController are: \(homeworkWords)")
print("savedWords after clear all in chosenLanguage method in homeworkViewController are: \(savedWords)")
}
}
}
func clearChosenLanguage(action: UIAlertAction!) {
homeworkWords.removeAll()
saveWords()
chosenLanguageEnabled = false
}
#IBAction func allWords(_ sender: UIButton) {
homeworkWords.removeAll()
for language in languages {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
if var savedWords = defaults.object(forKey: "\(language)\(wordString)") as? [String] {
print("savedWords array is: \(savedWords)")
homeworkWords.append(contentsOf: savedWords)
saveWords()
savedWords.removeAll()
print("allWords method homeworkWords are: \(homeworkWords)")
print("allWords savedWords after clear all: \(savedWords)")
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let delete = UITableViewRowAction(style: .default, title: "Delete") { (action, indexPath) in
self.homeworkWords.remove(at: indexPath.row)
self.saveWords()
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .left)
tableView.endUpdates()
// delete item at indexPath
if self.homeworkWords.isEmpty == true {
self.clear.isEnabled = false
self.clear.alpha = 0.25
}
}
delete.backgroundColor = UIColor.red
if let cell = tableView.cellForRow(at: indexPath) {
cell.isHighlighted = true
}
return [delete]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//print("In cellForRowAt function")
let cell = tableView.dequeueReusableCell(withIdentifier: "homework", for: indexPath)
let word = homeworkWords[indexPath.row]
cell.textLabel?.text = word.capitalized
//print(cell)
return cell
}
func loadHomeworkWords() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
if var savedHomeWorkWords = defaults.object(forKey: "homeworkWords") as? [String] {
print("savedLanguages array is: \(savedHomeWorkWords)")
homeworkWords.removeAll()
homeworkWords = savedHomeWorkWords
savedHomeWorkWords.removeAll()
print("savedHomeWorkWords after removeAll: \(savedHomeWorkWords)")
print("loadHomeworkWords homeworkWords are: \(homeworkWords)")
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
loadHomeworkWords()
tableView.reloadData()
}
func saveWords() {
if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
defaults.set(homeworkWords, forKey: "homeworkWords")
print("saveWords homeworkWords homeworkViewController are: \(homeworkWords)")
}
}
}
I expect to see a UIAlertAction message telling the user they need to add some table row values by tapping on either the 'All words' button or 'chosen language' button, if there are no values in any of the table rows.
Instead I get the pre-compile error message:
Cannot call value of non-function type 'UITableView?'

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.

Why are the buttons on my UIAlert duplicating?

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

Cannot convert value of type "[(title: String, description : String, pubDate : String, link : String)]?" to type 'NSArray' in coercion

class NewsViewController: UITableViewController, UISearchResultsUpdating {
var rssItems : [(title: String, description : String, pubDate : String, link : String)]?
var filteredRssItems = [String]()
var resultSearchController = UISearchController()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.separatorColor = UIColor.clearColor()
self.view.backgroundColor = UIColor(colorLiteralRed: 1.4, green: 1.4, blue: 1.4, alpha: 1)
self.resultSearchController = UISearchController(searchResultsController: nil)
self.resultSearchController.searchResultsUpdater = self
self.resultSearchController.dimsBackgroundDuringPresentation = false
self.resultSearchController.searchBar.sizeToFit()
self.tableView.tableHeaderView = self.resultSearchController.searchBar
self.tableView.reloadData()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let feedParser = FeedParser()
feedParser.parseFeed("http://rss.etnews.co.kr/Section902.xml", completionHandler: { (rssItems: [(title: String, description: String, pubDate: String, link: String)]) -> Void in
self.rssItems = rssItems
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .None)
})
})
tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let rssItems = rssItems else {
return 0
}
if (self.resultSearchController.active) {
return self.filteredRssItems.count
} else {
return rssItems.count
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! NewsTableViewCell
if (self.resultSearchController.active) {
cell.titleLabel.text = filteredRssItems[indexPath.row]
cell.descriptionLabel.text = filteredRssItems[indexPath.row]
cell.dateLabel.text = filteredRssItems[indexPath.row]
} else {
cell.titleLabel.text = rssItems![indexPath.row].title
cell.descriptionLabel.text = rssItems![indexPath.row].description
cell.dateLabel.text = rssItems![indexPath.row].pubDate
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let item = rssItems?[indexPath.row]
if let url = NSURL(string: item!.link) {
let safariController = SFSafariViewController(URL: url, entersReaderIfAvailable: true)
presentViewController(safariController, animated: true, completion: nil)
}
tableView.deselectRowAtIndexPath(indexPath, animated: false)
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 190.0
}
#IBAction func shartBtnTapped(sender: AnyObject) {
let actionSheet = UIAlertController(title: "", message: "Share your Note", preferredStyle: UIAlertControllerStyle.ActionSheet)
// Configure a new action for sharing the note in Twitter.
let tweetAction = UIAlertAction(title: "Share on Twitter", style: UIAlertActionStyle.Default) { (action) -> Void in
}
// Configure a new action to share on Facebook.
let facebookPostAction = UIAlertAction(title: "Share on Facebook", style: UIAlertActionStyle.Default) { (action) -> Void in
}
// Configure a new action to show the UIActivityViewController
let moreAction = UIAlertAction(title: "More", style: UIAlertActionStyle.Default) { (action) -> Void in
let activityViewController = UIActivityViewController(activityItems: [self.title!], applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivityTypeMail]
self.presentViewController(activityViewController, animated: true, completion: nil)
}
let dismissAction = UIAlertAction(title: "Close", style: UIAlertActionStyle.Cancel) { (action) -> Void in
}
actionSheet.addAction(tweetAction)
actionSheet.addAction(facebookPostAction)
actionSheet.addAction(moreAction)
actionSheet.addAction(dismissAction)
presentViewController(actionSheet, animated: true, completion: nil)
}
func showAlertMessage(message: String!) {
let alertController = UIAlertController(title: "EasyShare", message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Okay", style: UIAlertActionStyle.Default, handler: nil))
presentViewController(alertController, animated: true, completion: nil)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredRssItems.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.rssItems as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredRssItems = array as! [String]
self.tableView.reloadData()
}
#IBAction func logoutBtnTapped(sender: AnyObject) {
NSUserDefaults.standardUserDefaults().setValue(nil, forKey: "uid")
self.dismissViewControllerAnimated(true, completion: nil)
}
please help me~ i have a problem.
let array = (self.rssItems as NSArray).filteredArrayUsingPredicate(searchPredicate) => Cannot convert value of type “[(title: String, description : String, pubDate : String, link : String)]?” to type 'NSArray' in coercion
please advice me solution~
NSArrays can only contain types conforming to AnyObject. A tuple is not an AnyObject, and as such cannot be stored in an NSArray.
I see, however, that you cast the result of the filter to [String]. Do you perhaps mean to extract one of the entries in the tuple before applying the filter? For example, something like:
let array = (self.rssItems.map({ $0.description }) as NSArray).filteredArrayUsingPredicate(searchPredicate)
If not, you may have to consider an alternative to filteredArrayUsingPredicate. Consider instead using a Swift Array and filter with your own filtering function.