My solution is using: cell.isUserInteractionEnabled = false
Only these 3 files were changed:
Item.swift - changed the convenience initializer to pass "No items!" as name param.
import UIKit
class Item: Equatable {
static func == (lhs: Item, rhs: Item) -> Bool {
return lhs.name == rhs.name
&& lhs.serialNumber == rhs.serialNumber
&& lhs.valueInDollars == rhs.valueInDollars
&& lhs.dateCreated == rhs.dateCreated
}
var name: String
var valueInDollars: Int
var serialNumber: String?
let dateCreated: Date
init(name: String, serialNumber: String?, valueInDollars: Int) {
self.name = name
self.valueInDollars = valueInDollars
self.serialNumber = serialNumber
self.dateCreated = Date()
}
convenience init (random: Bool = false){
if random {
let adjectives = ["Fluffy", "Rusty", "Shiny"]
let nouns = ["Bear", "Spork", "Mac"]
let randomAdjectives = adjectives.randomElement()!
let randomNoun = nouns.randomElement()!
let randomName = "\(randomAdjectives) \(randomNoun)"
let randomValue = Int.random(in: 0..<100)
let randomSerialNumber = UUID().uuidString.components(separatedBy: "-").first!
self.init(name: randomName,
serialNumber: randomSerialNumber,
valueInDollars: randomValue)
} else {
self.init(name: "No items!", serialNumber: nil, valueInDollars: 0)
}
}
}
ItemStore.swift - added a designated initializer and appended first Item as a “No items!” item, by passing Item(random: false)
as param.
import UIKit
class ItemStore {
var allItems = [Item]()
init(){
allItems.append(Item(random: false))
}
@discardableResult func createItem() -> Item {
let newItem = Item(random: true)
allItems.append(newItem)
return newItem
}
func removeItem(_ item: Item) {
if let index = allItems.firstIndex(of: item) {
allItems.remove(at: index)
}
}
func moveItem(from fromIndex: Int, to toIndex: Int) {
if fromIndex == toIndex {
return
}
// Get reference to object being moved so you can reinsert it
let movedItem = allItems[fromIndex]
// Remove item from array
allItems.remove(at: fromIndex)
// Insert item in array at new location
allItems.insert(movedItem, at: toIndex)
}
}
ItemViewController.swift - added 2 checkpoints:
1st - before adding a new Item: to check if we have only 1 item && if it is a “No items!” item. If yes, replaced(removed the old item/added a new item) the “No items!” item with a new regular item.
2nd - after removing item: to check if we removed all the items, if yes add a “No items!” item.
To make a cell constant (only the “No items!” cell), I used tableView(_:, cellForRowAt:)
func. And before returning a cell I checked if the cell is a “No items!” cell. If yes, made it constant/disabled by doing: cell.isUserInteractionEnabled = false
, otherwise for all other cells set it to true
.
import UIKit
class ItemsViewController: UITableViewController {
var itemsStore: ItemStore!
@IBAction func addNewItem(_ sender: UIButton) {
if itemsStore.allItems.count == 1,
itemsStore.allItems[0].name == "No items!",
itemsStore.allItems[0].serialNumber == nil,
itemsStore.allItems[0].valueInDollars == 0 {
itemsStore.allItems.remove(at: 0)
tableView.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
// Create a new item and add it to the store
let newItem = itemsStore.createItem()
// Figure out where the item is in the array
if let index = itemsStore.allItems.firstIndex(of: newItem){
let indexPath = IndexPath(row: index, section: 0)
// 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 tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return itemsStore.allItems.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a new or recycled cell
let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
// Set the text on the cell with the description of item
// that is at the nth index of item, where n = row this cell
// will appear in on the table view
let item = itemsStore.allItems[indexPath.row]
cell.textLabel?.text = item.name
cell.detailTextLabel?.text = "$\(item.valueInDollars)"
if item.name == "No items!", item.serialNumber == nil, item.valueInDollars == 0{
cell.isUserInteractionEnabled = false
} else {
cell.isUserInteractionEnabled = true
}
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 = itemsStore.allItems[indexPath.row]
// Remove the item from the store
itemsStore.removeItem(item)
// Also remove that roe from the table view with an animation
tableView.deleteRows(at: [indexPath], with: .automatic)
if itemsStore.allItems.count == 0 {
itemsStore.allItems.append(Item(random: false))
tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
}
}
}
override func tableView(_ tableView: UITableView,
moveRowAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
// Update the model
itemsStore.moveItem(from: sourceIndexPath.row, to: destinationIndexPath.row)
}
}