diff --git a/TIEcommerce/Sources/Filters/Models/BaseFilterCellAppearance.swift b/TIEcommerce/Sources/Filters/Models/BaseFilterCellAppearance.swift index 4bc4e447..4f31899e 100644 --- a/TIEcommerce/Sources/Filters/Models/BaseFilterCellAppearance.swift +++ b/TIEcommerce/Sources/Filters/Models/BaseFilterCellAppearance.swift @@ -26,17 +26,26 @@ open class BaseFilterCellAppearance: FilterCellAppearanceProtocol { public var selectedColor: UIColor public var selectedBgColor: UIColor public var deselectedBgColor: UIColor + public var selectedFontColor: UIColor + public var deselectedFontColor: UIColor public var contentInsets: UIEdgeInsets + public var cornerRadius: CGFloat - init(selectedColor: UIColor, - selectedBgColor: UIColor, - deselectedBgColor: UIColor, - contentInsets: UIEdgeInsets) { + public init(selectedColor: UIColor, + selectedBgColor: UIColor, + deselectedBgColor: UIColor, + selectedFontColor: UIColor, + deselectedFontColor: UIColor, + contentInsets: UIEdgeInsets, + cornerRadius: CGFloat) { self.selectedColor = selectedColor self.selectedBgColor = selectedBgColor self.deselectedBgColor = deselectedBgColor + self.selectedFontColor = selectedFontColor + self.deselectedFontColor = deselectedFontColor self.contentInsets = contentInsets + self.cornerRadius = cornerRadius } } @@ -47,6 +56,9 @@ public extension BaseFilterCellAppearance { .init(selectedColor: .systemGreen, selectedBgColor: .white, deselectedBgColor: .lightGray, - contentInsets: .init(top: 4, left: 8, bottom: 4, right: 8)) + selectedFontColor: .systemGreen, + deselectedFontColor: .black, + contentInsets: .init(top: 4, left: 8, bottom: 4, right: 8), + cornerRadius: 6) } -} \ No newline at end of file +} diff --git a/TIEcommerce/Sources/Filters/Models/DefaultFilterModel.swift b/TIEcommerce/Sources/Filters/Models/DefaultFilterModel.swift deleted file mode 100644 index 2f116ded..00000000 --- a/TIEcommerce/Sources/Filters/Models/DefaultFilterModel.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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 struct DefaultFilterModel: FilterRepresenter { - public let id: String - public let property: DefaultFilterPropertyValue? - public let properties: [DefaultFilterPropertyValue]? -} - -public extension DefaultFilterModel { - init(id: String, property: DefaultFilterPropertyValue) { - self.id = id - self.property = property - self.properties = nil - } - - init(id: String, properties: [DefaultFilterPropertyValue]) { - self.id = id - self.property = nil - self.properties = properties - } -} diff --git a/TIEcommerce/Sources/Filters/Models/DefaultFilterPropertyValue.swift b/TIEcommerce/Sources/Filters/Models/DefaultFilterPropertyValue.swift index 3990634d..fa0952d4 100644 --- a/TIEcommerce/Sources/Filters/Models/DefaultFilterPropertyValue.swift +++ b/TIEcommerce/Sources/Filters/Models/DefaultFilterPropertyValue.swift @@ -22,17 +22,19 @@ import UIKit +@available(iOS 13, *) public struct DefaultFilterPropertyValue: FilterPropertyValueRepresenter, Identifiable { public let id: String public let title: String - public let excludingProperties: [String]? + public let excludingProperties: [String] public var isSelected: Bool } +@available(iOS 13, *) public extension DefaultFilterPropertyValue { - init(id: String, title: String, excludingProperties: [String]? = nil) { + init(id: String, title: String, excludingProperties: [String] = []) { self.id = id self.title = title self.excludingProperties = excludingProperties @@ -40,6 +42,7 @@ public extension DefaultFilterPropertyValue { } } +@available(iOS 13, *) extension DefaultFilterPropertyValue: Hashable { public static func == (lhs: DefaultFilterPropertyValue, rhs: DefaultFilterPropertyValue) -> Bool { lhs.id == rhs.id diff --git a/TIEcommerce/Sources/Filters/Models/FilterCellViewModelProtocol.swift b/TIEcommerce/Sources/Filters/Models/FilterCellViewModelProtocol.swift index b68516d6..f7e9c13b 100644 --- a/TIEcommerce/Sources/Filters/Models/FilterCellViewModelProtocol.swift +++ b/TIEcommerce/Sources/Filters/Models/FilterCellViewModelProtocol.swift @@ -24,5 +24,4 @@ public protocol FilterCellViewModelProtocol { var id: String { get set } var title: String { get set } var isSelected: Bool { get set } - var appearance: FilterCellAppearanceProtocol { get } } diff --git a/TIEcommerce/Sources/Filters/Protocols/FilterCellAppearanceProtocol.swift b/TIEcommerce/Sources/Filters/Protocols/FilterCellAppearanceProtocol.swift index 282a9246..91f0c617 100644 --- a/TIEcommerce/Sources/Filters/Protocols/FilterCellAppearanceProtocol.swift +++ b/TIEcommerce/Sources/Filters/Protocols/FilterCellAppearanceProtocol.swift @@ -26,5 +26,8 @@ public protocol FilterCellAppearanceProtocol { var selectedColor: UIColor { get set } var selectedBgColor: UIColor { get set } var deselectedBgColor: UIColor { get set } + var selectedFontColor: UIColor { get set } + var deselectedFontColor: UIColor { get set } var contentInsets: UIEdgeInsets { get set } -} \ No newline at end of file + var cornerRadius: CGFloat { get set } +} diff --git a/TIEcommerce/Sources/Filters/Protocols/FilterPropertyValueRepresenter.swift b/TIEcommerce/Sources/Filters/Protocols/FilterPropertyValueRepresenter.swift index 18aff49b..7cb4c304 100644 --- a/TIEcommerce/Sources/Filters/Protocols/FilterPropertyValueRepresenter.swift +++ b/TIEcommerce/Sources/Filters/Protocols/FilterPropertyValueRepresenter.swift @@ -22,6 +22,6 @@ public protocol FilterPropertyValueRepresenter { var id: String { get } - var excludingProperties: [String]? { get } + var excludingProperties: [String] { get } var isSelected: Bool { get set } } diff --git a/TIEcommerce/Sources/Filters/Protocols/FilterRepresenter.swift b/TIEcommerce/Sources/Filters/Protocols/FilterRepresenter.swift deleted file mode 100644 index 5fe2ba52..00000000 --- a/TIEcommerce/Sources/Filters/Protocols/FilterRepresenter.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// 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 FilterRepresenter { - - associatedtype FilterProperty: FilterPropertyValueRepresenter - - var id: String { get } - var property: FilterProperty? { get } - var properties: [FilterProperty]? { get } -} diff --git a/TIEcommerce/Sources/Filters/Protocols/FilterViewModelProtocol.swift b/TIEcommerce/Sources/Filters/Protocols/FilterViewModelProtocol.swift index b3eb51ab..4a9ebb11 100644 --- a/TIEcommerce/Sources/Filters/Protocols/FilterViewModelProtocol.swift +++ b/TIEcommerce/Sources/Filters/Protocols/FilterViewModelProtocol.swift @@ -24,17 +24,17 @@ import Foundation public protocol FilterViewModelProtocol: AnyObject { - associatedtype Property: FilterPropertyValueRepresenter, Hashable + associatedtype Property: FilterPropertyValueRepresenter & Hashable associatedtype CellViewModel: FilterCellViewModelProtocol & Hashable typealias Change = (indexPath: IndexPath, viewModel: CellViewModel) var properties: [Property] { get set } - var selectedProperties: Set { get set } + var selectedProperties: [Property] { get set } func filterDidSelected(atIndexPath indexPath: IndexPath) -> [Change] func toggleProperty(atIndexPath indexPath: IndexPath) -> (selected: [Property], deselected: [Property]) - func getCellsViewModels() -> [CellViewModel] +// func getCellsViewModels() -> [CellViewModel] } public extension FilterViewModelProtocol { @@ -49,17 +49,17 @@ public extension FilterViewModelProtocol { private func toggleProperty(_ property: Property) -> (selected: [Property], deselected: [Property]) { var propertiesToDeselect = [Property]() var propertiesToSelect = [Property]() - let selectedProperty = selectedProperties.first { selectedProperty in + let selectedPropertyId = selectedProperties.firstIndex { selectedProperty in selectedProperty.id == property.id } - if let selectedProperty = selectedProperty { + if let selectedPropertyId = selectedPropertyId { // Removes previously selected filter - selectedProperties.remove(selectedProperty) + selectedProperties.remove(at: selectedPropertyId) propertiesToDeselect.append(property) } else { // Selectes unselected filter - selectedProperties.insert(property) + selectedProperties.append(property) propertiesToSelect.append(property) // If the filter has filters to exclude, these filters marks as deselected @@ -71,7 +71,9 @@ public extension FilterViewModelProtocol { } private func excludeProperties(_ filter: Property) -> [Property] { - guard let propertiesToExclude = filter.excludingProperties, !propertiesToExclude.isEmpty else { + let propertiesToExclude = filter.excludingProperties + + guard !propertiesToExclude.isEmpty else { return [] } diff --git a/TIEcommerce/Sources/Filters/ViewModels/BaseFilterViewModel.swift b/TIEcommerce/Sources/Filters/ViewModels/BaseFilterViewModel.swift index 10c77c76..0af7553d 100644 --- a/TIEcommerce/Sources/Filters/ViewModels/BaseFilterViewModel.swift +++ b/TIEcommerce/Sources/Filters/ViewModels/BaseFilterViewModel.swift @@ -36,7 +36,7 @@ open class BaseFilterViewModel = [] { + public var selectedProperties: [PropertyValue] = [] { didSet { filtersCollection?.updateView() } @@ -44,8 +44,11 @@ open class BaseFilterViewModel [Change] { @@ -56,8 +59,6 @@ open class BaseFilterViewModel [CellViewModelType] { - // override in subclasses - return [] - } - - // MARK: - Public methods - - public func isFilterInArray(_ filter: PropertyValue, filters: [PropertyValue]) -> Bool { + open func isFilterInArray(_ filter: PropertyValue, filters: [PropertyValue]) -> Bool { filters.contains(where: { $0.id == filter.id }) } } diff --git a/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterCellViewModel.swift b/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterCellViewModel.swift index 689a1752..fd7d0afe 100644 --- a/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterCellViewModel.swift +++ b/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterCellViewModel.swift @@ -26,10 +26,6 @@ public struct DefaultFilterCellViewModel: FilterCellViewModelProtocol, Hashable public var title: String public var isSelected: Bool - public var appearance: FilterCellAppearanceProtocol { - BaseFilterCellAppearance.defaultFilterCellAppearance - } - public init(id: String, title: String, isSelected: Bool) { diff --git a/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterViewModel.swift b/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterViewModel.swift index 5c1b194e..454efdf5 100644 --- a/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterViewModel.swift +++ b/TIEcommerce/Sources/Filters/ViewModels/DefaultFilterViewModel.swift @@ -20,24 +20,16 @@ // THE SOFTWARE. // +@available(iOS 13, *) open class DefaultFilterViewModel: BaseFilterViewModel { - private var cellsViewModels: [DefaultFilterCellViewModel] = [] - - public override init(filters: [DefaultFilterPropertyValue]) { - super.init(filters: filters) - self.cellsViewModels = filters.compactMap { + public init(filters: [DefaultFilterPropertyValue]) { + let cellsViewModel = filters.compactMap { DefaultFilterCellViewModel(id: $0.id, title: $0.title, isSelected: $0.isSelected) } - } - open override func setSelectedCell(atIndex index: Int, isSelected: Bool) { - cellsViewModels[index].isSelected = isSelected - } - - open override func getCellsViewModels() -> [DefaultFilterCellViewModel] { - cellsViewModels + super.init(filters: filters, cellsViewModels: cellsViewModel) } } diff --git a/TIEcommerce/Sources/Filters/Views/BaseFiltersCollectionView.swift b/TIEcommerce/Sources/Filters/Views/BaseFiltersCollectionView.swift index a8c2d78e..618acfb8 100644 --- a/TIEcommerce/Sources/Filters/Views/BaseFiltersCollectionView.swift +++ b/TIEcommerce/Sources/Filters/Views/BaseFiltersCollectionView.swift @@ -85,7 +85,9 @@ open class BaseFiltersCollectionView, public var viewModel: DefaultFilterCellViewModel? - open override var reuseIdentifier: String? { + open var cellAppearance: BaseFilterCellAppearance { + .defaultFilterCellAppearance + } + + open class var reuseIdentifier: String { "default-filter-cell" } + open override var isSelected: Bool { + didSet { + if isSelected { + setSelectedAppearance() + } else { + setDeselectAppearance() + } + } + } + open override func configureAppearance() { super.configureAppearance() - layer.round(corners: .allCorners, radius: 6) + layer.round(corners: .allCorners, radius: cellAppearance.cornerRadius) + contentInsets = cellAppearance.contentInsets + + setDeselectAppearance() } // MARK: - ConfigurableView @@ -45,30 +62,20 @@ open class DefaultFilterCollectionCell: ContainerCollectionViewCell, self.viewModel = viewModel wrappedView.text = viewModel.title - contentInsets = viewModel.appearance.contentInsets - setSelected(isSelected: viewModel.isSelected) + isSelected = viewModel.isSelected } - // MARK: - Public methods - - open func setSelected(isSelected: Bool) { - let appearance = viewModel?.appearance - - let selectedColor = appearance?.selectedColor ?? .green - wrappedView.textColor = isSelected ? selectedColor : .black - - if isSelected { - backgroundColor = appearance?.selectedBgColor ?? .white - layer.borderColor = selectedColor.cgColor - layer.borderWidth = 1 - } else { - setDeselectAppearance() - } + open func setSelectedAppearance() { + wrappedView.textColor = cellAppearance.selectedFontColor + backgroundColor = cellAppearance.selectedBgColor + layer.borderColor = cellAppearance.selectedColor.cgColor + layer.borderWidth = 1 } open func setDeselectAppearance() { + wrappedView.textColor = cellAppearance.deselectedFontColor layer.borderWidth = 0 - backgroundColor = viewModel?.appearance.deselectedBgColor ?? .lightGray + backgroundColor = cellAppearance.deselectedBgColor } }