How to save reorder of rows in TableView with Swift - ios

I search already a few days to the answer on the question how can I save the reorder TableView with Swift? So many tutorials on the internet show how to reorder but not how to save with UserDefaults.
This is my code right now. What do I wrong?
Please can you help me to the solution? Thank you very much.
import UIKit
class soundboardTableViewController: UITableViewController {
var list = ["January","February","March","April","May","June", "July","August","September","October","November", "December"]
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = self.editButtonItem
let blockItems = UserDefaults.standard.object(forKey:"soundboard")
if(blockItems != nil) {
list = blockItems as! [String]
}
}
// MARK: - Table view data source
override func numberOfSections(in 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 list.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.row]
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
/* Override to support editing the table view.*/
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
list.remove(at: indexPath.row)
UserDefaults.standard.set(self.list, forKey:"soundboard")
UserDefaults.standard.synchronize()
tableView.reloadData()
}
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt indexPath: IndexPath, to: IndexPath) {
let itemToMove = list[indexPath.row]
list.remove(at: indexPath.row)
list.insert(itemToMove, at: indexPath.row)
UserDefaults.standard.set(self.list, forKey:"soundboard")
UserDefaults.standard.synchronize()
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if(self.isEditing) {
}
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
}

Your code in tableView(_:moveRowAt:to:) has some errors. You should change it to:
override func tableView(_ tableView: UITableView, moveRowAt indexPath: IndexPath, to: IndexPath) {
let itemToMove = list[indexPath.row]
list.remove(at: indexPath.row)
list.insert(itemToMove, at: to.row)
UserDefaults.standard.set(self.list, forKey:"soundboard")
}
Notice that I removed UserDefaults.standard.synchronize(). You should remove it from your other methods as well, since it is kind of a non-sense in this case. I have also changed list.insert(itemToMove, at: indexPath.row) to list.insert(itemToMove, at: to.row), because you need to place it to the "destination" index, and not put it back from where you removed it.
The rest of your code seems fine. It should be working once you perform these changes.

Related

UITableView drag and drop a cell onto another cell?

I have a UITableView with a Drag and Drop delegate set up. I am able to drag cells between other cells in the UITableView but it does not seem like there is a delegate method for what happens if I drag a cell on top of another cell, in which case I want to combine the two cells (kind of like dragging around apps in iOS). I couldn't seem to find a delegate method that is called when a cell is placed on top of another cell. What's the best way to do detect when a cell has been 'dropped' ontop of another cell? Thanks
EDIT: I don't know why the undervote, but I have this code working in an app merging cells one in other. I was going to upload a video to show what is doing, but now I will not hurry up, sorry.
there is no delegate method for it, I did it in an application with some work around. I did it like this. Create a UITableViewController with the basic setup in Swift:
import UIKit
class TableViewController: UITableViewController {
var fruitsArray = ["Apple", "Banana", "Mango", "Guava", "PineApple", "Watermelon", "Grapes", "GroundNut", "Muskmelon", "Orange", "Cherry"]
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
// Setup tableview
tableView.isEditing = true
}
// MARK: - Table view data source
override func numberOfSections(in 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 fruitsArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
cell.textLabel?.text = fruitsArray[indexPath.row]
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
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
}
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
if fromIndexPath.row != to.row {
let sourceString = fruitsArray[fromIndexPath.row];
let destinationString = fruitsArray[to.row];
let mergeString = String("\(destinationString), \(sourceString)")
fruitsArray[to.row] = mergeString //replaceObjectAtIndex:destinationIndexPath.row withObject:destinationString];
fruitsArray.remove(at: fromIndexPath.row)
tableView.reloadData()
}
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
With this code you will be able to merge the content of the cells if the draped cell is dropped over other cell. The problem is how to difference between drag & drop a cell, or merge two cells. You will need to add some logic to this or make the user select what to do with a selector, I mean, before edit the cell the user can choose to move or merge a cell, so you will know what to do.
Other option is add custom drag button, or add long press on the cell, etc... to determine if you are dragging or merging.
tableView.dragDelegate = self
tableView.dragInteractionEnabled = true
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let dragItem = UIDragItem(itemProvider: NSItemProvider())
dragItem.localObject = data[indexPath.row]
return [ dragItem ]
}
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// Update the model
let mover = data.remove(at: sourceIndexPath.row)
data.insert(mover, at: destinationIndexPath.row)
}

Why are the changes made in Xcode Storyboard not getting reflected on Simulator / Device?

I've designed a custom tableview using the storyboard. When app runs on simulator or iphone, the changes does not show up. It still displays last version of the tableview, which does not have Static Cells.
I've tried deleting /Users/xxx/Library/Developer/Xcode/DerivedData directory, cleaning build folder, deleting app on the iPhone and running again, restarting iPhone. Nothing works.
Storyboard version
What shows on device
Here is the code:
import UIKit
class NewDebtViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
let saveText = NSLocalizedString("Save", comment: "Save")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: saveText, style: .done, target: self, action: #selector(saveDebt))
}
//Function to go back
#objc func saveDebt(){
//Here the code for saving the New Debt
//print("Back Button Clicked")
_ = navigationController?.popViewController(animated: true)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
/*
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
return cell
}
*/
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
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
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
What else to check or do?
Runtime Hierarchy
Solved. Although I'm designing in the Storyboard the delegate code in the class where returning 0 for the number of rows so logically there were no rows in the iPhone:
This is the evil code which was commented for the problem to be solved.
// MARK: - Table view data source
override func numberOfSections(in 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 4
}
This is a screenshot for the runtime:
iPhone Runtime

how handle click cell in UITableViewController

I would like to handle click each cell in my controller but my log doesn't show anything !
class RightMenuController: UITableViewController {
let row_items = ["دسته بندی", "ثبت نام/ورود"]
override func viewDidLoad() {
navigationController?.navigationBar.isHidden = true
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in 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 row_items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
cell.textLabel?.text = row_items[indexPath.row]
return cell
}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
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
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(self.row_items[indexPath.row]) // not print anything
}
}
Try this instead of your didSelectRow-method:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(self.row_items[indexPath.row])
}
The "method signature" (function name) that you are using for the didSelect method is incorrect. What you have listed in your current implementation is the "Swift 2 Version". You might note the way that the argument labels are separated to make the functions a bit more readable like in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
This same renaming style should be applied to didSelectRowAtIndexPath. Here is the full code you can use to test this out in a playground.
import UIKit
class RightMenuController: UITableViewController {
let row_items = ["دسته بندی", "ثبت نام/ورود"]
override func viewDidLoad() {
// We must register a cell manually since there is no storyboard
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "LabelCell")
navigationController?.navigationBar.isHidden = true
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return row_items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
cell.textLabel?.text = row_items[indexPath.row]
return cell
}
// Old Swift 2 method signature
//func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(self.row_items[indexPath.row]) // not print anything
}
}
// Create the ViewController
let right = RightMenuController(style: .plain)
// Ask the Playground to show your ViewController in the Assistant (2 rings - Right side view)
import PlaygroundSupport
PlaygroundPage.current.liveView = right

'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier

Im trying to do the following but my when i go to add the content to the table it crashes. with this error:
'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier UtilityTableViewCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
import UIKit
class UtilityBillTableViewController: UITableViewController {
var bills = [UtilityBill]()
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in 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 bills.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "UtilityTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! UtilityTableViewCell
let bill = bills[indexPath.row]
cell.billName.text = bill.billName
cell.totalDue.text = bill.amountDue
cell.totalDue.text = bill.dueDate
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
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
}
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
#IBAction func unwindToBillList(sender: UIStoryboardSegue){
if let sourceViewController = sender.source as? UtilitiesViewController, let bill = sourceViewController.bill {
// Add a new meal item.
let newIndexPath = NSIndexPath(row: bills.count, section: 0)
bills.append(bill)
tableView.insertRows(at: [newIndexPath as IndexPath], with: .bottom)
}
}
}
If you've defined cell in storyboard, you have to specify that reuse identifier in the prototype cell in that storyboard scene; if you're using NIB or custom class, you have to register it.
You should double check the spelling for your cell identifier in the storyboard's prototype cell. Or if using NIB or programmatically created cell, make sure to call the appropriate register method.

Collection View not printing inside TableViewCell with Autoresize Row in ios swift

Problem Statement: Collection View required to be printed inside table view cell and auto resize should work as well
Thanks
There is a table view cell.
Collection view is required to be printed.
Auto resizing of Table View cell works well without Collection View
When Collection view is added then it doesnt work.
Collection view is printed if Height of the cell by heightOfRow method is used
But if it is not used then the collection view methods are not called even.
Constraints from IB have been added but still not working.
In the following you can see the Example view controller
import UIKit
import AVFoundation
let studentName = ["Stockholm","London","Paris","Oslo"]
class TableViewController: UITableViewController,UICollectionViewDelegate,UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 140
tableView.rowHeight = UITableViewAutomaticDimension
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
/*
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}*/
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return studentName.count
// return 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! TableViewCell
cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
cell.secondName.text = "Second Name is Here"
cell.firstName.text = studentName[indexPath.row]
print("cell height is == \(cell.frame.size.height)")
// Configure the cell...
return cell
}
// All collection view methods
#available(iOS 6.0, *)
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewReuseIdentifier", for: indexPath as IndexPath) as! myCollectionViewCell
cell.collection_first.text = "1"
cell.Collection_second.text = "2"
print("in collection view number of items in section---cell--\(indexPath.row)--")
return cell
}
#available(iOS 6.0, *)
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("in collection view number of items in section---\(studentName.count)")
return studentName.count
}
/*
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
//
// func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//
// return 5
// }
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewReuseIdentifier", for: indexPath as IndexPath) as! myCollectionViewCell
//.......cell configure....
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
print("get selected collectionview itemindex \(indexPath.row)")
}
*/
//}
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
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
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

Resources