diff --git a/FormEditor/Classes/FECustom.swift b/FormEditor/Classes/FECustom.swift index 7260aeb..37365d4 100755 --- a/FormEditor/Classes/FECustom.swift +++ b/FormEditor/Classes/FECustom.swift @@ -19,6 +19,19 @@ public class FECustom: PFEParam { self.configureCell = configureCell } + public func copy(from: PFEParam) { + guard let from = from as? FECustom else { + return + } + + self.id = from.id + self.cellReuseId = from.cellReuseId + self.cellNibName = from.cellNibName + self.visible = from.visible + self.onSelect = from.onSelect + self.configureCell = from.configureCell + } + public func configure(cell: UITableViewCell, facade: FormParamFacade) { configureCell?(cell) } diff --git a/FormEditor/Classes/FEDate.swift b/FormEditor/Classes/FEDate.swift index 3addc64..0b4c272 100755 --- a/FormEditor/Classes/FEDate.swift +++ b/FormEditor/Classes/FEDate.swift @@ -31,6 +31,23 @@ public class FEDate: PFEParam { self.valueChangeListener = listener } + public func copy(from: PFEParam) { + guard let from = from as? FEDate else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.displayableValueFormat = from.displayableValueFormat + self.readOnly = from.readOnly + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.minDate = from.minDate + self.maxDate = from.maxDate + self.valueChangeListener = from.valueChangeListener + } + public func configure(cell: UITableViewCell, facade: FormParamFacade) { if let paramCell = cell as? FEDateCell { paramCell.configure(facade: facade) diff --git a/FormEditor/Classes/FEDateCell.swift b/FormEditor/Classes/FEDateCell.swift index 66cc23c..79348e1 100755 --- a/FormEditor/Classes/FEDateCell.swift +++ b/FormEditor/Classes/FEDateCell.swift @@ -89,12 +89,17 @@ class FEDateCell: UITableViewCell, UITextFieldDelegate, FormParamFacadeDelegate } func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + let result = enableNavigationToolbar() + dateTextField.inputView = datePicker() + return result + } + + internal func enableNavigationToolbar() -> Bool { guard let facade = self.facade else { return false } - dateTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.editNextParam, movePreviousClosure: facade.editPreviousParam) - dateTextField.inputView = datePicker() + dateTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.functionForMoveToNextParam, movePreviousClosure: facade.functionForMoveToPreviousParam) return true } diff --git a/FormEditor/Classes/FELabel.swift b/FormEditor/Classes/FELabel.swift index 4d50a3e..e741815 100644 --- a/FormEditor/Classes/FELabel.swift +++ b/FormEditor/Classes/FELabel.swift @@ -26,6 +26,21 @@ public class FELabel: PFEParam { self.onSelect = onSelect } + public func copy(from: PFEParam) { + guard let from = from as? FELabel else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.mask = from.mask + self.alwaysShowTitle = from.alwaysShowTitle + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.onSelect = from.onSelect + } + public var canReceiveFocus: Bool { return false } diff --git a/FormEditor/Classes/FESelector.swift b/FormEditor/Classes/FESelector.swift index e7f9f52..a884303 100755 --- a/FormEditor/Classes/FESelector.swift +++ b/FormEditor/Classes/FESelector.swift @@ -34,6 +34,25 @@ public class FESelector: PFEParam { } } + public func copy(from: PFEParam) { + guard let from = from as? FESelector else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.emptyVisibleValue = from.emptyVisibleValue + self.displayableValueFormat = from.displayableValueFormat + self.readOnly = from.readOnly + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.valueChangeListener = from.valueChangeListener + + self.items = [] + self.items?.append(contentsOf: (from.items ?? [])) + } + public func configure(cell: UITableViewCell, facade: FormParamFacade) { if let paramCell = cell as? FESelectorCell { paramCell.configure(facade: facade) diff --git a/FormEditor/Classes/FESelectorCell.swift b/FormEditor/Classes/FESelectorCell.swift index d98fe7b..e4d2e4e 100755 --- a/FormEditor/Classes/FESelectorCell.swift +++ b/FormEditor/Classes/FESelectorCell.swift @@ -75,7 +75,7 @@ class FESelectorCell: UITableViewCell, UITextFieldDelegate, UIPickerViewDelegate return false } valueTextField.inputView = pickerView() - valueTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.editNextParam, movePreviousClosure: facade.editPreviousParam) + valueTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.functionForMoveToNextParam, movePreviousClosure: facade.functionForMoveToPreviousParam) return true } diff --git a/FormEditor/Classes/FESwitch.swift b/FormEditor/Classes/FESwitch.swift index dd729be..6dd6fe5 100644 --- a/FormEditor/Classes/FESwitch.swift +++ b/FormEditor/Classes/FESwitch.swift @@ -26,6 +26,20 @@ public class FESwitch: PFEParam { self.listener = listener } + public func copy(from: PFEParam) { + guard let from = from as? FESwitch else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.readOnly = from.readOnly + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.listener = from.listener + } + public func configure(cell: UITableViewCell, facade: FormParamFacade) { if let paramCell = cell as? FESwitchCell { paramCell.configure(facade: facade) diff --git a/FormEditor/Classes/FEText.swift b/FormEditor/Classes/FEText.swift index 244b017..5fb5065 100755 --- a/FormEditor/Classes/FEText.swift +++ b/FormEditor/Classes/FEText.swift @@ -37,6 +37,26 @@ public class FEText: PFEParam { self.accessoryImageNames = accessoryImageNames } + public func copy(from: PFEParam) { + guard let from = from as? FEText else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.keyboardType = from.keyboardType + self.autocapitalizationType = from.autocapitalizationType + self.inputMask = from.inputMask + self.inputMaskForwardDecoration = from.inputMaskForwardDecoration + self.maxLength = from.maxLength + self.readOnly = from.readOnly + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.valueChangeListener = from.valueChangeListener + self.accessoryImageNames = from.accessoryImageNames + } + public var canReceiveFocus: Bool { return !readOnly && isVisible() } diff --git a/FormEditor/Classes/FETextArea.swift b/FormEditor/Classes/FETextArea.swift index e5e3d33..d9f3485 100644 --- a/FormEditor/Classes/FETextArea.swift +++ b/FormEditor/Classes/FETextArea.swift @@ -24,6 +24,20 @@ public class FETextArea: PFEParam { self.valueChangeListener = listener } + public func copy(from: PFEParam) { + guard let from = from as? FETextArea else { + return + } + + self.id = from.id + self.title = from.title + self.value = from.value + self.readOnly = from.readOnly + self.visible = from.visible + self.accessibilityIdentifier = from.accessibilityIdentifier + self.valueChangeListener = from.valueChangeListener + } + public var canReceiveFocus: Bool { return !readOnly && isVisible() } diff --git a/FormEditor/Classes/FETextAreaCell.swift b/FormEditor/Classes/FETextAreaCell.swift index c88ec43..18ee73d 100644 --- a/FormEditor/Classes/FETextAreaCell.swift +++ b/FormEditor/Classes/FETextAreaCell.swift @@ -54,10 +54,16 @@ class FETextAreaCell: UITableViewCell, UITextViewDelegate, FormParamFacadeDelega } func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { + let result = enableNavigationToolbar() + return result + } + + internal func enableNavigationToolbar() -> Bool { guard let facade = self.facade else { return false } - valueTextView.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.editNextParam, movePreviousClosure: facade.editPreviousParam) + + valueTextView.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.functionForMoveToNextParam, movePreviousClosure: facade.functionForMoveToPreviousParam) return true } diff --git a/FormEditor/Classes/FETextCell.swift b/FormEditor/Classes/FETextCell.swift index a2793b6..2ec8de7 100755 --- a/FormEditor/Classes/FETextCell.swift +++ b/FormEditor/Classes/FETextCell.swift @@ -102,10 +102,16 @@ class FETextCell: UITableViewCell, UITextFieldDelegate, FormParamFacadeDelegate } func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + let result = enableNavigationToolbar() + return result + } + + internal func enableNavigationToolbar() -> Bool { guard let facade = self.facade else { return false } - valueTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.editNextParam, movePreviousClosure: facade.editPreviousParam) + + valueTextField.enableParamsNavigationToolbar(preferences: facade.preferences, moveNextClosure: facade.functionForMoveToNextParam, movePreviousClosure: facade.functionForMoveToPreviousParam) return true } diff --git a/FormEditor/Classes/FormEditorFacade.swift b/FormEditor/Classes/FormEditorFacade.swift index 5262dac..a53217a 100644 --- a/FormEditor/Classes/FormEditorFacade.swift +++ b/FormEditor/Classes/FormEditorFacade.swift @@ -216,6 +216,8 @@ class FormEditorFacade { let addedSections = self.addedSections(oldSections: oldSections, newSections: newSections) let deletedSections = self.deletedSections(oldSections: oldSections, newSections: newSections) + replaceParams(fromOldSections: oldSections, toNewSections: newSections) + visibleSections = newSections delegate.beginUpdates() @@ -242,10 +244,45 @@ class FormEditorFacade { } updatedItems.forEach({delegate.reload(indexPath: $0)}) + // if the first item in the list was changed, then update NavigationBar's buttons + if let firstVisibleParamOld = firstVisibleFocusableParam(fromSections: oldSections), let firstVisibleParamNew = firstVisibleFocusableParam(fromSections: newSections), firstVisibleParamOld.id != firstVisibleParamNew.id { + updateNavigationBar( forParam: firstVisibleParamOld, sections: oldSections) + updateNavigationBar( forParam: firstVisibleParamNew, sections: newSections) + } + // if the last item in the list was changed, then update NavigationBar's buttons + if let lastVisibleParamOld = lastVisibleFocusableParam(fromSections: oldSections), let lastVisibleParamNew = lastVisibleFocusableParam(fromSections: newSections), lastVisibleParamOld.id != lastVisibleParamNew.id { + updateNavigationBar( forParam: lastVisibleParamOld, sections: oldSections) + updateNavigationBar( forParam: lastVisibleParamNew, sections: newSections) + } + delegate.endUpdates() } + private func updateNavigationBar( forParam: PFEParam, sections: [FESection]) { + if let indexPath = indexPath(param: forParam, sections: sections) { + if let cell = (form as! UITableViewController).tableView.cellForRow(at: indexPath) { + if let cell = (cell as? FEDateCell) { + let _ = cell.enableNavigationToolbar() + } + + if let cell = (cell as? FETextCell) { + let _ = cell.enableNavigationToolbar() + } + + if let cell = (cell as? FETextAreaCell) { + let _ = cell.enableNavigationToolbar() + } + } + } + } + private func firstVisibleFocusableParam( fromSections sections: [FESection]) -> PFEParam? { + return sections.first?.params?.first(where: { $0.isVisible() && $0.canReceiveFocus }) + } + + private func lastVisibleFocusableParam( fromSections sections: [FESection]) -> PFEParam? { + return sections.last?.params?.filter({ $0.isVisible() && $0.canReceiveFocus }).last + } private func addedSections(oldSections: [FESection], newSections: [FESection]) -> [Int] { guard newSections.count > oldSections.count else { @@ -359,6 +396,24 @@ class FormEditorFacade { } return updatedItems } + + private func replaceParams(fromOldSections oldSections: [FESection], toNewSections newSections: [FESection]) { + for (newSectionIndex, newSection) in newSections.enumerated() { + for (newParamIndex, newParam) in (newSection.params ?? []).enumerated() { + for oldSection in oldSections { + for oldParam in oldSection.params ?? [] { + if newParam.id == oldParam.id { + if !newParam.equals(other: oldParam) { + oldParam.copy(from: newParam) + } + newSections[newSectionIndex].params?[newParamIndex] = oldParam + } + } + } + } + } + } + } diff --git a/FormEditor/Classes/FormParamFacade.swift b/FormEditor/Classes/FormParamFacade.swift index 37dd33c..72c9688 100644 --- a/FormEditor/Classes/FormParamFacade.swift +++ b/FormEditor/Classes/FormParamFacade.swift @@ -59,6 +59,30 @@ public class FormParamFacade { // MARK: Взаимодействие с другими параметрами + var isPreviousParamExists: Bool { + return formEditorFacade?.previous(param: param) != nil + } + + var isNextParamExists: Bool { + return formEditorFacade?.next(param: param) != nil + } + + public var functionForMoveToNextParam: (()-> Void)? { + guard isNextParamExists else { + return nil + } + + return editNextParam + } + + public var functionForMoveToPreviousParam: (()-> Void)? { + guard isPreviousParamExists else { + return nil + } + + return editPreviousParam + } + public func editPreviousParam() { if let previousParam = formEditorFacade?.previous(param: param) { formEditorFacade?.select(param: previousParam, scrollToPosition: true) diff --git a/FormEditor/Classes/PFEParam.swift b/FormEditor/Classes/PFEParam.swift index b3ab87c..9060581 100644 --- a/FormEditor/Classes/PFEParam.swift +++ b/FormEditor/Classes/PFEParam.swift @@ -18,4 +18,6 @@ public protocol PFEParam: class { func isVisible() -> Bool func equals(other: PFEParam) -> Bool + + func copy(from: PFEParam) } diff --git a/FormEditor/Classes/TextFieldExtensions.swift b/FormEditor/Classes/TextFieldExtensions.swift index e3b6aad..5a94f0c 100755 --- a/FormEditor/Classes/TextFieldExtensions.swift +++ b/FormEditor/Classes/TextFieldExtensions.swift @@ -1,8 +1,15 @@ import UIKit extension UITextField { - func enableParamsNavigationToolbar(preferences: FEPreferences, moveNextClosure: @escaping (() -> Void), movePreviousClosure: @escaping (() -> Void)) { + func enableParamsNavigationToolbar(preferences: FEPreferences, moveNextClosure: (() -> Void)?, movePreviousClosure: (() -> Void)?) { + + let needReloadNavigator = (self.inputAccessoryView != nil) + self.inputAccessoryView = navigationToolbar(target: self, preferences: preferences, onDoneButtonClick: #selector(onDoneButtonClick), moveNextClosure: moveNextClosure, movePreviousClosure: movePreviousClosure) + + if needReloadNavigator { + self.reloadInputViews() + } } @objc private func onDoneButtonClick() { @@ -11,7 +18,7 @@ extension UITextField { } extension UITextView { - func enableParamsNavigationToolbar(preferences: FEPreferences, moveNextClosure: @escaping (() -> Void), movePreviousClosure: @escaping (() -> Void)) { + func enableParamsNavigationToolbar(preferences: FEPreferences, moveNextClosure: (() -> Void)?, movePreviousClosure: (() -> Void)? ) { self.inputAccessoryView = navigationToolbar(target: self, preferences: preferences, onDoneButtonClick: #selector(onDoneButtonClick), moveNextClosure: moveNextClosure, movePreviousClosure: movePreviousClosure) } @@ -20,7 +27,7 @@ extension UITextView { } } -fileprivate func navigationToolbar(target: Any, preferences: FEPreferences, onDoneButtonClick: Selector, moveNextClosure: @escaping (() -> Void), movePreviousClosure: @escaping (() -> Void)) -> UIToolbar { +fileprivate func navigationToolbar(target: Any, preferences: FEPreferences, onDoneButtonClick: Selector, moveNextClosure: (() -> Void)?, movePreviousClosure: (() -> Void)? ) -> UIToolbar { // Отступы let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) @@ -46,7 +53,9 @@ fileprivate func navigationToolbar(target: Any, preferences: FEPreferences, onDo fileprivate class NavigationButtons: UISegmentedControl { private var moveNextClosure: (() -> Void)? + private var moveNextClosureIndex: Int? private var movePreviousClosure: (() -> Void)? + private var movePreviousClosureIndex: Int? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -56,20 +65,43 @@ fileprivate class NavigationButtons: UISegmentedControl { super.init(frame: frame) } - init(preferences: FEPreferences, moveNextClosure: @escaping (() -> Void), movePreviousClosure: @escaping (() -> Void)) { - super.init(items: [preferences.labels.inputAccessory.back, preferences.labels.inputAccessory.forward]) - self.movePreviousClosure = movePreviousClosure - self.moveNextClosure = moveNextClosure + init(preferences: FEPreferences, moveNextClosure: (() -> Void)?, movePreviousClosure: (() -> Void)?) { + var items: [Any] = [] + + if movePreviousClosure != nil { + items.append(preferences.labels.inputAccessory.back) + } + if moveNextClosure != nil { + items.append(preferences.labels.inputAccessory.forward) + } + super.init(items: items) + + if movePreviousClosure != nil { + movePreviousClosureIndex = 0 + self.movePreviousClosure = movePreviousClosure + } + if moveNextClosure != nil { + moveNextClosureIndex = items.count - 1 + self.moveNextClosure = moveNextClosure + } + + let allItems = [preferences.labels.inputAccessory.back, preferences.labels.inputAccessory.forward] + if let maxSegmentWidth = allItems.map( { $0.size(attributes: self.titleTextAttributes(for: .normal) as? [String : Any]).width } ).max() { + for (index, _ ) in items.enumerated() { + setWidth(maxSegmentWidth + 20 , forSegmentAt: index) + } + } + addTarget(self, action: #selector(onValueChanged), for: .valueChanged) } func onValueChanged() { - if selectedSegmentIndex == 0 { + if selectedSegmentIndex == movePreviousClosureIndex { movePreviousClosure?() } - if selectedSegmentIndex == 1 { + if selectedSegmentIndex == moveNextClosureIndex { moveNextClosure?() }