|
|
|
// Copyright SIX DAY LLC. All rights reserved.
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import UIKit
|
|
|
|
import Eureka
|
|
|
|
|
|
|
|
// MARK: FloatLabelCell
|
|
|
|
public class _FloatLabelCell<T>: Cell<T>, UITextFieldDelegate, TextFieldCell where T: Equatable, T: InputTypeInitiable {
|
|
|
|
|
|
|
|
public var textField: UITextField! { return floatLabelTextField }
|
|
|
|
|
|
|
|
required public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
lazy public var floatLabelTextField: FloatLabelTextField = { [unowned self] in
|
|
|
|
let floatTextField = FloatLabelTextField()
|
|
|
|
floatTextField.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
floatTextField.font = .preferredFont(forTextStyle: .body)
|
|
|
|
floatTextField.titleFont = .boldSystemFont(ofSize: 11.0)
|
|
|
|
floatTextField.clearButtonMode = .whileEditing
|
|
|
|
return floatTextField
|
|
|
|
}()
|
|
|
|
|
|
|
|
open override func setup() {
|
|
|
|
super.setup()
|
|
|
|
height = { 55 }
|
|
|
|
selectionStyle = .none
|
|
|
|
contentView.addSubview(floatLabelTextField)
|
|
|
|
floatLabelTextField.delegate = self
|
|
|
|
floatLabelTextField.addTarget(self, action: #selector(_FloatLabelCell.textFieldDidChange(_:)), for: .editingChanged)
|
|
|
|
contentView.addConstraints(layoutConstraints())
|
|
|
|
}
|
|
|
|
|
|
|
|
open override func update() {
|
|
|
|
super.update()
|
|
|
|
textLabel?.text = nil
|
|
|
|
detailTextLabel?.text = nil
|
|
|
|
floatLabelTextField.attributedPlaceholder = NSAttributedString(string: row.title ?? "", attributes: [.foregroundColor: UIColor.lightGray])
|
|
|
|
floatLabelTextField.text = row.displayValueFor?(row.value)
|
|
|
|
floatLabelTextField.isEnabled = !row.isDisabled
|
|
|
|
floatLabelTextField.titleTextColour = .lightGray
|
|
|
|
floatLabelTextField.alpha = row.isDisabled ? 0.6 : 1
|
|
|
|
}
|
|
|
|
|
|
|
|
open override func cellCanBecomeFirstResponder() -> Bool {
|
|
|
|
return !row.isDisabled && floatLabelTextField.canBecomeFirstResponder
|
|
|
|
}
|
|
|
|
|
|
|
|
open override func cellBecomeFirstResponder(withDirection direction: Direction) -> Bool {
|
|
|
|
return floatLabelTextField.becomeFirstResponder()
|
|
|
|
}
|
|
|
|
|
|
|
|
open override func cellResignFirstResponder() -> Bool {
|
|
|
|
return floatLabelTextField.resignFirstResponder()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func layoutConstraints() -> [NSLayoutConstraint] {
|
|
|
|
let views = ["floatLabeledTextField": floatLabelTextField]
|
|
|
|
let metrics = ["vMargin": 8.0]
|
|
|
|
return NSLayoutConstraint.constraints(
|
|
|
|
withVisualFormat: "H:|-[floatLabeledTextField]-|",
|
|
|
|
options: .alignAllLastBaseline,
|
|
|
|
metrics: metrics,
|
|
|
|
views: views
|
|
|
|
) +
|
|
|
|
NSLayoutConstraint.constraints(
|
|
|
|
withVisualFormat: "V:|-(vMargin)-[floatLabeledTextField]-(vMargin)-|",
|
|
|
|
options: .alignAllLastBaseline,
|
|
|
|
metrics: metrics,
|
|
|
|
views: views
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
@objc public func textFieldDidChange(_ textField: UITextField) {
|
|
|
|
guard let textValue = textField.text else {
|
|
|
|
row.value = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if let fieldRow = row as? FormatterConformance, let formatter = fieldRow.formatter {
|
|
|
|
if fieldRow.useFormatterDuringInput {
|
|
|
|
let value: AutoreleasingUnsafeMutablePointer<AnyObject?> = AutoreleasingUnsafeMutablePointer<AnyObject?>.init(UnsafeMutablePointer<T>.allocate(capacity: 1))
|
|
|
|
let errorDesc: AutoreleasingUnsafeMutablePointer<NSString?>? = nil
|
|
|
|
if formatter.getObjectValue(value, for: textValue, errorDescription: errorDesc) {
|
|
|
|
row.value = value.pointee as? T
|
|
|
|
if var selStartPos = textField.selectedTextRange?.start {
|
|
|
|
let oldVal = textField.text
|
|
|
|
textField.text = row.displayValueFor?(row.value)
|
|
|
|
if let f = formatter as? FormatterProtocol {
|
|
|
|
selStartPos = f.getNewPosition(forPosition: selStartPos, inTextInput: textField, oldValue: oldVal, newValue: textField.text)
|
|
|
|
}
|
|
|
|
textField.selectedTextRange = textField.textRange(from: selStartPos, to: selStartPos)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let value: AutoreleasingUnsafeMutablePointer<AnyObject?> = AutoreleasingUnsafeMutablePointer<AnyObject?>.init(UnsafeMutablePointer<T>.allocate(capacity: 1))
|
|
|
|
let errorDesc: AutoreleasingUnsafeMutablePointer<NSString?>? = nil
|
|
|
|
if formatter.getObjectValue(value, for: textValue, errorDescription: errorDesc) {
|
|
|
|
row.value = value.pointee as? T
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
guard !textValue.isEmpty else {
|
|
|
|
row.value = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
guard let newValue = T.init(string: textValue) else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
row.value = newValue
|
|
|
|
}
|
|
|
|
|
|
|
|
private func displayValue(useFormatter: Bool) -> String? {
|
|
|
|
guard let v = row.value else { return nil }
|
|
|
|
if let formatter = (row as? FormatterConformance)?.formatter, useFormatter {
|
|
|
|
return textField?.isFirstResponder == true ? formatter.editingString(for: v) : formatter.string(for: v)
|
|
|
|
}
|
|
|
|
return String(describing: v)
|
|
|
|
}
|
|
|
|
|
|
|
|
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
|
|
|
formViewController()?.beginEditing(of: self)
|
|
|
|
if let fieldRowConformance = row as? FormatterConformance, let _ = fieldRowConformance.formatter, fieldRowConformance.useFormatterOnDidBeginEditing ?? fieldRowConformance.useFormatterDuringInput {
|
|
|
|
textField.text = displayValue(useFormatter: true)
|
|
|
|
} else {
|
|
|
|
textField.text = displayValue(useFormatter: false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public func textFieldDidEndEditing(_ textField: UITextField) {
|
|
|
|
formViewController()?.endEditing(of: self)
|
|
|
|
formViewController()?.textInputDidEndEditing(textField, cell: self)
|
|
|
|
textFieldDidChange(textField)
|
|
|
|
textField.text = displayValue(useFormatter: (row as? FormatterConformance)?.formatter != nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class TextFloatLabelCell: _FloatLabelCell<String>, CellType {
|
|
|
|
|
|
|
|
required public init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
public override func setup() {
|
|
|
|
super.setup()
|
|
|
|
textField?.autocorrectionType = .default
|
|
|
|
textField?.autocapitalizationType = .sentences
|
|
|
|
textField?.keyboardType = .default
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: FloatLabelRow
|
|
|
|
open class FloatFieldRow<Cell: CellType>: FormatteableRow<Cell> where Cell: BaseCell, Cell: TextFieldCell {
|
|
|
|
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public final class TextFloatLabelRow: FloatFieldRow<TextFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|