31. Jul 2023iOS

Náš iOS toolbox - Práca s UITableView & UICollectionView vo Swifte

Ak si niekedy vyvíjal iOS aplikácie pomocou UITableView alebo UICollectionView, vieš, že už aj najjednoduchšie prípady vyžadujú implementáciu mnohých metód. Pre zjednodušenie konfigurácie a používania UITableView a UICollectionView sme vytvorili triedy GRTableViewProvider a GRCollectionViewProvider. Tieto triedy poskytujú sadu metód, ktoré umožňujú jednoduchšie implementovať viaceré sekcie a položky.

Marek VricaniOS Developer

Práca s UITableView

Tu si ukážeme, ako zjednodušiť používanie UITableView pomocou GRTableViewProvider  pri vývoji tvojej iOS aplikácie.

Najskôr musíme importovať GRProvider a vytvoriť štruktúru, ktorá spĺňa protokol Sectionable v rámci nášho ViewController. Táto štruktúra bude mať nadpis typu String a pole položiek typu String.

import UIKit
import GRProvider

fileprivate struct Section: Sectionable {

    var title: String?
    var items: [String]

}

V rámci našej triedy TableViewSampleController deklarujeme samotný UITableView a tableProvider, ktorý je vytvorený na základe predtým definovanej štruktúry Section:

class TableViewSampleController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    private let tableProvider = GRTableViewProvider<Section>()

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Table View Provider"
        
        setupTableView()
        showItems()
    }
}

V metóde setupTableView() môžeme ľahko prispôsobiť inštanciu tableProvider podľa našich špecifických potrieb pomocou GRProvider.

Napríklad môžeme nastaviť estimatedHeightForRow, definovať akciu, ktorá vykoná po kliknutí na UITableViewCell, nastaviť heightForHeaderInSection a konfigurovať header pre jednotlivé sekcie atď.

private func setupTableView() {
    tableProvider.estimatedHeightForRow = 100
        
    tableProvider.configureOnItemSelected = { [unowned self] _, _, _, item in
        let alert = UIAlertController(title: "Wow!", message: "You clicked an item: \(item)", preferredStyle: .alert)
        alert.addAction(.init(title: "Cancel", style: .cancel, handler: nil))
        self.present(alert, animated: true)
    }
        
    tableProvider.configureCell = { _, tv, index, title in
        guard let cell = tv.dequeueReusableCell(fromClass: SimpleTableViewCell.self, for: index) else { return UITableViewCell() }
        cell.titleLabel.text = title
        return cell
    }
        
    tableProvider.heightForHeaderInSection = UITableView.automaticDimension
        
    tableProvider.configureSectionHeader = { _, _, _, section in
        let container = UIView()
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false

        container.addSubview(label)

        NSConstraints.activate([
            label.topAnchor.constraint(equalTo: container.topAnchor, constant: 15),
            label.leftAnchor.constraint(equalTo: container.leftAnchor, constant: 15),
            label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -15),
            label.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -15)
            ])

        label.text = section.title

        return container
    }
}

Ďalej v našom TableViewSampleController vytvoríme metódu showItems(), kde definujeme údaje, ktoré majú byť zobrazené v tabuľke. Na záver použijeme metódu bind(), ktorá vyplní TableView s našimi údajmi. 

private func showItems() {
    let section1 = Section(title: "Section1", items: (1...4).map { "Item \($0)" })
    let section2 = Section(title: "Section2", items: (5...8).map { "Item \($0)" })
    tableProvider.bind(to: tableView, sections: [section1, section2])
}

Práca s UICollectionView

Použitie GRCollectionViewProvider  pri práci s UICollectionView je podobné vyššie uvedenému príkladu. Najskôr vytvoríme štruktúru Section, kde definujeme nadpis a položky. Potom definujeme GRCollectionViewProvider s našou štruktúrou Section.

import UIKit
import GRProvider

fileprivate struct Section: Sectionable {
    
    struct Item {
        let title: String
    }
    
    var items: [Item]
    var title: String?
              
    init(items: [Item], title: String?) {
        self.items = items
        self.title = title
    }
    
}

final class CollectionProviderViewSampleController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    
    fileprivate lazy var collectionProvider = GRCollectionViewProvider<Section>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Collection View Provider"
        
        setupCollectionView()
        showItems()
    }

}

V metóde setupCollectionView() môžeme prispôsobiť collectionView pomocou nastavení dostupných v collectionProvider. Napríklad môžeme nastaviť sectionInsets, cellSize, cell a akcie, ktoré sa majú vykonať po kliknutí na jednotlivé bunky.

Keď je collectionView nakonfigurované, môžeme použiť metódu showItems() na naplnenie collectionView požadovanými údajmi.

// Configure the collectionView
private func setupCollectionView() {
    collectionProvider.sectionInsets = .init(top: 0, left: 5, bottom: 0, right: 5)
    collectionProvider.minimumLineSpacingForSection = 5
    collectionProvider.minInteritemSpacingForSection = 5

    collectionProvider.configureCellSize = { _, cv, index, item in
        return CGSize(width: (cv.frame.width - 21) / 3, height: (cv.frame.width - 21) / 3)
    }
        
    collectionProvider.configureSupplementaryElementOfKind = { provider, cv, index, type in
        let section = provider.sections[index.section]
        let view = cv.dequeueReusableSupplementaryView(ofKind: type, fromClass: SimpleCollectionViewSupplementaryView.self, for: index)
        view.titleLabel.text = section.title
        return view
    }
        
    collectionProvider.configureCell = { _, tv, indexPath, item in
        let cell = tv.dequeueReusableCell(fromClass: SimpleCollectionViewCell.self, for: indexPath)
        cell.titleLabel.text = item.title
        return cell
    }
        
    collectionProvider.configureOnItemSelected = { [unowned self] _, _, _, item in
        let alert = UIAlertController(title: "Wow!", message: "You clicked an item: \(item)", preferredStyle: .alert)
        alert.addAction(.init(title: "Cancel", style: .cancel, handler: nil))
        self.present(alert, animated: true)
    }
}

// fill collectionView with data
private func showItems() {
    let section1 = Section(items: (1...5).map { Section.Item(title: "Item \($0)") }, title: "Section 1")
    let section2 = Section(items: (10...12).map { Section.Item(title: "Item \($0)") }, title: "Section 2")
    collectionProvider.bind(to: collectionView, sections: [section1, section2])
}

Gratulujem!

Prešiel/prešla si procesom práce s UITableView a UICollectionView použitím balíka GoodProvider.

Teraz sa možno zaujímaš, ako to presne funguje v rámci aplikácie. Našťastie, balík obsahuje ukážkový kód, ktorý môžeš preskúmať a lepšie porozumieť jeho funkciám. 

Ak bol pre teba tento balík užitočný, určite sa pozri aj na naše ďalšie balíky. Kto vie, možno nájdeš ďalší, ktorý ti pomôže posunúť tvoju appku na vyššiu úroveň! 

Ďalšie články z našej série:

iOS 5 Minút čítania

Náš iOS toolbox - Ako ukladať hodnoty do UserDefaults a KeyChain vo Swifte

Marek Vrican20 Apr 2023
iOS 6 Minút čítania

Náš iOS toolbox - Spracovanie sieťových požiadaviek vo Swifte

Marek Vrican20 Apr 2023
iOS 4 Minúty čítania

Náš iOS toolbox – Ako vytvárame reaktívne aplikácie v Swifte?

Marek Vrican15 Jun 2023
Marek VricaniOS Developer