Last active
May 12, 2016 18:44
-
-
Save alexfu/461e7fd396dac2abb95fd31f9d4f1653 to your computer and use it in GitHub Desktop.
A UITextField with error text support. Draws error text underneath UITextField.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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