Skip to content

Instantly share code, notes, and snippets.

@alanf
Created September 29, 2017 18:17
Show Gist options
  • Save alanf/4a6fda27a9cc5c177e492b71c4d0d99c to your computer and use it in GitHub Desktop.
Save alanf/4a6fda27a9cc5c177e492b71c4d0d99c to your computer and use it in GitHub Desktop.
//
// UnderlinedTextField.swift
// Halo
//
// Created by Alan Fineberg on 7/25/17.
//
//
import Foundation
import ReactiveKit
import UIKit
class UnderlinedTextField: UITextField {
let placeholderLabel = UILabel()
@IBOutlet weak var underlineView: UIView!
var animateOpenControlEvent: UIControlEvents {
return .editingDidBegin
}
var animateClosedControlEvent: UIControlEvents? {
return .editingDidEnd
}
override func awakeFromNib() {
super.awakeFromNib()
textColor = Sport.Color.LightGrey
clipsToBounds = false
setupPlaceholderLabel()
setupUnderline()
}
override internal var text: String? {
willSet {
if !open, text?.isEmpty == true, newValue?.isEmpty == false {
placeholderLabel.transform = placeholderLabel.transform.scaledBy(x: scaleFactor, y: scaleFactor).translatedBy(x: 0.0, y: -self.frame.size.height / (scaleFactor * 2.0))
placeholderLabel.textColor = Sport.Color.Green
textColor = Sport.Color.Green
open = true
}
}
}
private var open = false
private func animateOpen(originalTransform: CGAffineTransform, scaleFactor: CGFloat) {
guard !open else { return }
placeholder = nil
UIView.animate(withDuration: 0.30, delay: 0.0, options: .curveEaseInOut, animations: { [weak self] in
guard let `self` = self else { return }
self.open = true
if self.text?.isEmpty == true {
self.placeholderLabel.transform = originalTransform.scaledBy(x: scaleFactor, y: scaleFactor).translatedBy(x: 0.0, y: -self.frame.size.height / (scaleFactor * 2.0))
}
self.placeholderLabel.textColor = Sport.Color.Green
}, completion: { [weak self] _ in
self?.textColor = Sport.Color.Green
})
}
private func animateClosed(originalTransform: CGAffineTransform, scaleFactor: CGFloat, originalPlaceholder: String?) {
guard open else { return }
placeholder = nil
UIView.animate(withDuration: 0.30, delay: 0.0, options: .curveEaseInOut, animations: { [weak self] in
guard let `self` = self else { return }
if self.text?.isEmpty == true {
self.placeholderLabel.transform = originalTransform
}
self.placeholderLabel.textColor = Sport.Color.LightGrey
}, completion: { [weak self] _ in
self?.textColor = Sport.Color.LightGrey
self?.placeholder = originalPlaceholder
self?.open = false
})
}
let scaleFactor: CGFloat = 0.4
private func setupPlaceholderLabel() {
addSubview(placeholderLabel)
placeholderLabel.anchorPoint = CGPoint.zero
placeholderLabel.text = placeholder
placeholderLabel.textColor = Sport.Color.LightGrey
placeholderLabel.font = font
placeholderLabel.frame = frame
let originalPlaceholder = placeholder
placeholder = nil
let originalTransform = placeholderLabel.transform
reactive.controlEvents(animateOpenControlEvent).observeNext { [weak self] _ in
guard let `self` = self else { return }
self.animateOpen(originalTransform: originalTransform, scaleFactor: self.scaleFactor)
}.dispose(in: reactive.bag)
if let animatedClosedControlEvent = animateClosedControlEvent {
reactive.controlEvents(animatedClosedControlEvent).observeNext { [weak self] _ in
guard let `self` = self else { return }
self.animateClosed(originalTransform: originalTransform, scaleFactor: self.scaleFactor, originalPlaceholder: originalPlaceholder)
}.dispose(in: reactive.bag)
}
}
private func setupUnderline() {
underlineView.backgroundColor = Sport.Color.LightGrey
reactive.controlEvents(animateOpenControlEvent).map { _ in
return Sport.Color.Green
}.bind(to: underlineView.reactive.backgroundColor).dispose(in: reactive.bag)
if let animatedClosedControlEvent = animateClosedControlEvent {
reactive.controlEvents(animatedClosedControlEvent).map { _ in
return Sport.Color.LightGrey
}.bind(to: underlineView.reactive.backgroundColor).dispose(in: reactive.bag)
}
}
}
/// A text field that doesn't actually allow editing to occur, but still wants to be highlighted on tap.
class UneditableUnderlinedTextField: UnderlinedTextField {
override var animateOpenControlEvent: UIControlEvents {
return .touchDown
}
override var animateClosedControlEvent: UIControlEvents? {
return nil
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment