From b2c00419626db7915d17bc9d4d44b3dd5e74e7ba Mon Sep 17 00:00:00 2001 From: Bruno Muniz Azevedo Filho <bruno@elixxir.io> Date: Fri, 22 Jul 2022 19:20:38 -0300 Subject: [PATCH] Using compositional layout w/ collectionview on search left controller --- .../Controllers/SearchLeftController.swift | 48 ++++++++++------- .../Utils/SearchDiffableDataSource.swift | 23 -------- .../ViewModels/SearchLeftViewModel.swift | 10 ++++ .../Views/SearchLeftSectionHeader.swift | 32 +++++++++++ .../SearchFeature/Views/SearchLeftView.swift | 53 +++++++++++++++++-- Sources/Shared/AutoGenerated/Strings.swift | 2 + .../Resources/en.lproj/Localizable.strings | 2 + Sources/Shared/Views/AvatarCell.swift | 6 +-- 8 files changed, 128 insertions(+), 48 deletions(-) delete mode 100644 Sources/SearchFeature/Utils/SearchDiffableDataSource.swift create mode 100644 Sources/SearchFeature/Views/SearchLeftSectionHeader.swift diff --git a/Sources/SearchFeature/Controllers/SearchLeftController.swift b/Sources/SearchFeature/Controllers/SearchLeftController.swift index 4b71340e..e7c39382 100644 --- a/Sources/SearchFeature/Controllers/SearchLeftController.swift +++ b/Sources/SearchFeature/Controllers/SearchLeftController.swift @@ -19,10 +19,10 @@ final class SearchLeftController: UIViewController { lazy private var screenView = SearchLeftView() - private var dataSource: SearchDiffableDataSource! private(set) var viewModel = SearchLeftViewModel() private var drawerCancellables = Set<AnyCancellable>() private let adrpURLString = "https://links.xx.network/adrp" + private var dataSource: UICollectionViewDiffableDataSource<SearchSection, SearchItem>! private var cancellables = Set<AnyCancellable>() private var hudCancellables = Set<AnyCancellable>() @@ -33,7 +33,7 @@ final class SearchLeftController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - setupTableView() + setupCollectionView() setupBindings() } @@ -41,18 +41,17 @@ final class SearchLeftController: UIViewController { screenView.inputField.endEditing(true) } - private func setupTableView() { - screenView.tableView.separatorStyle = .none - screenView.tableView.tableFooterView = UIView() - screenView.tableView.register(AvatarCell.self) - screenView.tableView.dataSource = dataSource - screenView.tableView.delegate = self + private func setupCollectionView() { + screenView.collectionView.delegate = self + screenView.collectionView.dataSource = dataSource + screenView.collectionView.register(AvatarCell.self) + screenView.collectionView.registerSectionHeader(SearchLeftSectionHeader.self) - dataSource = SearchDiffableDataSource( - tableView: screenView.tableView - ) { tableView, indexPath, item in + dataSource = UICollectionViewDiffableDataSource<SearchSection, SearchItem>( + collectionView: screenView.collectionView + ) { collectionView, indexPath, item in let contact: Contact - let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: AvatarCell.self) + let cell: AvatarCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) let h1Text: String var h2Text: String? @@ -111,6 +110,23 @@ final class SearchLeftController: UIViewController { return cell } + + dataSource.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in + guard let self = self else { return nil } + + let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[indexPath.section] + let sectionView = collectionView.dequeueReusableSupplementaryView( + ofKind: kind, + withReuseIdentifier: String(describing: SearchLeftSectionHeader.self), + for: indexPath + ) + + if let sectionView = sectionView as? SearchLeftSectionHeader, case .connections = sectionIdentifier { + sectionView.set(title: Localized.Ud.localResults) + } + + return sectionView + } } private func setupBindings() { @@ -422,8 +438,8 @@ final class SearchLeftController: UIViewController { } -extension SearchLeftController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { +extension SearchLeftController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let item = dataSource.itemIdentifier(for: indexPath) { switch item { case .stranger(let contact): @@ -434,10 +450,6 @@ extension SearchLeftController: UITableViewDelegate { } } - func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { - (view as! UITableViewHeaderFooterView).textLabel?.textColor = Asset.neutralWeak.color - } - private func didTap(contact: Contact) { guard contact.authStatus == .stranger else { coordinator.toContact(contact, from: self) diff --git a/Sources/SearchFeature/Utils/SearchDiffableDataSource.swift b/Sources/SearchFeature/Utils/SearchDiffableDataSource.swift deleted file mode 100644 index 10d7bccc..00000000 --- a/Sources/SearchFeature/Utils/SearchDiffableDataSource.swift +++ /dev/null @@ -1,23 +0,0 @@ -import UIKit -import XXModels - -enum SearchSection { - case stranger - case connections -} - -enum SearchItem: Equatable, Hashable { - case stranger(Contact) - case connection(Contact) -} - -class SearchDiffableDataSource: UITableViewDiffableDataSource<SearchSection, SearchItem> { - override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - switch snapshot().sectionIdentifiers[section] { - case .stranger: - return "" - case .connections: - return "LOCAL RESULTS" - } - } -} diff --git a/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift index 6c0a8390..d7749c15 100644 --- a/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchLeftViewModel.swift @@ -7,6 +7,16 @@ import Countries import Integration import DependencyInjection +enum SearchSection { + case stranger + case connections +} + +enum SearchItem: Equatable, Hashable { + case stranger(Contact) + case connection(Contact) +} + typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem> struct SearchLeftViewState { diff --git a/Sources/SearchFeature/Views/SearchLeftSectionHeader.swift b/Sources/SearchFeature/Views/SearchLeftSectionHeader.swift new file mode 100644 index 00000000..a8d3a829 --- /dev/null +++ b/Sources/SearchFeature/Views/SearchLeftSectionHeader.swift @@ -0,0 +1,32 @@ +import UIKit +import Shared + +final class SearchLeftSectionHeader: UICollectionReusableView { + private let titleLabel = UILabel() + + override func prepareForReuse() { + super.prepareForReuse() + titleLabel.text = nil + } + + override init(frame: CGRect) { + super.init(frame: frame) + + titleLabel.textColor = Asset.neutralWeak.color + titleLabel.font = Fonts.Mulish.bold.font(size: 12.0) + + addSubview(titleLabel) + + titleLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(5) + $0.left.equalToSuperview().offset(24) + $0.bottom.equalToSuperview() + } + } + + required init?(coder: NSCoder) { nil } + + func set(title: String) { + titleLabel.text = title + } +} diff --git a/Sources/SearchFeature/Views/SearchLeftView.swift b/Sources/SearchFeature/Views/SearchLeftView.swift index bbdda3f9..b4d59f3b 100644 --- a/Sources/SearchFeature/Views/SearchLeftView.swift +++ b/Sources/SearchFeature/Views/SearchLeftView.swift @@ -2,26 +2,71 @@ import UIKit import Shared final class SearchLeftView: UIView { - let tableView = UITableView() let inputStackView = UIStackView() let inputField = SearchComponent() let emptyView = SearchLeftEmptyView() let countryButton = SearchCountryComponent() let placeholderView = SearchLeftPlaceholderView() + lazy var collectionView: UICollectionView = { + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .estimated(1) + ) + + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1), + heightDimension: .estimated(1) + ) + + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + let section = NSCollectionLayoutSection(group: group) + section.interGroupSpacing = 5 + section.contentInsets = NSDirectionalEdgeInsets( + top: 0, + leading: 10, + bottom: 0, + trailing: 10 + ) + + let headerFooterSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(44) + ) + + let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerFooterSize, + elementKind: UICollectionView.elementKindSectionHeader, + alignment: .top + ) + + section.boundarySupplementaryItems = [sectionHeader] + + let layout = UICollectionViewCompositionalLayout(section: section) + let collectionView = UICollectionView(frame: bounds, collectionViewLayout: layout) + collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + collectionView.backgroundColor = Asset.neutralWhite.color + return collectionView + }() + init() { super.init(frame: .zero) emptyView.isHidden = true backgroundColor = Asset.neutralWhite.color - tableView.backgroundColor = Asset.neutralWhite.color inputStackView.spacing = 5 inputStackView.addArrangedSubview(countryButton) inputStackView.addArrangedSubview(inputField) addSubview(inputStackView) - addSubview(tableView) + addSubview(collectionView) addSubview(emptyView) addSubview(placeholderView) @@ -47,7 +92,7 @@ final class SearchLeftView: UIView { $0.right.equalToSuperview().offset(-20) } - tableView.snp.makeConstraints { + collectionView.snp.makeConstraints { $0.top.equalTo(inputField.snp.bottom).offset(20) $0.left.equalToSuperview() $0.right.equalToSuperview() diff --git a/Sources/Shared/AutoGenerated/Strings.swift b/Sources/Shared/AutoGenerated/Strings.swift index 315babf8..ed3fb2e5 100644 --- a/Sources/Shared/AutoGenerated/Strings.swift +++ b/Sources/Shared/AutoGenerated/Strings.swift @@ -1211,6 +1211,8 @@ public enum Localized { } public enum Ud { + /// LOCAL RESULTS + public static let localResults = Localized.tr("Localizable", "ud.localResults") /// There are no users with that %@. public static func noneFound(_ p1: Any) -> String { return Localized.tr("Localizable", "ud.noneFound", String(describing: p1)) diff --git a/Sources/Shared/Resources/en.lproj/Localizable.strings b/Sources/Shared/Resources/en.lproj/Localizable.strings index 2b726391..b0dfdda4 100644 --- a/Sources/Shared/Resources/en.lproj/Localizable.strings +++ b/Sources/Shared/Resources/en.lproj/Localizable.strings @@ -988,6 +988,8 @@ = "Edit your new contact’s nickname so you know who they are."; "ud.nicknameDrawer.save" = "Save"; +"ud.localResults" += "LOCAL RESULTS"; "ud.search.input" = "Search by %@"; diff --git a/Sources/Shared/Views/AvatarCell.swift b/Sources/Shared/Views/AvatarCell.swift index aeae12dd..cc39fc57 100644 --- a/Sources/Shared/Views/AvatarCell.swift +++ b/Sources/Shared/Views/AvatarCell.swift @@ -1,7 +1,7 @@ import UIKit import Combine -public final class AvatarCell: UITableViewCell { +public final class AvatarCell: UICollectionViewCell { public struct Action { public var title: String public var color: UIColor @@ -32,8 +32,8 @@ public final class AvatarCell: UITableViewCell { private let actionButton = AvatarCellButton() private var cancellables = Set<AnyCancellable>() - public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) + public override init(frame: CGRect) { + super.init(frame: frame) backgroundColor = Asset.neutralWhite.color separatorView.backgroundColor = Asset.neutralLine.color -- GitLab