10. Bronze Challenge: Sections

The code is pretty straightforward the only thing needing to be refactored is the section titles titleForHeaderInSection. I’m not happy with them being coded in the controller. I would prefer to have the title and data coupled, but that would call for a dictionary and tableView’s protocol functions pass in Ints and they don’t play well with dictionaries.

itemStore.swift

class ItemStore {
    
    var allItems = [Item]()
    
    @discardableResult func createItem() -> Item {
        let newItem = Item(random: true)
        
        allItems.append(newItem)
        
        return newItem
    }
    
    func filterItemsBy(_ price: Int = 50) -> [[Item]] {
        var filteredItems = [[Item](), [Item]()]
        for item in allItems {
            if item.valueInDollars > price {
                filteredItems[0].append(item)
            } else {
                filteredItems[1].append(item)
            }
        }
        return filteredItems
    }
    
    init() {
        for _ in 0..<5 {
            createItem()
        }
    }
}

itemsViewController.swift

class ItemsViewController: UITableViewController {

    var filteredItems = [[Item]]()
    var itemStore: ItemStore! {
        didSet {
            // reload table each time new data is set
            filteredItems = itemStore.filterItemsBy()
            self.tableView.reloadData()
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let statusBarHeight = UIApplication.shared.statusBarFrame.height
        let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
        
        tableView.contentInset = insets
        tableView.scrollIndicatorInsets = insets
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return filteredItems.count
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        switch section {
        case 0:
            return "Over $50"
        case 1:
            return "Under $50"
        default:
            return nil
        }
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filteredItems[section].count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let item = filteredItems[indexPath.section][indexPath.row]
        
        // this is better for memory management but must be configued in IB
        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
        cell.textLabel?.text = item.name
        cell.detailTextLabel?.text = "$\(item.valueInDollars)"
        
        return cell
        
    }
}
1 Like

I just add a self.tableView.reloadData() in the viewWillAppear method:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.tableView.reloadData()
}

This is because, when you press the button home twice and terminate the app process, when you open it again the values of the list are unordered.

1 Like

For the color change challenge I simply check the item.valueInDollars in the ItemViewController. If it is >= $50 change the text red otherwise, change the text green. I put it in the cellForRowAt tableView function.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell
    let item = itemStore.allItems[indexPath.row]

    cell.nameLabel.text = item.name
    cell.serialNumberLabel.text = item.serialNumber

    //This section changes the color of the 
    //valueInDollars according to the value
    let val = item.valueInDollars
    if val > 50 {
        cell.valueLabel.textColor = UIColor.red
    } else if val <= 50 {
        cell.valueLabel.textColor = UIColor.green
    }
    cell.valueLabel.text = "$\(val)"
    return cell
}

Here is a solution I came up, which lists both less and higher than $50 and separates them by an empty row. First change ItemStore.swift

class ItemStore {
var lessFiftyItems = [Item] ()
var moreFiftyItems = [Item] ()
var newItems = [Item] ()
var emptyLess = [Item] ()

@discardableResult func createItem() -> Item {
    var newItem = Item(random: true)
    
    if newItem.valueInDollars < 50 {
        lessFiftyItems.append(newItem)
    }
    
    if newItem.valueInDollars >= 50 {
        moreFiftyItems.append(newItem)
    }
    
    if newItems.count == 0 {
        newItem = Item(random: false)
        emptyLess.append(newItem)
    }

    newItems = emptyLess + lessFiftyItems + emptyLess + moreFiftyItems
    
    return newItem
}

init() {
    for _ in 0..<5 {
        createItem()
    }
}

}

then change one part in ItemsViewController.swift tableView function:

if item.name != “” && item.valueInDollars != 0 {
cell.detailTextLabel?.text = “$(item.valueInDollars)”
}