rootstrap/RSFormView

View on GitHub
RSFormView/Classes/Views/TextField/TextFieldViewPrivateExtension.swift

Summary

Maintainability
A
0 mins
Test Coverage
//
//  TextFieldViewPrivateExtension.swift
//  RSFormView
//
//  Created by Germán Stábile on 5/13/19.
//

import Foundation

internal extension TextFieldView {
  func configureViews() {
    actualView = addNibView(inBundle: Constants.formViewBundle)
    
    textField.delegate = self
    textField.font = formConfigurator.textFont
    textField.textColor = formConfigurator.textColor
    
    titleLabel.font = formConfigurator.titleFont
    titleLabel.textColor = formConfigurator.textColor
    
    errorLabel.font = formConfigurator.errorFont
    errorLabel.textColor = formConfigurator.errorTextColor

    setContraints() 
    
    actualView?.sendSubviewToBack(textFieldContainerView)
    
    let tapGesture = UITapGestureRecognizer(target: self,
                                            action: #selector(tappedView))
    addGestureRecognizer(tapGesture)
    updateErrorState()
  }
  
  func saveCursorPosition() {
    if let selectedRange = textField.selectedTextRange {
      lastCursorPosition = textField.offset(from: textField.beginningOfDocument,
                                            to: selectedRange.start)
    }
  }
  
  func setCursorPosition(jump: Int) {
    guard let lastCursorPosition = lastCursorPosition,
      fieldData?.fieldType != .usPhone,
      fieldData?.fieldType != .expiration,
      textField.selectedTextRange != nil else {
        self.lastCursorPosition = nil
        return
    }
    
    if let newPosition = textField.position(from: textField.beginningOfDocument,
                                            offset: lastCursorPosition + jump) {
      textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
    }
    
    self.lastCursorPosition = nil
  }
  
  func titleValidColor() -> UIColor {
    return textField.isFirstResponder ? formConfigurator.editingTitleColor : formConfigurator.validTitleColor
  }
  
  func bottomLineValidColor() -> UIColor {
    return textField.isFirstResponder ?
      formConfigurator.editingLineColor : formConfigurator.validLineColor
  }
  
  func borderLineValidColor() -> UIColor {
    return textField.isFirstResponder ?
      formConfigurator.editingBorderColor : formConfigurator.validBorderColor
  }
  
  func setKeyboardType() {
    guard let fieldData = fieldData else { return }
    switch fieldData.fieldType {
    case .integer, .usPhone, .fiveDigitZipCode, .expiration:
      textField.keyboardType = .numberPad
    case .double:
        textField.keyboardType = .decimalPad
    case .email:
      textField.keyboardType = .emailAddress
    default:
      textField.keyboardType = .default
    }
  }
  
  func configureFormPicker() {
    guard let fieldData = fieldData,
      fieldData.fieldType == .date ||
      fieldData.fieldType == .picker else {
        textField.inputView = nil
        return
    }
    if fieldData.fieldType == .date {
      setDatePicker(with: fieldData)
    } else {
      setGeneralPicker(with: fieldData)
    }
  }
  
  func setDatePicker(with fieldData: FormField) {
    let datePicker = UIDatePicker()
    if fieldData.value != "" {
      let dateFormatter = DateFormatter()
      dateFormatter.dateFormat = TextFieldView.dateFormat
      if let date = dateFormatter.date(from: fieldData.value) {
        datePicker.date = date
      }
    }
    datePicker.datePickerMode = .date
    datePicker.minimumDate = fieldData.minimumDate
    datePicker.maximumDate = fieldData.maximumDate
    datePicker.addTarget(self,
                         action: #selector(datePickerChangedValue),
                         for: .valueChanged)
    textField.inputView = datePicker
  }
  
  func setGeneralPicker(with fieldData: FormField) {
    let picker = UIPickerView()
    
    if let options = fieldData.options,
      let index = options.firstIndex(of: fieldData.value) {
      picker.selectRow(index, inComponent: 0, animated: false)
    }
    
    picker.delegate = self
    textField.inputView = picker
  }
  
  func processTextUpdates(with text: String, updatedText: String, data: FormField) {
    var updatedText = updatedText
    if data.fieldType == .usPhone &&
      textField.text?.count ?? 0 < updatedText.count &&
      [3, 7].contains(updatedText.count) {
      updatedText += "-"
    }
    
    if data.fieldType == .expiration {
      updatedText = processExpirationDate(previousText: text,
                                          updatedText: updatedText)
    }
    
    propagateUpdates(previousText: text, updatedText: updatedText, data: data)
  }
  
  func propagateUpdates(previousText: String, updatedText: String, data: FormField) {
    guard previousText != updatedText else { return }
    var updatedText = updatedText
    updatedText = data.capitalizeValue ? updatedText.capitalized : updatedText
    updatedText = data.uppercaseValue ? updatedText.uppercased() : updatedText
    if data.fieldType == .usState {
      updatedText = updatedText.count > 2 ? updatedText.capitalized : updatedText.uppercased()
    }
    let jump = updatedText.count - previousText.count
    data.oneTimeErrorMessage = nil
    data.value = updatedText
    data.shouldDisplayError = true
    
    validate(with: updatedText)
    saveCursorPosition()
    delegate?.didUpdate(textFieldView: self, with: data)
    setCursorPosition(jump: jump)
  }
  
  func processExpirationDate(previousText: String, updatedText: String) -> String {
    if updatedText.count < previousText.count {
      //if deleting we don't need to do any manipulation
      return updatedText
    }
    
    if updatedText.count > 7 {
      //if we have more characters than the format mm/yyyy, don't update the text
      return previousText
    }
    
    var resultingText = ""
    switch previousText.count {
    case 0:
      resultingText = processExpirationMonthFirstCharacter(updatedText: updatedText)
    case 1:
      resultingText = processExpirationMonth(previousText: previousText,
                                             updatedText: updatedText)
    default:
      resultingText = updatedText
    }
    
    return resultingText
  }
  
  func processExpirationMonthFirstCharacter(updatedText: String) -> String {
    //if first character is bigger than 1 put zero at the begginning
    //since its typing a month
    if Int(updatedText) ?? 0 <= 1 {
      return updatedText
    } else {
      return "0\(updatedText)/"
    }
  }
  
  func processExpirationMonth(previousText: String, updatedText: String) -> String {
    if Int(updatedText) ?? 0 > 12 {
      return previousText
    } else {
      return "\(updatedText)/"
    }
  }
  
  func update(withPickerText pickerText: String) {
    guard let data = fieldData else { return }
    textField.text = pickerText
    data.value = pickerText
    delegate?.didUpdate(textFieldView: self, with: data)
  }
}