I'm trying to create an UITableView with alphabetical order headers depending on my businessName property's first letter of my Company model, and then hide the header sections if there's not a company in the collection that starts with a letter. I'm already fetching the companies into the controller, but I'm having difficulty with limiting my companies into the right section and then hiding the sections that don't have the businessName's first letter.
// Current situation
// What I'm looking for
// Company Model
`struct Company {
var uid: String
var businessAddress: String
var businessName: String
init(dictionary: [String : Any]) {
self.uid = dictionary["uid"] as? String ?? ""
self.businessAddress = dictionary["businessAddress"] as? String ?? ""
self.businessName = dictionary["businessName"] as? String ?? ""
}
}`
// Service
`struct EmployeeService {
static func fetchCompanies(completion: @escaping([Company]) -> Void) {
var companies = [Company]()
let query = REF_COMPANIES.order(by: "businessName")
query.addSnapshotListener { snapshot, error in
snapshot?.documentChanges.forEach({ change in
let dictionary = change.document.data()
let company = Company(dictionary: dictionary)
companies.append(company)
companies.sort {
$0.businessName < $1.businessName
}
completion(companies)
})
}
}
}`
// CompanySelectionController
`class CompanySelectionController: UIViewController {
// MARK: - Properties
var companies = [Company]()
let sectionTitles = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".map(String.init)
}
// MARK: - UITableViewDataSource
extension CompanySelectionController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return sectionTitles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return companies.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
// Create the header view
let headerView = UIView.init(frame: CGRectMake(0, 0, self.view.frame.size.width, 40))
headerView.backgroundColor = UIColor.groupTableViewBackground
// Create the Label
let label = UILabel(frame: CGRectMake(10, -40, 120, 60))
label.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
label.textAlignment = .left
label.text = sectionTitles[section]
// Add the label to your headerview
headerView.addSubview(label)
return headerView
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.CompanySelectionCell, for: indexPath) as! CompanySelectionCell
cell.selectionStyle = .none
cell.company = companies[indexPath.row]
return cell
}
}`
// CompanySelectionCell
`protocol CompannyViewModelItem {
var type: CompannyViewModelItemType { get }
var rowCount: Int { get }
var sectionTitle: String { get }
}
class CompanySelectionCell: UITableViewCell {
// MARK: - Properties
var company: Company! {
didSet {
configure()
}
}
private let businessNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "AvenirNext-DemiBold", size: 14)
label.textColor = .black
label.textAlignment = .left
return label
}()
private let businessAddressLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "AvenirNext-MediumItalic", size: 12)
label.textColor = .darkGray
label.textAlignment = .left
return label
}()
lazy var businessStack = UIStackView(arrangedSubviews: [businessNameLabel, businessAddressLabel])
// MARK: - Lifecycle
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Helper Functions
fileprivate func configure() {
guard let company = company else { return }
let viewModel = CompannyViewModel(company: company)
businessNameLabel.text = viewModel.businessName
businessAddressLabel.text = viewModel.businessAddress
}
}`
// CompannyViewModel
enum CompannyViewModelItemType { case uid case businessName }
I have tried changing the label property inside viewForHeaderInSection
to try and conform to the right letter, but the screenshot of my problem has been the furthest I've got.
CodePudding user response:
Your problem is your datasource for the UITableView
. What you're using is actually a static structure with 26 sections (all letters) and you're appending all your companies to every section. That is happening here:
func numberOfSections(in tableView: UITableView) -> Int {
return sectionTitles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return companies.count
}
To show what you actually want you need a different datastructure like a nested Array
to fill your table view.
let dataSource: [String: [Company]] = // however you want to read the data in here
func numberOfSections(in tableView: UITableView) -> Int {
dataSource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
datasource[section].count
}
To create the datasource you'd need to go through all your companies and only fill the letters (section) of the ones actually existing.
Edit: I'd highly recommend you using AutoLayout to create your views