|
|
|
// 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: 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<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: 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<Int>, 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<String>, 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<String>, 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<String>, 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<String>, 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<Float>, 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<URL>, 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<String>, 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<String>, 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<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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class IntFloatLabelRow: FloatFieldRow<IntFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class DecimalFloatLabelRow: FloatFieldRow<DecimalFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class URLFloatLabelRow: FloatFieldRow<URLFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class TwitterFloatLabelRow: FloatFieldRow<TwitterFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class AccountFloatLabelRow: FloatFieldRow<AccountFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class PasswordFloatLabelRow: FloatFieldRow<PasswordFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class NameFloatLabelRow: FloatFieldRow<NameFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public final class EmailFloatLabelRow: FloatFieldRow<EmailFloatLabelCell>, RowType {
|
|
|
|
public required init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public final class ImageCheckRow<T: Equatable>: Row<ImageCheckCell<T>>, SelectableRowType, RowType {
|
|
|
|
public var selectableValue: T?
|
|
|
|
required public init(tag: String?) {
|
|
|
|
super.init(tag: tag)
|
|
|
|
displayValueFor = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public class ImageCheckCell<T: Equatable> : Cell<T>, 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()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|