Skip to content

Instantly share code, notes, and snippets.

@mahabaleshwarhnr
Created September 3, 2018 07:59
Show Gist options
  • Save mahabaleshwarhnr/8675915a0a74f0f5f492a766146a5a4a to your computer and use it in GitHub Desktop.
Save mahabaleshwarhnr/8675915a0a74f0f5f492a766146a5a4a to your computer and use it in GitHub Desktop.
//
// ICTextField.swift
//
//
// Created by Mahabaleshwar Hegde on 26/04/18.
// Copyright © 2018 Mahabaleshwar. All rights reserved.
//
import UIKit
@objc
protocol UIDatePickerDelegate: UITextFieldDelegate {
@objc optional func datePickerValueChanged(date: Date, textField: ICTextField)
}
@objc
protocol UIDropDownDelegate: UITextFieldDelegate {
@objc optional func datePickerDidSelect(row: Int)
}
@IBDesignable
final class ICTextField: UITextField {
private var pickerView: UIPickerView?
public var textFieldIdentifier: TextFieldIdentifier = .default
/// Textfield leftView or rightViewMode
///
/// - left: leftView Mode enabled
/// - right: rightView Mode enabled
public enum ViewModePostion {
case left
case right
}
/// Date picker content dispaly type.
///
/// - time: Displays hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. 6 | 53 | PM)
/// - `date`: Displays month, day, and year depending on the locale setting (e.g. November | 15 | 2007)
/// - dateAndTime: Displays date, hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. Wed Nov 15 | 6 | 53 | PM)
/// - countDownTimer: Displays hour and minute (e.g. 1 | 53)
public enum DatePickerMode: Int {
case time = 1
case date
case dateAndTime
case countDownTimer
}
/// To show the date or time input from textfield set this value to true
/// Default is false.
@IBInspectable open var isDatePicker: Bool = false {
didSet {
if self.isDatePicker {
self.inputView = self.datePicker
self.inputAccessoryView = self.configureToolBar()
}
self.reloadInputViews()
}
}
/// Legtview image name
@IBInspectable open var leftDrawable: UIImage? {
didSet {
guard let _ = self.leftDrawable else {
self.rightViewMode = .never
return
}
self.updateDrwableViewMode(forPostion: .left)
}
}
/// Right view image name
@IBInspectable open var rightDrawable: UIImage? {
didSet {
guard let _ = self.rightDrawable else {
self.rightViewMode = .never
return
}
self.updateDrwableViewMode(forPostion: .right)
}
}
/// Textfield placeholder color
@IBInspectable open var placeholderColor: UIColor? = UIColor.white.withAlphaComponent(0.5) {
didSet {
guard let placeholder = self.placeholder, let color = self.placeholderColor else { return }
let attributes = [NSAttributedStringKey.foregroundColor: color]
self.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: attributes)
}
}
/// Shows the inputview as UITableView. able to select multiple option, default is false
@IBInspectable open var isDropDown: Bool = false {
didSet {
if isDropDown {
self.pickerView = UIPickerView()
self.pickerView?.dataSource = self
self.pickerView?.delegate = self
self.inputAccessoryView = self.configureToolBar()
self.inputView = self.pickerView
self.reloadInputViews()
} else {
self.inputView = nil
self.reloadInputViews()
}
}
}
// MARK: - Public Properties
let datePicker: UIDatePicker = {
let dateDropDown = UIDatePicker()
dateDropDown.datePickerMode = .date
dateDropDown.locale = Locale(identifier: "en_US_POSIX")
dateDropDown.addTarget(self, action: #selector(ICTextField.datePickerValueChanged(sender:)), for: .valueChanged)
return dateDropDown
}()
/// Datepicker mode
var datePickerMode: DatePickerMode = .date {
didSet {
var mode: UIDatePickerMode = .date
switch self.datePickerMode {
case .date:
mode = .date
case .countDownTimer:
mode = .countDownTimer
case .dateAndTime:
mode = .dateAndTime
case .time:
mode = .time
}
self.datePicker.datePickerMode = mode
}
}
var dropDownSource: [String] = [] {
didSet {
self.pickerView?.reloadAllComponents()
}
}
/// Tool bar to show cancel and done on top of textfield.
open var accessoryToolBar: UIToolbar? {
didSet {
self.inputAccessoryView = accessoryToolBar
self.reloadInputViews()
}
}
override var keyboardType: UIKeyboardType {
didSet {
if self.keyboardType == .numberPad || self.keyboardType == .decimalPad {
self.inputAccessoryView = self.configureToolBar()
self.reloadInputViews()
}
}
}
private(set) var selectedDropDownItem: (row: Int, item: String)!
// MARK: - private Properties
private var datePickerDelegate: UIDatePickerDelegate? {
return self.delegate as? UIDatePickerDelegate
}
private var dropDownDelegate: UIDropDownDelegate? {
return self.delegate as? UIDropDownDelegate
}
private var selectedDate: Date = Date()
// MARK: - override Methods
override func awakeFromNib() {
super.awakeFromNib()
}
@objc private func datePickerValueChanged(sender: UIDatePicker) {
self.selectedDate = sender.date
self.datePickerDelegate?.datePickerValueChanged?(date: sender.date, textField: self)
}
private func configureToolBar() -> UIToolbar {
let frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 40.0)
let toolBar = UIToolbar(frame: frame)
toolBar.barStyle = .default
toolBar.barTintColor = UIColor(hex: "4db8ff")
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(donePressed))
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(cancelPressed))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
@objc fileprivate func donePressed() {
if self.isDatePicker {
self.datePickerDelegate?.datePickerValueChanged?(date: self.selectedDate, textField: self)
} else if self.isDropDown {
guard let row = self.pickerView?.selectedRow(inComponent: 0) else {
self.resignFirstResponder()
return
}
self.setSelectedItem(row: row)
}
self.resignFirstResponder()
}
@objc private func cancelPressed() {
if self.isDatePicker {
self.text = nil
}
self.text = ""
self.selectedDropDownItem = nil
self.resignFirstResponder()
}
// MARK: - Private Functions
private func updateDrwableViewMode(forPostion postion: ViewModePostion) {
switch postion {
case .left:
guard let image = self.leftDrawable else { return }
self.leftViewMode = .always
let leftView = UIImageView(frame: CGRect(x: 0.0, y: 0.0, width: self.bounds.height - 8.0, height: self.bounds.height - 16.0))
leftView.image = image
leftView.contentMode = .scaleAspectFill
leftView.clipsToBounds = true
self.leftView = leftView
self.layoutIfNeeded()
case .right:
guard let image = self.rightDrawable else { return }
self.rightViewMode = .always
let rightView = UIImageView(frame: CGRect(x: 0.0, y: 8.0, width: 12.0, height: 12.0))
rightView.image = image
rightView.contentMode = .scaleAspectFill
rightView.clipsToBounds = true
self.rightView = rightView
self.layoutIfNeeded()
}
}
}
// MARK: - UIPickerViewDataSource
extension ICTextField: UIPickerViewDataSource {
func numberOfComponents(in _: UIPickerView) -> Int {
return 1
}
func pickerView(_: UIPickerView, numberOfRowsInComponent _: Int) -> Int {
return self.dropDownSource.count
}
}
// MARK: - UIPickerViewDelegate
extension ICTextField: UIPickerViewDelegate {
func pickerView(_: UIPickerView, titleForRow row: Int, forComponent _: Int) -> String? {
guard self.dropDownSource.indices.contains(row) else {
return nil
}
return self.dropDownSource[row]
}
func pickerView(_: UIPickerView, didSelectRow row: Int, inComponent _: Int) {
self.setSelectedItem(row: row)
self.dropDownDelegate?.datePickerDidSelect?(row: row)
}
private func setSelectedItem(row: Int) {
guard self.dropDownSource.indices.contains(row) else {
self.text = nil
self.selectedDropDownItem = nil
return
}
let item = self.dropDownSource[row]
self.text = item
self.selectedDropDownItem = (row: row, item: item)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment