123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // BaseRow.swift
- // Eureka ( https://github.com/xmartlabs/Eureka )
- //
- // Copyright (c) 2016 Xmartlabs ( http://xmartlabs.com )
- //
- //
- // 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 Foundation
- import UIKit
- open class BaseRow: BaseRowType {
- var callbackOnChange: (() -> Void)?
- var callbackCellUpdate: (() -> Void)?
- var callbackCellSetup: Any?
- var callbackCellOnSelection: (() -> Void)?
- var callbackOnExpandInlineRow: Any?
- var callbackOnCollapseInlineRow: Any?
- var callbackOnCellHighlightChanged: (() -> Void)?
- var callbackOnRowValidationChanged: (() -> Void)?
- var _inlineRow: BaseRow?
- var _cachedOptionsData: Any?
- public var validationOptions: ValidationOptions = .validatesOnBlur
- // validation state
- public internal(set) var validationErrors = [ValidationError]() {
- didSet {
- guard validationErrors != oldValue else { return }
- RowDefaults.onRowValidationChanged["\(type(of: self))"]?(baseCell, self)
- callbackOnRowValidationChanged?()
- updateCell()
- }
- }
- public internal(set) var wasBlurred = false
- public internal(set) var wasChanged = false
- public var isValid: Bool { return validationErrors.isEmpty }
- public var isHighlighted: Bool = false
- /// The title will be displayed in the textLabel of the row.
- public var title: String?
- /// Parameter used when creating the cell for this row.
- public var cellStyle = UITableViewCell.CellStyle.value1
- /// String that uniquely identifies a row. Must be unique among rows and sections.
- public var tag: String?
- /// The untyped cell associated to this row.
- public var baseCell: BaseCell! { return nil }
- /// The untyped value of this row.
- public var baseValue: Any? {
- set {}
- get { return nil }
- }
- public func validate(quietly: Bool = false) -> [ValidationError] {
- return []
- }
- public static var estimatedRowHeight: CGFloat = 44.0
- /// Condition that determines if the row should be disabled or not.
- public var disabled: Condition? {
- willSet { removeFromDisabledRowObservers() }
- didSet { addToDisabledRowObservers() }
- }
- /// Condition that determines if the row should be hidden or not.
- public var hidden: Condition? {
- willSet { removeFromHiddenRowObservers() }
- didSet { addToHiddenRowObservers() }
- }
- /// Returns if this row is currently disabled or not
- public var isDisabled: Bool { return disabledCache }
- /// Returns if this row is currently hidden or not
- public var isHidden: Bool { return hiddenCache }
- /// The section to which this row belongs.
- open weak var section: Section?
-
- public lazy var trailingSwipe = {[unowned self] in SwipeConfiguration(self)}()
-
- //needs the accessor because if marked directly this throws "Stored properties cannot be marked potentially unavailable with '@available'"
- private lazy var _leadingSwipe = {[unowned self] in SwipeConfiguration(self)}()
- @available(iOS 11,*)
- public var leadingSwipe: SwipeConfiguration{
- get { return self._leadingSwipe }
- set { self._leadingSwipe = newValue }
- }
-
- public required init(tag: String? = nil) {
- self.tag = tag
- }
- /**
- Method that reloads the cell
- */
- open func updateCell() {}
- /**
- Method called when the cell belonging to this row was selected. Must call the corresponding method in its cell.
- */
- open func didSelect() {}
- open func prepare(for segue: UIStoryboardSegue) {}
- /**
- Helps to pick destination part of the cell after scrolling
- */
- open var destinationScrollPosition: UITableView.ScrollPosition? = UITableView.ScrollPosition.bottom
- /**
- Returns the IndexPath where this row is in the current form.
- */
- public final var indexPath: IndexPath? {
- guard let sectionIndex = section?.index, let rowIndex = section?.firstIndex(of: self) else { return nil }
- return IndexPath(row: rowIndex, section: sectionIndex)
- }
- var hiddenCache = false
- var disabledCache = false {
- willSet {
- if newValue && !disabledCache {
- baseCell.cellResignFirstResponder()
- }
- }
- }
- }
- extension BaseRow {
- // Reset validation
- public func cleanValidationErrors(){
- validationErrors = []
- }
- }
- extension BaseRow {
- /**
- Evaluates if the row should be hidden or not and updates the form accordingly
- */
- public final func evaluateHidden() {
- guard let h = hidden, let form = section?.form else { return }
- switch h {
- case .function(_, let callback):
- hiddenCache = callback(form)
- case .predicate(let predicate):
- hiddenCache = predicate.evaluate(with: self, substitutionVariables: form.dictionaryValuesToEvaluatePredicate())
- }
- if hiddenCache {
- section?.hide(row: self)
- } else {
- section?.show(row: self)
- }
- }
- /**
- Evaluates if the row should be disabled or not and updates it accordingly
- */
- public final func evaluateDisabled() {
- guard let d = disabled, let form = section?.form else { return }
- switch d {
- case .function(_, let callback):
- disabledCache = callback(form)
- case .predicate(let predicate):
- disabledCache = predicate.evaluate(with: self, substitutionVariables: form.dictionaryValuesToEvaluatePredicate())
- }
- updateCell()
- }
- final func wasAddedTo(section: Section) {
- self.section = section
- if let t = tag {
- assert(section.form?.rowsByTag[t] == nil, "Duplicate tag \(t)")
- self.section?.form?.rowsByTag[t] = self
- self.section?.form?.tagToValues[t] = baseValue != nil ? baseValue! : NSNull()
- }
- addToRowObservers()
- evaluateHidden()
- evaluateDisabled()
- }
- final func addToHiddenRowObservers() {
- guard let h = hidden else { return }
- switch h {
- case .function(let tags, _):
- section?.form?.addRowObservers(to: self, rowTags: tags, type: .hidden)
- case .predicate(let predicate):
- section?.form?.addRowObservers(to: self, rowTags: predicate.predicateVars, type: .hidden)
- }
- }
- final func addToDisabledRowObservers() {
- guard let d = disabled else { return }
- switch d {
- case .function(let tags, _):
- section?.form?.addRowObservers(to: self, rowTags: tags, type: .disabled)
- case .predicate(let predicate):
- section?.form?.addRowObservers(to: self, rowTags: predicate.predicateVars, type: .disabled)
- }
- }
- final func addToRowObservers() {
- addToHiddenRowObservers()
- addToDisabledRowObservers()
- }
- final func willBeRemovedFromForm() {
- (self as? BaseInlineRowType)?.collapseInlineRow()
- if let t = tag {
- section?.form?.rowsByTag[t] = nil
- section?.form?.tagToValues[t] = nil
- }
- removeFromRowObservers()
- }
- final func willBeRemovedFromSection() {
- willBeRemovedFromForm()
- section = nil
- }
- final func removeFromHiddenRowObservers() {
- guard let h = hidden else { return }
- switch h {
- case .function(let tags, _):
- section?.form?.removeRowObservers(from: self, rowTags: tags, type: .hidden)
- case .predicate(let predicate):
- section?.form?.removeRowObservers(from: self, rowTags: predicate.predicateVars, type: .hidden)
- }
- }
- final func removeFromDisabledRowObservers() {
- guard let d = disabled else { return }
- switch d {
- case .function(let tags, _):
- section?.form?.removeRowObservers(from: self, rowTags: tags, type: .disabled)
- case .predicate(let predicate):
- section?.form?.removeRowObservers(from: self, rowTags: predicate.predicateVars, type: .disabled)
- }
- }
- final func removeFromRowObservers() {
- removeFromHiddenRowObservers()
- removeFromDisabledRowObservers()
- }
- }
- extension BaseRow: Equatable, Hidable, Disableable {}
- extension BaseRow {
- public func reload(with rowAnimation: UITableView.RowAnimation = .none) {
- guard let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView, let indexPath = indexPath else { return }
- tableView.reloadRows(at: [indexPath], with: rowAnimation)
- }
- public func deselect(animated: Bool = true) {
- guard let indexPath = indexPath,
- let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView else { return }
- tableView.deselectRow(at: indexPath, animated: animated)
- }
- public func select(animated: Bool = false, scrollPosition: UITableView.ScrollPosition = .none) {
- guard let indexPath = indexPath,
- let tableView = baseCell?.formViewController()?.tableView ?? (section?.form?.delegate as? FormViewController)?.tableView else { return }
- tableView.selectRow(at: indexPath, animated: animated, scrollPosition: scrollPosition)
- }
- }
- public func == (lhs: BaseRow, rhs: BaseRow) -> Bool {
- return lhs === rhs
- }
|