fix: code refactoring + add additional protocols
This commit is contained in:
parent
b52d57a5e7
commit
01c4ef6f44
|
|
@ -88,11 +88,10 @@ GEM
|
|||
zeitwerk (2.5.4)
|
||||
|
||||
PLATFORMS
|
||||
arm64-darwin-21
|
||||
x86_64-darwin-20
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods (~> 1.11)
|
||||
|
||||
BUNDLED WITH
|
||||
2.3.18
|
||||
2.3.10
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
"repositoryURL": "https://github.com/Alamofire/Alamofire.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
|
||||
"version": "5.6.2"
|
||||
"revision": "f96b619bcb2383b43d898402283924b80e2c4bae",
|
||||
"version": "5.4.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -24,8 +24,8 @@
|
|||
"repositoryURL": "https://github.com/petropavel13/Cursors",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "52f27b82cb1cbbc2b5fd09514c48b9c75e3b0300",
|
||||
"version": "0.6.0"
|
||||
"revision": "a1561869135e72832eff3b1e729075c56c2eebf6",
|
||||
"version": "0.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// 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 CollectionDirectorRepresenter: UICollectionViewDataSource, UICollectionViewDelegate {
|
||||
|
||||
associatedtype Item
|
||||
|
||||
var collectionView: UICollectionView? { get set }
|
||||
var delegate: FilterItemsDelegate? { get set }
|
||||
|
||||
func insertItem(_ item: Item, at index: Int)
|
||||
func deleteItem(at index: Int)
|
||||
func update(item: Item, at index: Int)
|
||||
func scrollToItem(at indexPath: IndexPath, animated: Bool)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// 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 FiltersCollectionHolder: AnyObject {
|
||||
func select(_ items: [FilterPropertyValueRepresenter])
|
||||
func deselect(_ items: [FilterPropertyValueRepresenter])
|
||||
func updateView()
|
||||
}
|
||||
|
|
@ -22,12 +22,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public protocol FiltersCollectionHolder: AnyObject {
|
||||
func select(_ items: [FilterPropertyValueRepresenter])
|
||||
func deselect(_ items: [FilterPropertyValueRepresenter])
|
||||
func updateView()
|
||||
}
|
||||
|
||||
public protocol FiltersViewModelProtocol: AnyObject {
|
||||
|
||||
associatedtype Filter: FilterPropertyValueRepresenter, Hashable
|
||||
|
|
@ -42,23 +36,17 @@ public protocol FiltersViewModelProtocol: AnyObject {
|
|||
|
||||
public extension FiltersViewModelProtocol {
|
||||
|
||||
/// Method of filtering items
|
||||
/// - Algorithm:
|
||||
/// 1. Determine if current item is selected;
|
||||
/// 2. If it was selected:
|
||||
/// - Remove the item from the array of selected items;
|
||||
/// 3. if it wasn't selected:
|
||||
/// - Insert the item in the array of selected items;
|
||||
/// - Run excluding filtering (remove every item that should be excluded from list of selected items).
|
||||
/// 4. Notify the collection of filters about updating of the item at indexPath
|
||||
func filterItem(atIndexPath indexPath: IndexPath) {
|
||||
guard let item = getItemSafely(indexPath.item) else { return }
|
||||
|
||||
filterItem(item)
|
||||
let (s, d) = filterItem(item)
|
||||
filtersCollectionHolder?.select(s)
|
||||
filtersCollectionHolder?.deselect(d)
|
||||
filtersCollectionHolder?.updateView()
|
||||
}
|
||||
|
||||
private func filterItem(_ item: Filter) {
|
||||
@discardableResult
|
||||
private func filterItem(_ item: Filter) -> (selected: [Filter], deselected: [Filter]) {
|
||||
var itemsToDeselect = [Filter]()
|
||||
var itemsToSelect = [Filter]()
|
||||
let selectedItem = selectedFilters.first { selectedItem in
|
||||
|
|
@ -67,12 +55,10 @@ public extension FiltersViewModelProtocol {
|
|||
|
||||
if let selectedItem = selectedItem {
|
||||
selectedFilters.remove(selectedItem)
|
||||
|
||||
itemsToDeselect.append(item)
|
||||
} else {
|
||||
selectedFilters.insert(item)
|
||||
itemsToSelect.append(item)
|
||||
itemsToDeselect.append(contentsOf: exclude(withItem: item))
|
||||
|
||||
if let itemsToExclude = item.excludingProperties, !itemsToExclude.isEmpty {
|
||||
for itemIdToExclude in itemsToExclude {
|
||||
|
|
@ -81,25 +67,14 @@ public extension FiltersViewModelProtocol {
|
|||
}
|
||||
|
||||
if let itemToExclude = itemToExclude {
|
||||
filterItem(itemToExclude)
|
||||
let (_, deselected) = filterItem(itemToExclude)
|
||||
itemsToDeselect.append(contentsOf: deselected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemsToSelect.forEach { item in
|
||||
if let index = filters.firstIndex(of: item) {
|
||||
filters[index].isSelected = true
|
||||
}
|
||||
}
|
||||
|
||||
itemsToDeselect.forEach { item in
|
||||
if let index = filters.firstIndex(of: item) {
|
||||
filters[index].isSelected = false
|
||||
}
|
||||
}
|
||||
// filtersCollectionHolder?.select(itemsToSelect)
|
||||
// filtersCollectionHolder?.deselect(itemsToDeselect)
|
||||
|
||||
return (itemsToSelect, itemsToDeselect)
|
||||
}
|
||||
|
||||
private func getItemSafely(_ index: Int) -> Filter? {
|
||||
|
|
@ -109,25 +84,4 @@ public extension FiltersViewModelProtocol {
|
|||
|
||||
return filters[index]
|
||||
}
|
||||
|
||||
private func exclude(withItem item: Filter) -> [Filter] {
|
||||
guard let itemsToExclude = item.excludingProperties, !itemsToExclude.isEmpty else {
|
||||
return []
|
||||
}
|
||||
|
||||
var deselectedItems = [Filter]()
|
||||
|
||||
for itemIdToExclude in itemsToExclude {
|
||||
let itemToExclude = selectedFilters.first { item in
|
||||
item.id == itemIdToExclude
|
||||
}
|
||||
|
||||
if let itemToExclude = itemToExclude {
|
||||
selectedFilters.remove(itemToExclude)
|
||||
deselectedItems.append(itemToExclude)
|
||||
}
|
||||
}
|
||||
|
||||
return deselectedItems
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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 SelectableCell: UICollectionViewCell {
|
||||
var isFilterSelected: Bool { get set }
|
||||
|
||||
func toggleSelection()
|
||||
}
|
||||
|
||||
public extension SelectableCell {
|
||||
func toggleSelection() {
|
||||
isFilterSelected.toggle()
|
||||
}
|
||||
}
|
||||
|
|
@ -22,15 +22,16 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class CollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
||||
UICollectionViewDataSource,
|
||||
UICollectionViewDelegate {
|
||||
open class DefaultFiltersCollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
||||
CollectionDirectorRepresenter,
|
||||
UICollectionViewDataSource,
|
||||
UICollectionViewDelegate {
|
||||
|
||||
private(set) weak var collectionView: UICollectionView?
|
||||
private weak var scrollDelegate: UIScrollViewDelegate?
|
||||
|
||||
private var _collectionItems: [FilterItem] = []
|
||||
|
||||
|
||||
public weak var collectionView: UICollectionView?
|
||||
public weak var delegate: FilterItemsDelegate?
|
||||
|
||||
public var collectionItems: [FilterItem] {
|
||||
get {
|
||||
_collectionItems
|
||||
|
|
@ -41,11 +42,7 @@ open class CollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
|||
}
|
||||
}
|
||||
|
||||
public weak var delegate: FilterItemsDelegate?
|
||||
|
||||
public init(collectionView: UICollectionView, scrollDelegate: UIScrollViewDelegate? = nil) {
|
||||
self.scrollDelegate = scrollDelegate
|
||||
|
||||
public init(collectionView: UICollectionView) {
|
||||
super.init()
|
||||
|
||||
bind(to: collectionView)
|
||||
|
|
@ -70,19 +67,8 @@ open class CollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
|||
scrollToItem(at: indexPath)
|
||||
}
|
||||
|
||||
public func replaceItem(with item: FilterItem, at index: Int) {
|
||||
collectionItems[index] = item
|
||||
|
||||
let indexPath = IndexPath(row: index, section: .zero)
|
||||
scrollToItem(at: indexPath)
|
||||
}
|
||||
|
||||
public func replaceItem(with item: FilterItem, at indexPath: IndexPath) {
|
||||
collectionItems[indexPath.row] = item
|
||||
}
|
||||
|
||||
public func update(item: FilterItem, at indexPath: IndexPath) {
|
||||
_collectionItems[indexPath.item] = item
|
||||
public func update(item: FilterItem, at index: Int) {
|
||||
_collectionItems[index] = item
|
||||
}
|
||||
|
||||
public func scrollToItem(at indexPath: IndexPath, animated: Bool = true) {
|
||||
|
|
@ -108,7 +94,7 @@ open class CollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
|||
viewModel.configure(item: cell)
|
||||
return cell
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
|
||||
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
|
|
@ -116,15 +102,11 @@ open class CollectionDirector<FilterItem: FilterCollectionItem>: NSObject,
|
|||
didSelectItem(atIndexPath: indexPath, cell: cell)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
scrollDelegate?.scrollViewDidEndDecelerating?(scrollView)
|
||||
}
|
||||
|
||||
private func didSelectItem(atIndexPath indexPath: IndexPath,
|
||||
cell: UICollectionViewCell?) {
|
||||
|
||||
guard let item = item(at: indexPath) else { return }
|
||||
|
||||
|
||||
delegate?.didSelectItem(atIndexPath: indexPath)
|
||||
|
||||
item.didSelectItem(atIndexPath: indexPath, cell: cell)
|
||||
|
|
|
|||
|
|
@ -62,13 +62,13 @@ open class BaseFilterCollectionCell: UICollectionViewCell,
|
|||
open func addViews() {
|
||||
addSubview(titleLabel)
|
||||
}
|
||||
|
||||
|
||||
open func configureLayout() {
|
||||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
|
||||
NSLayoutConstraint.activate(titleLabel.edgesEqualToSuperview())
|
||||
}
|
||||
|
||||
|
||||
open func bindViews() {
|
||||
// override in subclass
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,18 +23,6 @@
|
|||
import UIKit
|
||||
import TIUIKitCore
|
||||
|
||||
public protocol SelectableCell: UICollectionViewCell {
|
||||
var isFilterSelected: Bool { get set }
|
||||
|
||||
func toggleSelection()
|
||||
}
|
||||
|
||||
public extension SelectableCell {
|
||||
func toggleSelection() {
|
||||
isFilterSelected.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
public typealias AnyCollectionCell = SelectableCell & ConfigurableView
|
||||
|
||||
open class BaseFilterCollectionItem<CellType: AnyCollectionCell,
|
||||
|
|
@ -45,7 +33,7 @@ open class BaseFilterCollectionItem<CellType: AnyCollectionCell,
|
|||
}
|
||||
|
||||
public var filter: Filter
|
||||
|
||||
|
||||
public var viewModel: CellType.ViewModelType?
|
||||
|
||||
required public init(filter: Filter, viewModel: CellType.ViewModelType) {
|
||||
|
|
@ -62,6 +50,6 @@ open class BaseFilterCollectionItem<CellType: AnyCollectionCell,
|
|||
}
|
||||
|
||||
open func didSelectItem(atIndexPath indexPath: IndexPath, cell: UICollectionViewCell?) {
|
||||
|
||||
// Override in subviews
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@
|
|||
import TIUIKitCore
|
||||
import UIKit
|
||||
|
||||
typealias DefaultBaseFiltersCollectionView = BaseFiltersCollectionView<BaseFilterCollectionItem<BaseFilterCollectionCell, DefaultFilterPropertyValue>>
|
||||
typealias DefaultBaseFiltersCollectionView = BaseFiltersCollectionView<BaseFilterCollectionItem<BaseFilterCollectionCell,
|
||||
DefaultFilterPropertyValue>>
|
||||
|
||||
open class BaseFiltersCollectionView<Cell: FilterCollectionItem>: BaseInitializableView, ConfigurableView {
|
||||
|
||||
typealias Director = CollectionDirector<Cell>
|
||||
typealias Director = DefaultFiltersCollectionDirector<Cell>
|
||||
|
||||
private var collectionDirector: Director
|
||||
|
||||
|
|
@ -91,10 +92,9 @@ open class BaseFiltersCollectionView<Cell: FilterCollectionItem>: BaseInitializa
|
|||
// MARK: - FiltersCollectionHolder
|
||||
|
||||
extension BaseFiltersCollectionView: FiltersCollectionHolder {
|
||||
public func select(_ items: [FilterPropertyValueRepresenter]) {
|
||||
open func select(_ items: [FilterPropertyValueRepresenter]) {
|
||||
guard let viewModel = viewModel else { return }
|
||||
|
||||
// TODO: replace instead of make new
|
||||
items.forEach { item in
|
||||
if let index = viewModel.filters.firstIndex(of: item as! DefaultFilterPropertyValue) {
|
||||
viewModel.filters[index].isSelected = true
|
||||
|
|
@ -102,10 +102,9 @@ extension BaseFiltersCollectionView: FiltersCollectionHolder {
|
|||
}
|
||||
}
|
||||
|
||||
public func deselect(_ items: [FilterPropertyValueRepresenter]) {
|
||||
open func deselect(_ items: [FilterPropertyValueRepresenter]) {
|
||||
guard let viewModel = viewModel else { return }
|
||||
|
||||
// TODO: replace instead of make new
|
||||
items.forEach { item in
|
||||
if let index = viewModel.filters.firstIndex(of: item as! DefaultFilterPropertyValue) {
|
||||
viewModel.filters[index].isSelected = false
|
||||
|
|
@ -113,7 +112,7 @@ extension BaseFiltersCollectionView: FiltersCollectionHolder {
|
|||
}
|
||||
}
|
||||
|
||||
public func updateView() {
|
||||
open func updateView() {
|
||||
guard let viewModel = viewModel else { return }
|
||||
|
||||
collectionDirector.collectionItems = viewModel.filters.map {
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@ ORDERED_PODSPECS="../TISwiftUtils/TISwiftUtils.podspec
|
|||
../TIYandexMapUtils/TIYandexMapUtils.podspec"
|
||||
|
||||
for podspec_path in ${ORDERED_PODSPECS}; do
|
||||
bundle exec pod repo push git@github.com:castlele/Podspecs-castle ${podspec_path} --allow-warnings
|
||||
bundle exec pod repo push git@github.com:TouchInstinct/Podspecs ${podspec_path} --allow-warnings
|
||||
done
|
||||
|
|
|
|||
Loading…
Reference in New Issue