I'm trying to make a pros and cons list in swift, but whenever I delete a con it deletes a pro. I think that it is a problem with index path being linked to both the pros and cons view controller but I don't know how or where I can separate them
class prosConsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
#IBOutlet var prosTableViewOutlet: UITableView!
#IBOutlet var consTableViewOutlet: UITableView!
#IBOutlet var tableViewOutlet: UITableView!
var colleges : [NetCollege] = []
#IBOutlet var consTableView: UITableView!
var collegesTwo : [NetCollegeTwo] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
if tableView == tableViewOutlet
{
return colleges.count
}
else
{
return collegesTwo.count
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
if tableView == tableViewOutlet
{
let cell = tableViewOutlet.dequeueReusableCellWithIdentifier("cellID") as! tableViewCell
//the line under maybe?
let college = colleges[indexPath.row]
cell.textLabel?.text = college.name
return cell
}
else
{
let cellTwo = consTableView.dequeueReusableCellWithIdentifier("IDCell") as! tableViewCell
let collegeTwo = collegesTwo[indexPath.row]
cellTwo.textLabel?.text = collegeTwo.conName
return cellTwo
}
}
override func viewDidLoad()
{
super.viewDidLoad()
editButtonItem().tag = 0
func shouldAutorotate() -> Bool {
return false
}
func supportedInterfaceOrientations() -> Int {
return UIInterfaceOrientation.LandscapeRight.rawValue
}
}
#IBAction func plusButtonTwo(sender: UIBarButtonItem)
{
let alertTwo = UIAlertController(title: "Add Con", message: nil, preferredStyle: .Alert)
alertTwo.addTextFieldWithConfigurationHandler
{ (textField) -> Void in
textField.placeholder = "Add Con Here"
}
let cancelActionTwo = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alertTwo.addAction(cancelActionTwo)
let addActionTwo = UIAlertAction(title: "Add", style: .Default) { (action) -> Void in
let addCollegesTextFieldTwo = (alertTwo.textFields?[0])! as UITextField
let netCollegeTwo = NetCollegeTwo(nameTwo: addCollegesTextFieldTwo.text!)
self.collegesTwo.append(netCollegeTwo)
self.consTableView.reloadData()
}
alertTwo.addAction(addActionTwo)
self.presentViewController(alertTwo, animated: true, completion: nil)
}
#IBAction func onTappedPlusButton(sender: UIBarButtonItem)
{
let alert = UIAlertController(title: "Add Pro", message: nil, preferredStyle: .Alert)
alert.addTextFieldWithConfigurationHandler
{ (textField) -> Void in
textField.placeholder = "Add Pro Here"
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(cancelAction)
let addAction = UIAlertAction(title: "Add", style: .Default) { (action) -> Void in
let addCollegesTextField = (alert.textFields?[0])! as UITextField
let netCollege = NetCollege(name: addCollegesTextField.text!)
self.colleges.append(netCollege)
self.tableViewOutlet.reloadData()
}
alert.addAction(addAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if editingStyle == UITableViewCellEditingStyle.Delete
{
colleges.removeAtIndex(indexPath.row)
tableViewOutlet.reloadData()
}
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool
{
return true
}
If you want to implement all this in one view controller, you can try this:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
{
if editingStyle == UITableViewCellEditingStyle.Delete
{
if tableView == tableViewOutlet
{
colleges.removeAtIndex(indexPath.row)
tableView.reloadData()
}
else
{
collegesTwo.removeAtIndex(indexPath.row)
tableView.reloadData()
}
}
}
But in this case better solution would be to create two classes called like DataSourceOne, DataSourceTwo (or TableViewModelOne, TableViewModelTwo), and implement all related logic there. This even could be two instances of just one class DataSource, depending on what exactly you need. Then you can instantiate those helper classes in viewDidLoad and assign them to dataSource and delegate properties of your table views. Your will also need to hold strong reference for them somewhere, because dataSource and delegate properties are week.
Related
I'm working on core data crud operation.I've Person object and when user click on add (+) button user can add the person name and person name will show in tableview. When user click on any tableView cell it shows the alert and current tap cell person show in alert text field and user can edit the name of person but my logic is not working fine and user is not editing.I've one more problem like when i run the app the pervious users record not showing but it will showing after I will enter the new user and fetch all the pervious users see the code and guide me thanks in advance.
ViewController Class:
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items : [Person] = [] {
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: StundentCell.self), bundle: .main), forCellReuseIdentifier: String(describing: StundentCell.self))
}
#IBAction func addPerson(_ sender: UIBarButtonItem) {
showAlert()
}
private func showAlert(_ title:String = "Add new Item", message: String = "what is your name"){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addTextField()
let subbmitBtn = UIAlertAction(title: "Add Person", style: .default) { (action) in
let textField = alertController.textFields![0]
let newPerson = Person(context:self.context)
newPerson.name = textField.text
newPerson.age = 20
newPerson.gender = "Male"
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(subbmitBtn)
self.present(alertController, animated: true, completion: nil)
}
func fetchPerople(){
do{
self.items = try context.fetch(Person.fetchRequest())
}
catch{
}
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.configurePersonCell(tableView, cellForRowAt: indexPath)
}
func configurePersonCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StundentCell.self)) as? StundentCell else {return UITableViewCell()}
cell.studentData = items[indexPath.row]
cell.delete.tag = indexPath.row
cell.delete.addTarget(self, action: #selector(deleteRecord(sender:)), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var person = items[indexPath.row].name
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
let getName = alertController.textFields![0]
person = getName.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}
func gotoPersonDetails(personData:Person){
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
#objc func deleteRecord(sender: UIButton){
if items.count > 0{
let personRemove = self.items[sender.tag]
self.context.delete(personRemove)
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
}
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var items : [Person] = [] {
didSet{
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
tableView.register(UINib(nibName: String(describing: StundentCell.self), bundle: .main), forCellReuseIdentifier: String(describing: StundentCell.self))
}
#IBAction func addPerson(_ sender: UIBarButtonItem) {
showAlert()
}
private func showAlert(_ title:String = "Add new Item", message: String = "what is your name"){
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addTextField()
let subbmitBtn = UIAlertAction(title: "Add Person", style: .default) { (action) in
let textField = alertController.textFields![0]
let newPerson = Person(context:self.context)
newPerson.name = textField.text
newPerson.age = 20
newPerson.gender = "Male"
do {
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(subbmitBtn)
self.present(alertController, animated: true, completion: nil)
}
func fetchPerople(){
do{
self.items = try context.fetch(Person.fetchRequest())
}
catch{
}
}
}
extension ViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
self.configurePersonCell(tableView, cellForRowAt: indexPath)
}
func configurePersonCell(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
guard let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: StundentCell.self)) as? StundentCell else {return UITableViewCell()}
cell.studentData = items[indexPath.row]
cell.delete.tag = indexPath.row
cell.delete.addTarget(self, action: #selector(deleteRecord(sender:)), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var person = items[indexPath.row].name
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
//get the person name in alert text field
let getName = alertController.textFields![0]
//edit the name of the person object
person = getName.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}
func gotoPersonDetails(personData:Person){
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
#objc func deleteRecord(sender: UIButton){
/* fro api calling or firebase for deleting the rows in tableview
if items.count > 0 {
items.remove(at:sender.tag)
}
*/
if items.count > 0{
let personRemove = self.items[sender.tag]
//remove the person
self.context.delete(personRemove)
//save the data
do {
try self.context.save()
}
catch{
}
//refetch the data
self.fetchPerople()
}
}
You are modifying the name string but you don't update the record.
Get the whole person
let person = items[indexPath.row] // can be a constant because it's reference type
assign the name to the text field
alertField.text = person.name
and assign the edited name back
let nameField = alertController.textFields![0]
person.name = nameField.text
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let person = items[indexPath.row]
let alertController = UIAlertController(title: "Edit Person", message: "Edit name", preferredStyle: .alert)
alertController.addTextField()
let alertField = alertController.textFields![0]
alertField.text = person.name
let saveBtn = UIAlertAction(title: "Save", style: .default) { (action) in
let nameField = alertController.textFields![0]
person.name = nameField.text
do{
try self.context.save()
}
catch{
}
self.fetchPerople()
}
alertController.addAction(saveBtn)
self.present(alertController, animated: true, completion: nil)
}
How can I change the title of an UIAlertAction when I click the button ?
I want to click that button and from "Enable" to make it "Disable" for example.
I spent a lot of time trying to achieve this but I can't manage to do it.
Here is a small Demo with my issue: https://github.com/tygruletz/ChangeTitleOfAlertAction
Here is my code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
tableView.tableFooterView = UIView()
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showOptions()
}
func showOptions(){
var enable = "Enable"
let disable = "Disable"
let applyOn = UIAlertAction(title: enable, style: .default, handler: { (action: UIAlertAction!) in
enable = disable
})
let actionSheet = configureActionSheet()
actionSheet.addAction(applyOn)
self.present(actionSheet, animated: true, completion: nil)
}
func configureActionSheet() -> UIAlertController {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(cancel)
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad ){
actionSheet.popoverPresentationController?.sourceView = self.view
actionSheet.popoverPresentationController?.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
actionSheet.popoverPresentationController?.permittedArrowDirections = []
}
return actionSheet
}
}
And here is a capture of screen:
Thank you if you try to help me !
Please follow below code:
Define property in your UIViewController
var selectedIndexPath:IndexPath!
Add argument in showOptions method
func showOptions(indexPath:IndexPath){
var status = "Enable"
if selectedIndexPath == indexPath{
status = "Disable"
}
let applyOn = UIAlertAction(title: status, style: .default, handler: { (action: UIAlertAction!) in
if self.selectedIndexPath == indexPath{
self.selectedIndexPath = nil
}else{
self.selectedIndexPath = indexPath
}
})
let actionSheet = configureActionSheet()
actionSheet.addAction(applyOn)
self.present(actionSheet, animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
showOptions(indexPath: indexPath)
}
Note:
If you are going with this approach, Then you will never faced cell usability issue.
When I try to run my app, I get the error stating:
fatal error: unexpectedly found nil while unwrapping an Optional
value.
Can anybody tell me if its a way to locate the line where the problem is?
Unfortunately, I don't get a red line where the simulator crashes.
I pasted in all of the code, but the problem must have to do with the alert function because it worked fine until I tried to implement that.
import UIKit
var list = ["Visa code: 1234", "Mastercard code: 4321"]
class notesVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var messageLabel: UILabel!
var userMessage = "Sample text"
var theUserText: UITextField?
#IBOutlet weak var tabelView: UITableView!
#IBAction func addItemButton(_ sender: Any) {
let alertController = UIAlertController(title:"title",
message: "message",
preferredStyle: .alert)
alertController.addTextField(configurationHandler: theUserTextFunc)
let okAction = UIAlertAction(title: "OK",
style: .default,
handler: self.okHandler)
let cancleAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(okAction)
alertController.addAction(cancleAction)
self.present(alertController, animated: true)
}
func theUserTextFunc(textField: UITextField){
theUserText = textField
}
func okHandler(alert: UIAlertAction!){
list.append((theUserText?.text)!)
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return(cell)
}
// Swipe to delete an item
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
list.remove(at: indexPath.row)
tabelView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
messageLabel.text = userMessage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func customInit(userMessage: String) {
self.userMessage = userMessage
}
}
I tried your code. It works flawless except that you missed to reload on table on OkHandler.
So I suspect issue would be with ur IBOutlet or IBAction connections. Check around that...
import UIKit
var list = ["Visa code: 1234", "Mastercard code: 4321"]
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var messageLabel: UILabel!
var userMessage = "Sample text"
var theUserText: UITextField?
#IBOutlet weak var tabelView: UITableView!
#IBAction func addItemButton(_ sender: Any) {
let alertController = UIAlertController(title:"title",
message: "message",
preferredStyle: .alert)
alertController.addTextField(configurationHandler: theUserTextFunc)
let okAction = UIAlertAction(title: "OK",
style: .default,
handler: self.okHandler)
let cancleAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(okAction)
alertController.addAction(cancleAction)
self.present(alertController, animated: true)
}
func theUserTextFunc(textField: UITextField){
theUserText = textField
}
func okHandler(alert: UIAlertAction!){
list.append((theUserText?.text)!)
self.tabelView.reloadData()
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return (list.count)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
cell.textLabel?.text = list[indexPath.row]
return(cell)
}
// Swipe to delete an item
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == UITableViewCellEditingStyle.delete{
list.remove(at: indexPath.row)
tabelView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
messageLabel.text = userMessage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func customInit(userMessage: String) {
self.userMessage = userMessage
}
}
Try changing your add code
alertController.addTextField(configurationHandler: theUserTextFunc)
to -
alertController.addTextField(configurationHandler: {(textField : UITextField!) -> Void in
textField.placeholder = "Search"
// Set other textfield values that you want
})
I am trying to use an alertViewController to get text, add it to my array of strings, and then reload the tableView with the newly added cell. There seems to be an issue with the formatting after it is reloaded.
import UIKit
class TableViewController: UITableViewController, UINavigationControllerDelegate {
// store the tasks in an array of strings
var tasks = [String]()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Task List"
self.navigationItem.rightBarButtonItem = self.editButtonItem
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addRow))
}
func addRow() {
let ac = UIAlertController(title: "Add a task to the list", message: nil, preferredStyle: .alert)
// add a text field
ac.addTextField {
(textField) -> Void in
textField.placeholder = ""
}
// add "cancel" and "ok" actions
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
let createNewRow = UIAlertAction(title: "OK", style: .default) { action -> Void in
let text = ac.textFields?.first?.text
self.tasks.append(text!)
self.loadView()
}
ac.addAction(createNewRow)
present(ac, animated: true, completion: nil)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tasks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Task", for: indexPath)
cell.textLabel?.text = tasks[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
tasks.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
}
Your issue was you call self.loadView() instead of self.tableView.reloadData() in the alert view controller's action:
let createNewRow = UIAlertAction(title: "OK", style: .default) { action -> Void in
let text = ac.textFields?.first?.text
self.tasks.append(text!)
self.tableView.reloadData() // self.loadView() is wrong.
}
ac.addAction(createNewRow)
From Apple's document: https://developer.apple.com/reference/uikit/uiviewcontroller/1621454-loadview
You should never call this method directly. The view controller calls
this method when its view property is requested but is currently nil.
This method loads or creates a view and assigns it to the view
property.
I have a groceryList app
when you add an item to the category list it adds to the entire list of categories when is should not!
https://github.com/mrbryankmiller/Grocery-TableView-.git
class GroceryItemsTableViewController: UITableViewController {
//var groceryItem = ["Item1", "Item2", "Item3"]
//var groceryList = ["Breakfast","Lunch", "Dinner"]
#IBOutlet var groceryItemTableView: UITableView!
#IBAction func addGroceryItemButtonPressed(sender: UIBarButtonItem) {
///new way///
let alertController: UIAlertController = UIAlertController(title: "Add Grocery Item", message: "", preferredStyle: .Alert)
//Cancel Button
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//cancel code
}
alertController.addAction(cancelAction)
let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { action -> Void in
let textField = alertController.textFields![0]
groceryItem.items.append(textField.text!)
self.tableView.reloadData()
}
alertController.addAction(saveAction)
//Add text field
// alertController.addTextFieldWithConfigurationHandler { (textField) -> Void in
// textField.textColor = UIColor.blackColor()
alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
textField.placeholder = "Enter an Item"
//alertController.textFields
}
//Present the AlertController
self.presentViewController(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
//self.navigationItem.leftBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return groceryItem.items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("groceryItem1", forIndexPath: indexPath)
cell.textLabel!.text = groceryItem.items [indexPath.row]
return cell
}
}
If you see carefully the declaration of your class groceryItem you have a static array of elements for every item in your grocery list so every time you add a new element it's shared among all the grocery items.
Instead you should have for each grocery item a list associated with each of its items.
You could define a new struct to save for each grocery item its list of item associated like in the following way:
struct GroceryItem {
var name: String
var items: [String]
}
The we are going to change a little the code in your GroceryListTableViewController to refactor the code according your new model, so it should be like the following:
GroceryListTableViewController:
class GroceryListTableViewController: UITableViewController, GroceryItemsTableViewControllerProtocol {
var groceryList = [GroceryItem]()
#IBAction func addButton(sender: UIBarButtonItem) {
let alertController: UIAlertController = UIAlertController(title: "Add Grocery Category", message: "", preferredStyle: .Alert)
//Cancel Button
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//cancel code
}
alertController.addAction(cancelAction)
let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { action -> Void in
let textField = alertController.textFields![0]
self.groceryList.append(GroceryItem(name: textField.text!, items: [String]()))
self.tableView.reloadData()
}
alertController.addAction(saveAction)
alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
textField.placeholder = "Enter an Item"
//alertController.textFields
}
//Present the AlertController
self.presentViewController(alertController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
//edit button
self.navigationItem.leftBarButtonItem = self.editButtonItem()
groceryList.append(GroceryItem(name: "Breakfast", items: ["Item1", "Item2", "Item3"]))
groceryList.append(GroceryItem(name: "Lunch", items: ["Item1", "Item2", "Item3"]))
groceryList.append(GroceryItem(name: "Dinner", items: ["Item1", "Item2", "Item3"]))
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return groceryList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("prototype1", forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = groceryList [indexPath.row].name
return cell
}
// pass a tableview cell value to navigationBar title in swift//
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationVC = segue.destinationViewController as! GroceryItemsTableViewController
let cell = sender as! UITableViewCell
let idx = self.tableView.indexPathForSelectedRow?.row
destinationVC.delegate = self
destinationVC.itemList = groceryList[idx!].items
destinationVC.navigationItem.title = cell.textLabel?.text
}
func didAddGroceryItem(itemName: String) {
let idx = self.tableView.indexPathForSelectedRow?.row
groceryList[idx!].items.append(itemName)
}
func didRemoveGroceryItem(index: Int) {
let idx = self.tableView.indexPathForSelectedRow?.row
groceryList[idx!].items.removeAtIndex(index)
}
}
In the above I have refactored all the code regarding the new model, I put only the places where the code change the rest keep the same.
The thing you need to pass the item associated with the cell selected to the another UIViewController and you can do it very easily in your prepareForSegue. For that we need to get the index for the selected cell and pass the elements to the another UIViewController where we have a new array of [String] created as data source to show the items.
The another important point in the code is that the GroceryListTableViewController now implements a new protocol called GroceryItemsTableViewControllerProtocol. This protocol it's the way to notify to GroceryListTableViewController from the GroceryItemsTableViewController every time a new item is added to the list it's called the delegate pattern.
GroceryItemsTableViewController:
protocol GroceryItemsTableViewControllerProtocol: class {
func didAddGroceryItem(itemName: String)
func didRemoveGroceryItem(index: Int)
}
class GroceryItemsTableViewController: UITableViewController {
weak var delegate: GroceryItemsTableViewControllerProtocol?
var itemList: [String]!
#IBAction func addGroceryItemButtonPressed(sender: UIBarButtonItem) {
///new way///
let alertController: UIAlertController = UIAlertController(title: "Add Grocery Item", message: "", preferredStyle: .Alert)
//Cancel Button
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//cancel code
}
alertController.addAction(cancelAction)
let saveAction: UIAlertAction = UIAlertAction(title: "Save", style: .Default) { [weak self] action -> Void in
guard let s = self else { return }
let textField = alertController.textFields![0]
s.itemList.append(textField.text!)
s.delegate?.didAddGroceryItem(textField.text!)
s.tableView.reloadData()
}
alertController.addAction(saveAction)
alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
textField.placeholder = "Enter an Item"
//alertController.textFields
}
//Present the AlertController
self.presentViewController(alertController, animated: true, completion: nil)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemList.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("groceryItem1", forIndexPath: indexPath)
cell.textLabel!.text = itemList[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
itemList.removeAtIndex(indexPath.row)
delegate?.didRemoveGroceryItem(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
}
EDIT:
To handle properly the deletion you should create a new delegate method no notify the GroceryListTableViewController that a item has been deleted and then delete it properly and you can see in the updated code above.
I hope this help you.