fix: review notes
This commit is contained in:
parent
c583b8a98a
commit
47280c7cd7
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
open class BaseFilterCellAppearance: FilterCellAppearanceProtocol {
|
||||
public var selectedColor: UIColor
|
||||
public var selectedBgColor: UIColor
|
||||
public var deselectedBgColor: UIColor
|
||||
public var contentInsets: UIEdgeInsets
|
||||
|
||||
init(selectedColor: UIColor,
|
||||
selectedBgColor: UIColor,
|
||||
deselectedBgColor: UIColor,
|
||||
contentInsets: UIEdgeInsets) {
|
||||
|
||||
self.selectedColor = selectedColor
|
||||
self.selectedBgColor = selectedBgColor
|
||||
self.deselectedBgColor = deselectedBgColor
|
||||
self.contentInsets = contentInsets
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Default appearance
|
||||
|
||||
public extension BaseFilterCellAppearance {
|
||||
static var defaultFilterCellAppearance: BaseFilterCellAppearance {
|
||||
.init(selectedColor: .systemGreen,
|
||||
selectedBgColor: .white,
|
||||
deselectedBgColor: .lightGray,
|
||||
contentInsets: .init(top: 4, left: 8, bottom: 4, right: 8))
|
||||
}
|
||||
}
|
||||
|
|
@ -27,18 +27,9 @@ public struct DefaultFilterPropertyValue: FilterPropertyValueRepresenter, Identi
|
|||
public let id: String
|
||||
public let title: String
|
||||
public let excludingProperties: [String]?
|
||||
public let cellAppearance: FilterCellAppearanceProtocol
|
||||
|
||||
public var isSelected: Bool
|
||||
|
||||
public func convertToViewModel() -> FilterCellViewModelRepresentable {
|
||||
DefaultFilterCellViewModel(id: id,
|
||||
title: title,
|
||||
selectedColor: .green,
|
||||
selectedBgColor: .white,
|
||||
deselectedBgColor: .lightGray,
|
||||
insets: .init(top: 4, left: 8, bottom: 4, right: 8),
|
||||
isSelected: isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
public extension DefaultFilterPropertyValue {
|
||||
|
|
@ -46,11 +37,16 @@ public extension DefaultFilterPropertyValue {
|
|||
self.id = id
|
||||
self.title = title
|
||||
self.excludingProperties = excludingProperties
|
||||
self.cellAppearance = BaseFilterCellAppearance.defaultFilterCellAppearance
|
||||
self.isSelected = false
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultFilterPropertyValue: Hashable {
|
||||
public static func == (lhs: DefaultFilterPropertyValue, rhs: DefaultFilterPropertyValue) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol FilterCellViewModelProtocol {
|
||||
var id: String { get set }
|
||||
var title: String { get set }
|
||||
var appearance: FilterCellAppearanceProtocol { get set }
|
||||
var isSelected: Bool { get set }
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public protocol FilterCellAppearanceProtocol {
|
||||
var selectedColor: UIColor { get set }
|
||||
var selectedBgColor: UIColor { get set }
|
||||
var deselectedBgColor: UIColor { get set }
|
||||
var contentInsets: UIEdgeInsets { get set }
|
||||
}
|
||||
|
|
@ -20,14 +20,9 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol FilterPropertyValueRepresenter: FilterCellViewModelConvertable {
|
||||
public protocol FilterPropertyValueRepresenter {
|
||||
var id: String { get }
|
||||
var excludingProperties: [String]? { get }
|
||||
var cellAppearance: FilterCellAppearanceProtocol { get }
|
||||
var isSelected: Bool { get set }
|
||||
}
|
||||
|
||||
public protocol FilterCellViewModelRepresentable { }
|
||||
|
||||
public protocol FilterCellViewModelConvertable {
|
||||
func convertToViewModel() -> FilterCellViewModelRepresentable
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,22 +20,18 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
public struct DefaultFilterCellViewModel: FilterCellViewModelProtocol, Hashable {
|
||||
|
||||
public protocol FilterCellViewModelProtocol: FilterCellViewModelRepresentable {
|
||||
var id: String { get set }
|
||||
var title: String { get set }
|
||||
var selectedColor: UIColor { get set }
|
||||
var insets: UIEdgeInsets { get set }
|
||||
var isSelected: Bool { get set }
|
||||
}
|
||||
|
||||
public struct DefaultFilterCellViewModel: FilterCellViewModelProtocol, FilterCellViewModelRepresentable {
|
||||
public var id: String
|
||||
public var title: String
|
||||
public var selectedColor: UIColor
|
||||
public var selectedBgColor: UIColor
|
||||
public var deselectedBgColor: UIColor
|
||||
public var insets: UIEdgeInsets
|
||||
public var appearance: FilterCellAppearanceProtocol
|
||||
public var isSelected: Bool
|
||||
|
||||
public static func == (lhs: DefaultFilterCellViewModel, rhs: DefaultFilterCellViewModel) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,28 +20,49 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
open class DefaultFiltersViewModel: NSObject,
|
||||
FiltersViewModelProtocol {
|
||||
|
||||
public var filters: [DefaultFilterPropertyValue]
|
||||
public var selectedFilters: Set<DefaultFilterPropertyValue> = []
|
||||
public var cellsViewModels: [FilterCellViewModelProtocol]
|
||||
private var cellsViewModels: [FilterCellViewModelProtocol]
|
||||
|
||||
public var filters: [DefaultFilterPropertyValue] {
|
||||
didSet {
|
||||
rebuildCellsViewModels()
|
||||
filtersCollection?.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedFilters: Set<DefaultFilterPropertyValue> = [] {
|
||||
didSet {
|
||||
reselectFilters()
|
||||
rebuildCellsViewModels()
|
||||
filtersCollection?.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
public weak var filtersCollection: UpdatableView?
|
||||
|
||||
public init(filters: [DefaultFilterPropertyValue]) {
|
||||
self.filters = filters
|
||||
self.cellsViewModels = filters.compactMap { $0.convertToViewModel() as? FilterCellViewModelProtocol }
|
||||
self.cellsViewModels = filters.compactMap {
|
||||
DefaultFilterCellViewModel(id: $0.id,
|
||||
title: $0.title,
|
||||
appearance: $0.cellAppearance,
|
||||
isSelected: $0.isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
// MARK: - Open methods
|
||||
|
||||
open func filterDidSelected(atIndexPath indexPath: IndexPath) -> [Change] {
|
||||
let (selected, deselected) = toggleFilter(atIndexPath: indexPath)
|
||||
|
||||
let changedFilters = filters
|
||||
.enumerated()
|
||||
.filter { isFilterChanged($0.element, filters: selected) || isFilterChanged($0.element, filters: deselected) }
|
||||
.filter { isFilterInArray($0.element, filters: selected) || isFilterInArray($0.element, filters: deselected) }
|
||||
|
||||
for (offset, element) in changedFilters {
|
||||
cellsViewModels[offset].isSelected = selectedFilters.contains(element)
|
||||
|
|
@ -57,7 +78,30 @@ open class DefaultFiltersViewModel: NSObject,
|
|||
return changedItems
|
||||
}
|
||||
|
||||
public func isFilterChanged(_ filter: DefaultFilterPropertyValue, filters: [DefaultFilterPropertyValue]) -> Bool {
|
||||
open func rebuildCellsViewModels() {
|
||||
cellsViewModels = filters.compactMap {
|
||||
DefaultFilterCellViewModel(id: $0.id,
|
||||
title: $0.title,
|
||||
appearance: $0.cellAppearance,
|
||||
isSelected: $0.isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
public func getCellsViewModels() -> [FilterCellViewModelProtocol] {
|
||||
cellsViewModels
|
||||
}
|
||||
|
||||
public func isFilterInArray(_ filter: DefaultFilterPropertyValue, filters: [DefaultFilterPropertyValue]) -> Bool {
|
||||
filters.contains(where: { $0.id == filter.id })
|
||||
}
|
||||
|
||||
public func reselectFilters() {
|
||||
let selectedFilters = Array(selectedFilters)
|
||||
|
||||
filters.enumerated().forEach {
|
||||
filters[$0.offset].isSelected = isFilterInArray($0.element, filters: selectedFilters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ import UIKit
|
|||
@available(iOS 13.0, *)
|
||||
open class BaseFiltersCollectionView<CellType: UICollectionViewCell & ConfigurableView>: UICollectionView,
|
||||
InitializableViewProtocol,
|
||||
UICollectionViewDelegate where CellType.ViewModelType: Hashable {
|
||||
UpdatableView,
|
||||
UICollectionViewDelegate where CellType.ViewModelType: FilterCellViewModelProtocol & Hashable {
|
||||
|
||||
public enum Section {
|
||||
case main
|
||||
|
|
@ -85,6 +86,7 @@ open class BaseFiltersCollectionView<CellType: UICollectionViewCell & Configurab
|
|||
|
||||
open func viewDidLoad() {
|
||||
register(CellType.self, forCellWithReuseIdentifier: cellsReusedIdentifier)
|
||||
viewModel?.filtersCollection = self
|
||||
|
||||
applySnapshot()
|
||||
}
|
||||
|
|
@ -99,6 +101,12 @@ open class BaseFiltersCollectionView<CellType: UICollectionViewCell & Configurab
|
|||
applyChange(changes)
|
||||
}
|
||||
|
||||
// MARK: - UpdatableView
|
||||
|
||||
open func updateView() {
|
||||
applySnapshot()
|
||||
}
|
||||
|
||||
// MARK: - Open methods
|
||||
|
||||
open func applySnapshot() {
|
||||
|
|
@ -109,7 +117,7 @@ open class BaseFiltersCollectionView<CellType: UICollectionViewCell & Configurab
|
|||
var snapshot = Snapshot()
|
||||
|
||||
snapshot.appendSections([.main])
|
||||
snapshot.appendItems(viewModel.cellsViewModels as! [CellType.ViewModelType], toSection: .main)
|
||||
snapshot.appendItems(viewModel.getCellsViewModels() as! [CellType.ViewModelType], toSection: .main)
|
||||
|
||||
collectionViewDataSource.apply(snapshot, animatingDifferences: true)
|
||||
}
|
||||
|
|
@ -129,21 +137,13 @@ open class BaseFiltersCollectionView<CellType: UICollectionViewCell & Configurab
|
|||
|
||||
open func applyChange(_ changes: [DefaultFiltersViewModel.Change]) {
|
||||
for change in changes {
|
||||
guard let cell = cellForItem(at: change.indexPath) else {
|
||||
guard let cell = cellForItem(at: change.indexPath) as? CellType else {
|
||||
continue
|
||||
}
|
||||
|
||||
configure(filterCell: cell, cellViewModel: change.viewModel)
|
||||
// cell.configure(with: change.viewModel)
|
||||
}
|
||||
|
||||
applySnapshot()
|
||||
}
|
||||
|
||||
open func configure(filterCell: UICollectionViewCell, cellViewModel: FilterCellViewModelProtocol) {
|
||||
guard let cellViewModel = cellViewModel as? CellType.ViewModelType else { return }
|
||||
|
||||
guard let configurableCell = filterCell as? CellType else { return }
|
||||
|
||||
configurableCell.configure(with: cellViewModel)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@
|
|||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import TIUIKitCore
|
||||
import TIUIElements
|
||||
import UIKit
|
||||
|
||||
open class DefaultFilterCollectionCell: ContainerCollectionViewCell<UILabel>,
|
||||
ConfigurableView {
|
||||
|
|
@ -41,7 +41,7 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell<UILabel>,
|
|||
self.viewModel = viewModel
|
||||
|
||||
wrappedView.text = viewModel.title
|
||||
contentInsets = viewModel.insets
|
||||
contentInsets = viewModel.appearance.contentInsets
|
||||
|
||||
setSelected(isSelected: viewModel.isSelected)
|
||||
}
|
||||
|
|
@ -49,11 +49,13 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell<UILabel>,
|
|||
// MARK: - Public methods
|
||||
|
||||
open func setSelected(isSelected: Bool) {
|
||||
let selectedColor = viewModel?.selectedColor ?? .green
|
||||
let appearance = viewModel?.appearance
|
||||
|
||||
let selectedColor = appearance?.selectedColor ?? .green
|
||||
wrappedView.textColor = isSelected ? selectedColor : .black
|
||||
|
||||
if isSelected {
|
||||
backgroundColor = viewModel?.selectedBgColor ?? .white
|
||||
backgroundColor = appearance?.selectedBgColor ?? .white
|
||||
layer.borderColor = selectedColor.cgColor
|
||||
layer.borderWidth = 1
|
||||
} else {
|
||||
|
|
@ -63,6 +65,6 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell<UILabel>,
|
|||
|
||||
open func setDeselectAppearance() {
|
||||
layer.borderWidth = 0
|
||||
backgroundColor = viewModel?.deselectedBgColor ?? .lightGray
|
||||
backgroundColor = viewModel?.appearance.deselectedBgColor ?? .lightGray
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
public protocol UpdatableView: AnyObject {
|
||||
func updateView()
|
||||
}
|
||||
Loading…
Reference in New Issue