Because this thread helped me, I thought I’d add my own contribution.
My opinion and experience with this is that it’s a lot easier with a multidimensional array. I think there’s less redundancy in the code vs. using two arrays, and the changes needed to make it work in ItemStore and ItemsViewController are pretty minor. Mostly I needed to add the section number to the allItems array calls (that is, allItems[sectionNumber] etc. depending on what I was doing).
Here’s what I did.
Item.swift -> added var valueGT50 = false. In init, if valueInDollars > 50, self.valueGT50 =. true. I also added this comparison to the function for Equatable.
ItemStore.swift -> changed allItems to (those are empty square brackets. Creating empty elements in the array was necessary for append and other functions in ItemsViewController to work). createItem was also modified.
class ItemStore
{
var allItems : [[Item]] = [,]
@discardableResult func createItem() -> Item
{
let newItem = Item(random: true)
var sectionNum = 0
if newItem.valueGT50
{
sectionNum = 1
}
allItems[sectionNum].append(newItem)
return newItem
}
func removeItem(_ item: Item)
{
var sectionNum = 0
if item.valueGT50
{
sectionNum = 1
}
if let index = allItems[sectionNum].firstIndex(of: item)
{
allItems[sectionNum].remove(at: index)
}
}
func moveItem(from fromIndex: IndexPath, to toIndex: IndexPath)
{
if fromIndex == toIndex
{
return
}
// get reference to object being moved so it can be reinserted
let movedItem = allItems[fromIndex.section][fromIndex.row]
// remove item from array
allItems[fromIndex.section].remove(at: fromIndex.row)
// insert item in array at new location
allItems[fromIndex.section].insert(movedItem, at: toIndex.row)
}
}
ItemsViewController.swift looks like this. There are a couple of added functions which are also described in the thread above and some of the existing functions were modified to include the section number in the array call.
class ItemsViewController: UITableViewController
{
var itemStore: ItemStore!
@IBAction func addNewItem(_ sender: UIButton)
{
// create a new item and add it to the store
let newItem = itemStore.createItem()
var sectionNum = 0
if newItem.valueGT50
{
sectionNum = 1
}
// figure out where that item is in the array
if let index = itemStore.allItems[sectionNum].firstIndex(of: newItem)
{
let indexPath = IndexPath(row: index, section: sectionNum)
// Insert this new row into the table
tableView.insertRows(at: [indexPath], with: .automatic)
}
}
@IBAction func toggleEditingMode(_ sender: UIButton)
{
// if you are currently in editing mode
if isEditing
{
// change text of button to inform user of state
sender.setTitle("Edit", for: .normal)
// turn off editing mode
setEditing(false, animated: true)
}
else
{
// change text of button to inform user of state
sender.setTitle("Done", for: .normal)
// enter editing mode
setEditing(true, animated: true)
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return itemStore.allItems.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemStore.allItems[section].count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// get a new or reused cell
let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
// set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear in the table view
let item = itemStore.allItems[indexPath.section][indexPath.row]
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = "$\(item.valueInDollars)"
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
// if the table view is asking to commit a delete command…
if editingStyle == .delete
{
let item = itemStore.allItems[indexPath.section][indexPath.row]
// remove the item
itemStore.removeItem(item)
// also remove that row from the table view with an animation
tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
// update the model
itemStore.moveItem(from: sourceIndexPath, to: destinationIndexPath) // remove .row references to send entire indexPath for each item
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0
{
return "$50 or less"
}
else
{
return "Over $50"
}
}
override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath {
// I need to identify section
let sourceSection = sourceIndexPath.section
let destinationSection = proposedDestinationIndexPath.section
// only allow the move if sections are the same, otherwise, slide them within the same section
if destinationSection < sourceSection {
return IndexPath(row: 0, section: sourceSection)
} else if destinationSection > sourceSection {
return IndexPath(row: self.tableView(tableView, numberOfRowsInSection: sourceSection)-1, section: sourceSection)
}
return proposedDestinationIndexPath
}
}
I probably don’t need valueGT50 as part of the Item, but it was left over from my first pass at the challenge, where I was trying to do this with a one dimensional array. I gave up on that path when I realized that indexPath wasn’t giving me enough information to find the element I needed. In fact, my first working pass at two sections would correctly put the first elements in a section, but as soon as the other section had elements going to it, it was showing the elements from the first section. Fun bug!
Again, thanks for the help. I hope someone else might find this useful.