Skip to content

Instantly share code, notes, and snippets.

@alexfu
Last active May 12, 2016 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexfu/461e7fd396dac2abb95fd31f9d4f1653 to your computer and use it in GitHub Desktop.
Save alexfu/461e7fd396dac2abb95fd31f9d4f1653 to your computer and use it in GitHub Desktop.
A UITextField with error text support. Draws error text underneath UITextField.
import Foundation
import UIKit
class UIErrorTextField : UITextField {
private let bgView = UIView()
private var textFieldHeight = CGFloat(40)
private var errorTextHeight = CGFloat(12)
private let errorTextPadding = CGFloat(2)
var errorFont = UIFont.systemFontOfSize(12) {
didSet {
invalidateErrorTextContentSize()
}
}
var errorText: String? {
didSet {
setNeedsDisplay()
}
}
var errorTextColor = UIColor.blackColor() {
didSet {
setNeedsDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
private func setup() {
clipsToBounds = true
borderStyle = .None
let errorTextBounds = measureText("TEST", attributes: [NSFontAttributeName: errorFont])
errorTextHeight = errorTextBounds.height
setupBackgroundView()
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: self.frame.width, height: calculateHeight())
}
override func editingRectForBounds(bounds: CGRect) -> CGRect {
return offsetRectForEditing(super.editingRectForBounds(bounds))
}
override func textRectForBounds(bounds: CGRect) -> CGRect {
return offsetRectForEditing(super.textRectForBounds(bounds))
}
override func leftViewRectForBounds(bounds: CGRect) -> CGRect {
return offsetRectForEdgeView(super.leftViewRectForBounds(bounds))
}
override func rightViewRectForBounds(bounds: CGRect) -> CGRect {
return offsetRectForEdgeView(super.rightViewRectForBounds(bounds))
}
override func drawRect(rect: CGRect) {
bgView.frame.size.width = rect.width
bgView.frame.size.height = textFieldHeight
drawErrorText(rect)
}
private func invalidateErrorTextContentSize() {
guard let text = errorText else { return }
let bounds = measureText(text, attributes: [NSFontAttributeName: errorFont])
errorTextHeight = bounds.height
invalidateIntrinsicContentSize()
}
private func drawErrorText(rect: CGRect) {
guard let text = errorText else { return }
let errorTextBounds = CGRect(x: bgView.frame.origin.x,
y: bgView.frame.height + errorTextPadding,
width: bgView.frame.width,
height: errorTextHeight)
text.drawInRect(errorTextBounds,
withAttributes: [
NSFontAttributeName: errorFont,
NSForegroundColorAttributeName: errorTextColor
])
}
private func offsetRectForEditing(rect: CGRect) -> CGRect {
return UIEdgeInsetsInsetRect(rect, UIEdgeInsets(top: 0, left: 5, bottom: errorTextHeight, right: 5))
}
private func offsetRectForEdgeView(rect: CGRect) -> CGRect {
let x = CGFloat(5)
let y = (bgView.frame.height/2) - (rect.height/2)
return CGRect(x: x, y: y, width: rect.width, height: rect.height)
}
private func setupBackgroundView() {
bgView.layer.cornerRadius = 5
bgView.layer.borderWidth = 1
bgView.layer.borderColor = UIColor.blackColor().colorWithAlphaComponent(0.1).CGColor
bgView.backgroundColor = UIColor.clearColor()
bgView.userInteractionEnabled = false // Let touch events fall through
addSubview(bgView)
}
private func measureText(text: String, attributes: [String:AnyObject]) -> CGRect {
let nsString = NSString(string: text)
return nsString.boundingRectWithSize(self.frame.size,
options: .UsesLineFragmentOrigin,
attributes: attributes,
context: nil)
}
private func calculateHeight() -> CGFloat {
return errorTextHeight + textFieldHeight + errorTextPadding
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment