forked from zelo72/mastodon-ios
feat: make diffable data source works with category picker
This commit is contained in:
parent
8568debab0
commit
893dc2a668
|
@ -25,6 +25,22 @@ extension CategoryPickerSection {
|
||||||
cell.categoryView.titleLabel.font = .systemFont(ofSize: 28)
|
cell.categoryView.titleLabel.font = .systemFont(ofSize: 28)
|
||||||
}
|
}
|
||||||
cell.categoryView.titleLabel.text = item.title
|
cell.categoryView.titleLabel.text = item.title
|
||||||
|
cell.observe(\.isSelected, options: [.initial, .new]) { cell, _ in
|
||||||
|
if cell.isSelected {
|
||||||
|
cell.categoryView.bgView.backgroundColor = Asset.Colors.lightBrandBlue.color
|
||||||
|
cell.categoryView.bgView.applyShadow(color: Asset.Colors.lightBrandBlue.color, alpha: 1, x: 0, y: 0, blur: 4.0)
|
||||||
|
if case .all = item {
|
||||||
|
cell.categoryView.titleLabel.textColor = Asset.Colors.lightWhite.color
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cell.categoryView.bgView.backgroundColor = Asset.Colors.lightWhite.color
|
||||||
|
cell.categoryView.bgView.applyShadow(color: Asset.Colors.lightBrandBlue.color, alpha: 0, x: 0, y: 0, blur: 0.0)
|
||||||
|
if case .all = item {
|
||||||
|
cell.categoryView.titleLabel.textColor = Asset.Colors.lightBrandBlue.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cell.observations)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,18 @@ extension PickServerSection {
|
||||||
static func tableViewDiffableDataSource(
|
static func tableViewDiffableDataSource(
|
||||||
for tableView: UITableView,
|
for tableView: UITableView,
|
||||||
dependency: NeedsDependency,
|
dependency: NeedsDependency,
|
||||||
|
pickServerCategoriesCellDelegate: PickServerCategoriesCellDelegate,
|
||||||
pickServerSearchCellDelegate: PickServerSearchCellDelegate,
|
pickServerSearchCellDelegate: PickServerSearchCellDelegate,
|
||||||
pickServerCellDelegate: PickServerCellDelegate
|
pickServerCellDelegate: PickServerCellDelegate
|
||||||
) -> UITableViewDiffableDataSource<PickServerSection, PickServerItem> {
|
) -> UITableViewDiffableDataSource<PickServerSection, PickServerItem> {
|
||||||
UITableViewDiffableDataSource(tableView: tableView) { [weak pickServerSearchCellDelegate, weak pickServerCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
UITableViewDiffableDataSource(tableView: tableView) { [weak pickServerCategoriesCellDelegate, weak pickServerSearchCellDelegate, weak pickServerCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
||||||
switch item {
|
switch item {
|
||||||
case .header:
|
case .header:
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerTitleCell.self), for: indexPath) as! PickServerTitleCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerTitleCell.self), for: indexPath) as! PickServerTitleCell
|
||||||
return cell
|
return cell
|
||||||
case .categoryPicker(let items):
|
case .categoryPicker(let items):
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCategoriesCell.self), for: indexPath) as! PickServerCategoriesCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCategoriesCell.self), for: indexPath) as! PickServerCategoriesCell
|
||||||
|
cell.delegate = pickServerCategoriesCellDelegate
|
||||||
cell.diffableDataSource = CategoryPickerSection.collectionViewDiffableDataSource(
|
cell.diffableDataSource = CategoryPickerSection.collectionViewDiffableDataSource(
|
||||||
for: cell.collectionView,
|
for: cell.collectionView,
|
||||||
dependency: dependency
|
dependency: dependency
|
||||||
|
@ -48,19 +50,6 @@ extension PickServerSection {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCell.self), for: indexPath) as! PickServerCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCell.self), for: indexPath) as! PickServerCell
|
||||||
PickServerSection.configure(cell: cell, server: server, attribute: attribute)
|
PickServerSection.configure(cell: cell, server: server, attribute: attribute)
|
||||||
cell.delegate = pickServerCellDelegate
|
cell.delegate = pickServerCellDelegate
|
||||||
// cell.server = server
|
|
||||||
// if expandServerDomainSet.contains(server.domain) {
|
|
||||||
// cell.mode = .expand
|
|
||||||
// } else {
|
|
||||||
// cell.mode = .collapse
|
|
||||||
// }
|
|
||||||
// if server == viewModel.selectedServer.value {
|
|
||||||
// tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
|
||||||
// } else {
|
|
||||||
// tableView.deselectRow(at: indexPath, animated: false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// cell.delegate = self
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,17 @@ import UIKit
|
||||||
|
|
||||||
class PickServerCategoryCollectionViewCell: UICollectionViewCell {
|
class PickServerCategoryCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
var observations = Set<NSKeyValueObservation>()
|
||||||
|
|
||||||
var categoryView: PickServerCategoryView = {
|
var categoryView: PickServerCategoryView = {
|
||||||
let view = PickServerCategoryView()
|
let view = PickServerCategoryView()
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
override var isSelected: Bool {
|
override func prepareForReuse() {
|
||||||
didSet {
|
super.prepareForReuse()
|
||||||
categoryView.selected = isSelected
|
observations.removeAll()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
|
|
|
@ -122,6 +122,7 @@ extension MastodonPickServerViewController {
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel.setupDiffableDataSource(
|
||||||
for: tableView,
|
for: tableView,
|
||||||
dependency: self,
|
dependency: self,
|
||||||
|
pickServerCategoriesCellDelegate: self,
|
||||||
pickServerSearchCellDelegate: self,
|
pickServerSearchCellDelegate: self,
|
||||||
pickServerCellDelegate: self
|
pickServerCellDelegate: self
|
||||||
)
|
)
|
||||||
|
@ -398,6 +399,28 @@ extension MastodonPickServerViewController: UITableViewDelegate {
|
||||||
viewModel.selectedServer.send(nil)
|
viewModel.selectedServer.send(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
|
switch item {
|
||||||
|
case .categoryPicker:
|
||||||
|
guard let cell = cell as? PickServerCategoriesCell else { return }
|
||||||
|
guard let diffableDataSource = cell.diffableDataSource else { return }
|
||||||
|
let snapshot = diffableDataSource.snapshot()
|
||||||
|
|
||||||
|
let item = viewModel.selectCategoryItem.value
|
||||||
|
guard let section = snapshot.indexOfSection(.main),
|
||||||
|
let row = snapshot.indexOfItem(item) else { return }
|
||||||
|
cell.collectionView.selectItem(at: IndexPath(item: row, section: section), animated: false, scrollPosition: .centeredHorizontally)
|
||||||
|
case .search:
|
||||||
|
guard let cell = cell as? PickServerSearchCell else { return }
|
||||||
|
cell.searchTextField.text = viewModel.searchText.value
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonPickServerViewController {
|
extension MastodonPickServerViewController {
|
||||||
|
@ -409,38 +432,15 @@ extension MastodonPickServerViewController {
|
||||||
emptyStateView.topPaddingViewTopLayoutConstraint.constant = rectInTableView.maxY
|
emptyStateView.topPaddingViewTopLayoutConstraint.constant = rectInTableView.maxY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//extension MastodonPickServerViewController: UITableViewDataSource {
|
|
||||||
|
|
||||||
// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
// MARK: - PickServerCategoriesCellDelegate
|
||||||
//
|
extension MastodonPickServerViewController: PickServerCategoriesCellDelegate {
|
||||||
// let section = Self.Section.allCases[indexPath.section]
|
func pickServerCategoriesCell(_ cell: PickServerCategoriesCell, collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
// switch section {
|
guard let diffableDataSource = cell.diffableDataSource else { return }
|
||||||
// case .title:
|
let item = diffableDataSource.itemIdentifier(for: indexPath)
|
||||||
//
|
viewModel.selectCategoryItem.value = item ?? .all
|
||||||
// case .categories:
|
}
|
||||||
//
|
}
|
||||||
// case .search:
|
|
||||||
//
|
|
||||||
// case .serverList:
|
|
||||||
// let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: PickServerCell.self), for: indexPath) as! PickServerCell
|
|
||||||
// let server = viewModel.servers.value[indexPath.row]
|
|
||||||
// // cell.server = server
|
|
||||||
//// if expandServerDomainSet.contains(server.domain) {
|
|
||||||
//// cell.mode = .expand
|
|
||||||
//// } else {
|
|
||||||
//// cell.mode = .collapse
|
|
||||||
//// }
|
|
||||||
// if server == viewModel.selectedServer.value {
|
|
||||||
// tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
|
||||||
// } else {
|
|
||||||
// tableView.deselectRow(at: indexPath, animated: false)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// cell.delegate = self
|
|
||||||
// return cell
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// MARK: - PickServerSearchCellDelegate
|
// MARK: - PickServerSearchCellDelegate
|
||||||
extension MastodonPickServerViewController: PickServerSearchCellDelegate {
|
extension MastodonPickServerViewController: PickServerSearchCellDelegate {
|
||||||
|
@ -465,41 +465,7 @@ extension MastodonPickServerViewController: PickServerCellDelegate {
|
||||||
// expand attribute change do not needs apply snapshot to diffable data source
|
// expand attribute change do not needs apply snapshot to diffable data source
|
||||||
// but should I block the viewModel data binding during tableView.beginUpdates/endUpdates?
|
// but should I block the viewModel data binding during tableView.beginUpdates/endUpdates?
|
||||||
}
|
}
|
||||||
|
|
||||||
// func pickServerCell(modeChange server: Mastodon.Entity.Server, newMode: PickServerCell.Mode, updates: (() -> Void)) {
|
|
||||||
// if newMode == .collapse {
|
|
||||||
// expandServerDomainSet.remove(server.domain)
|
|
||||||
// } else {
|
|
||||||
// expandServerDomainSet.insert(server.domain)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// tableView.beginUpdates()
|
|
||||||
// updates()
|
|
||||||
// tableView.endUpdates()
|
|
||||||
//
|
|
||||||
// if newMode == .expand, let modeChangeIndex = self.viewModel.servers.value.firstIndex(where: { $0 == server }), self.tableView.indexPathsForVisibleRows?.last?.row == modeChangeIndex {
|
|
||||||
// self.tableView.scrollToRow(at: IndexPath(row: modeChangeIndex, section: 3), at: .bottom, animated: true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension MastodonPickServerViewController: PickServerCategoriesDataSource, PickServerCategoriesCellDelegate {
|
|
||||||
// func numberOfCategories() -> Int {
|
|
||||||
// return viewModel.categories.count
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func category(at index: Int) -> MastodonPickServerViewModel.Category {
|
|
||||||
// return viewModel.categories[index]
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func selectedIndex() -> Int {
|
|
||||||
// return viewModel.selectCategoryIndex.value
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func pickServerCategoriesCell(_ cell: PickServerCategoriesCell, didSelect index: Int) {
|
|
||||||
// return viewModel.selectCategoryIndex.send(index)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// MARK: - OnboardingViewControllerAppearance
|
// MARK: - OnboardingViewControllerAppearance
|
||||||
extension MastodonPickServerViewController: OnboardingViewControllerAppearance { }
|
extension MastodonPickServerViewController: OnboardingViewControllerAppearance { }
|
||||||
|
|
|
@ -12,12 +12,14 @@ extension MastodonPickServerViewModel {
|
||||||
func setupDiffableDataSource(
|
func setupDiffableDataSource(
|
||||||
for tableView: UITableView,
|
for tableView: UITableView,
|
||||||
dependency: NeedsDependency,
|
dependency: NeedsDependency,
|
||||||
|
pickServerCategoriesCellDelegate: PickServerCategoriesCellDelegate,
|
||||||
pickServerSearchCellDelegate: PickServerSearchCellDelegate,
|
pickServerSearchCellDelegate: PickServerSearchCellDelegate,
|
||||||
pickServerCellDelegate: PickServerCellDelegate
|
pickServerCellDelegate: PickServerCellDelegate
|
||||||
) {
|
) {
|
||||||
diffableDataSource = PickServerSection.tableViewDiffableDataSource(
|
diffableDataSource = PickServerSection.tableViewDiffableDataSource(
|
||||||
for: tableView,
|
for: tableView,
|
||||||
dependency: dependency,
|
dependency: dependency,
|
||||||
|
pickServerCategoriesCellDelegate: pickServerCategoriesCellDelegate,
|
||||||
pickServerSearchCellDelegate: pickServerSearchCellDelegate,
|
pickServerSearchCellDelegate: pickServerSearchCellDelegate,
|
||||||
pickServerCellDelegate: pickServerCellDelegate
|
pickServerCellDelegate: pickServerCellDelegate
|
||||||
)
|
)
|
||||||
|
|
|
@ -89,9 +89,11 @@ extension PickServerCategoriesCell {
|
||||||
|
|
||||||
// MARK: - UICollectionViewDelegateFlowLayout
|
// MARK: - UICollectionViewDelegateFlowLayout
|
||||||
extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout {
|
extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout {
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: indexPath: %s", ((#file as NSString).lastPathComponent), #line, #function, indexPath.debugDescription)
|
||||||
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
|
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredHorizontally)
|
||||||
// delegate.pickServerCategoriesCell(self, didSelect: indexPath.row)
|
delegate?.pickServerCategoriesCell(self, collectionView: collectionView, didSelectItemAt: indexPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
|
||||||
|
@ -106,27 +108,5 @@ extension PickServerCategoriesCell: UICollectionViewDelegateFlowLayout {
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||||
return CGSize(width: 60, height: 80)
|
return CGSize(width: 60, height: 80)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//extension PickServerCategoriesCell: UICollectionViewDataSource {
|
|
||||||
// func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
|
||||||
// return dataSource.numberOfCategories()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
||||||
// let category = dataSource.category(at: indexPath.row)
|
|
||||||
// let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: PickServerCategoryCollectionViewCell.self), for: indexPath) as! PickServerCategoryCollectionViewCell
|
|
||||||
// cell.category = category
|
|
||||||
//
|
|
||||||
// // Select the default category by default
|
|
||||||
// if indexPath.row == dataSource.selectedIndex() {
|
|
||||||
// // Use `[]` as the scrollPosition to avoid contentOffset change
|
|
||||||
// collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
|
|
||||||
// cell.isSelected = true
|
|
||||||
// }
|
|
||||||
// return cell
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ class PickServerSearchCell: UITableViewCell {
|
||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private var searchTextField: UITextField = {
|
let searchTextField: UITextField = {
|
||||||
let textField = UITextField()
|
let textField = UITextField()
|
||||||
textField.translatesAutoresizingMaskIntoConstraints = false
|
textField.translatesAutoresizingMaskIntoConstraints = false
|
||||||
textField.font = .preferredFont(forTextStyle: .headline)
|
textField.font = .preferredFont(forTextStyle: .headline)
|
||||||
|
|
|
@ -9,16 +9,6 @@ import UIKit
|
||||||
import MastodonSDK
|
import MastodonSDK
|
||||||
|
|
||||||
class PickServerCategoryView: UIView {
|
class PickServerCategoryView: UIView {
|
||||||
// var category: MastodonPickServerViewModel.Category? {
|
|
||||||
// didSet {
|
|
||||||
// updateCategory()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
var selected: Bool = false {
|
|
||||||
didSet {
|
|
||||||
// updateSelectStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var bgShadowView: UIView = {
|
var bgShadowView: UIView = {
|
||||||
let view = UIView()
|
let view = UIView()
|
||||||
|
@ -53,6 +43,7 @@ class PickServerCategoryView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PickServerCategoryView {
|
extension PickServerCategoryView {
|
||||||
|
|
||||||
private func configure() {
|
private func configure() {
|
||||||
addSubview(bgView)
|
addSubview(bgView)
|
||||||
addSubview(titleLabel)
|
addSubview(titleLabel)
|
||||||
|
@ -69,33 +60,7 @@ extension PickServerCategoryView {
|
||||||
titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),
|
titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// private func updateCategory() {
|
|
||||||
// guard let category = category else { return }
|
|
||||||
// titleLabel.text = category.title
|
|
||||||
// switch category {
|
|
||||||
// case .all:
|
|
||||||
// titleLabel.font = UIFont.systemFont(ofSize: 17)
|
|
||||||
// case .some:
|
|
||||||
// titleLabel.font = UIFont.systemFont(ofSize: 28)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private func updateSelectStatus() {
|
|
||||||
// if selected {
|
|
||||||
// bgView.backgroundColor = Asset.Colors.lightBrandBlue.color
|
|
||||||
// bgView.applyShadow(color: Asset.Colors.lightBrandBlue.color, alpha: 1, x: 0, y: 0, blur: 4.0)
|
|
||||||
// if case .all = category {
|
|
||||||
// titleLabel.textColor = Asset.Colors.lightWhite.color
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// bgView.backgroundColor = Asset.Colors.lightWhite.color
|
|
||||||
// bgView.applyShadow(color: Asset.Colors.lightBrandBlue.color, alpha: 0, x: 0, y: 0, blur: 0.0)
|
|
||||||
// if case .all = category {
|
|
||||||
// titleLabel.textColor = Asset.Colors.lightBrandBlue.color
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG && canImport(SwiftUI)
|
#if DEBUG && canImport(SwiftUI)
|
||||||
|
|
Loading…
Reference in New Issue