// Copyright SIX DAY LLC. All rights reserved. import Foundation import UIKit import Eureka // MARK: FloatLabelCell public class _FloatLabelCell: Cell, UITextFieldDelegate, TextFieldCell where T: Equatable, T: InputTypeInitiable { public var textField: UITextField! { return floatLabelTextField } required public init(style: UITableViewCellStyle, 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 = AutoreleasingUnsafeMutablePointer.init(UnsafeMutablePointer.allocate(capacity: 1)) let errorDesc: AutoreleasingUnsafeMutablePointer? = 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 = AutoreleasingUnsafeMutablePointer.init(UnsafeMutablePointer.allocate(capacity: 1)) let errorDesc: AutoreleasingUnsafeMutablePointer? = 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, CellType { required public init(style: UITableViewCellStyle, 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 } } public class IntFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .none textField?.keyboardType = .numberPad } } public class PhoneFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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?.keyboardType = .phonePad } } public class NameFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .no textField.autocapitalizationType = .words textField.keyboardType = .asciiCapable } } public class EmailFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .no textField?.autocapitalizationType = .none textField?.keyboardType = .emailAddress } } public class PasswordFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .no textField?.autocapitalizationType = .none textField?.keyboardType = .asciiCapable textField?.isSecureTextEntry = true } } public class DecimalFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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?.keyboardType = .decimalPad } } public class URLFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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?.keyboardType = .URL } } public class TwitterFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .no textField?.autocapitalizationType = .none textField?.keyboardType = .twitter } } public class AccountFloatLabelCell: _FloatLabelCell, CellType { required public init(style: UITableViewCellStyle, 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 = .no textField?.autocapitalizationType = .none textField?.keyboardType = .asciiCapable } } // MARK: FloatLabelRow open class FloatFieldRow: FormatteableRow where Cell: BaseCell, Cell: TextFieldCell { public required init(tag: String?) { super.init(tag: tag) } } public final class TextFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class IntFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class DecimalFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class URLFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class TwitterFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class AccountFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class PasswordFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class NameFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class EmailFloatLabelRow: FloatFieldRow, RowType { public required init(tag: String?) { super.init(tag: tag) } } public final class ImageCheckRow: Row>, SelectableRowType, RowType { public var selectableValue: T? required public init(tag: String?) { super.init(tag: tag) displayValueFor = nil } } public class ImageCheckCell : Cell, CellType { required public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// Image for selected state lazy public var trueImage: UIImage = { return UIImage(named: "selected")! }() /// Image for unselected state lazy public var falseImage: UIImage = { return UIImage(named: "unselected")! }() public override func update() { super.update() checkImageView?.image = row.value != nil ? trueImage : falseImage checkImageView?.sizeToFit() } /// Image view to render images. If `accessoryType` is set to `checkmark` /// will create a new `UIImageView` and set it as `accessoryView`. /// Otherwise returns `self.imageView`. open var checkImageView: UIImageView? { guard accessoryType == .checkmark else { return self.imageView } guard let accessoryView = accessoryView else { let imageView = UIImageView() self.accessoryView = imageView return imageView } return accessoryView as? UIImageView } public override func setup() { super.setup() accessoryType = .none } public override func didSelect() { row.reload() row.select() row.deselect() } }