Skip to content

Instantly share code, notes, and snippets.

@jeffleeismyhero
Created June 1, 2015 19:28
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 jeffleeismyhero/c851459fde1d324dedbd to your computer and use it in GitHub Desktop.
Save jeffleeismyhero/c851459fde1d324dedbd to your computer and use it in GitHub Desktop.
@import UIKit;
#import "FORMInputValidator.h"
#import "FORMFormatter.h"
typedef NS_ENUM(NSInteger, VPCounterFormFieldType) {
VPCounterFormFieldTypeDefault = 0,
VPCounterFormFieldTypeCounter,
VPCounterFormFieldTypeUnknown
};
typedef NS_ENUM(NSInteger, VPCounterFormFieldInputType) {
VPCounterFormFieldInputTypeDefault = 0,
VPCounterFormFieldInputTypeCounter,
VPCounterFormFieldInputTypeUnknown
};
@protocol VPCounterFormFieldDelegate;
@interface VPCounterFormField : UITextField
@property (nonatomic, copy) NSString *rawText;
@property (nonatomic) FORMInputValidator *inputValidator;
@property (nonatomic) FORMFormatter *formatter;
@property (nonatomic, copy) NSString *typeString;
@property (nonatomic) VPCounterFormFieldType type;
@property (nonatomic, copy) NSString *inputTypeString;
@property (nonatomic) VPCounterFormFieldInputType inputType;
@property (nonatomic, copy) NSString *info;
@property (nonatomic, getter = isValid) BOOL valid;
@property (nonatomic, getter = isActive) BOOL active;
@property (nonatomic, weak) id <VPCounterFormFieldDelegate> textFieldDelegate;
- (void)setCustomFont:(UIFont *)font UI_APPEARANCE_SELECTOR;
- (void)setBorderWidth:(CGFloat)borderWidth UI_APPEARANCE_SELECTOR;
- (void)setBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setCornerRadius:(CGFloat)cornerRadius UI_APPEARANCE_SELECTOR;
- (void)setActiveBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setActiveBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setInactiveBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setInactiveBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setEnabledBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setEnabledBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setEnabledTextColor:(UIColor *)textColor UI_APPEARANCE_SELECTOR;
- (void)setDisabledBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setDisabledBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setDisabledTextColor:(UIColor *)textColor UI_APPEARANCE_SELECTOR;
- (void)setValidBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setValidBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
- (void)setInvalidBackgroundColor:(UIColor *)backgroundColor UI_APPEARANCE_SELECTOR;
- (void)setInvalidBorderColor:(UIColor *)borderColor UI_APPEARANCE_SELECTOR;
@end
@protocol VPCounterFormFieldDelegate <NSObject>
@optional
- (void)textFormFieldDidBeginEditing:(VPCounterFormField *)textField;
- (void)textFormFieldDidEndEditing:(VPCounterFormField *)textField;
- (void)textFormField:(VPCounterFormField *)textField didUpdateWithText:(NSString *)text;
- (void)textFormFieldDidReturn:(VPCounterFormField *)textField;
@end
#import "VPCounterFormField.h"
#import "VPCounterFormFieldCell.h"
#import "VPCounterFormFieldTypeManager.h"
static const CGFloat VPCounterFormFieldMinusButtonWidth = 30.0f;
static const CGFloat VPCounterFormFieldMinusButtonHeight = 20.0f;
static const CGFloat VPCounterFormFieldPlusButtonWidth = 30.0f;
static const CGFloat VPCounterFormFieldPlusButtonHeight = 20.0f;
static UIColor *activeBackgroundColor;
static UIColor *activeBorderColor;
static UIColor *inactiveBackgroundColor;
static UIColor *inactiveBorderColor;
static UIColor *enabledBackgroundColor;
static UIColor *enabledBorderColor;
static UIColor *enabledTextColor;
static UIColor *disabledBackgroundColor;
static UIColor *disabledBorderColor;
static UIColor *disabledTextColor;
static UIColor *validBackgroundColor;
static UIColor *validBorderColor;
static UIColor *invalidBackgroundColor;
static UIColor *invalidBorderColor;
static BOOL enabledProperty;
@interface VPCounterFormField () <UITextFieldDelegate>
@property (nonatomic, getter = isModified) BOOL modified;
@end
@implementation VPCounterFormField
@synthesize rawText = _rawText;
#pragma mark - Initializers
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) return nil;
self.delegate = self;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addTarget:self action:@selector(textFieldDidUpdate:) forControlEvents:UIControlEventEditingChanged];
[self addTarget:self action:@selector(textFieldDidReturn:) forControlEvents:UIControlEventEditingDidEndOnExit];
self.returnKeyType = UIReturnKeyDone;
NSString *bundlePath = [[[NSBundle bundleForClass:self.class] resourcePath] stringByAppendingPathComponent:@"Images.bundle"];
NSBundle *bundle = [NSBundle bundleWithPath: bundlePath];
UITraitCollection *trait = [UITraitCollection traitCollectionWithDisplayScale:2.0];
UIButton *minusButton = [UIButton buttonWithType:UIButtonTypeCustom];
[minusButton setImage:[UIImage imageNamed:@"minus"
inBundle:bundle
compatibleWithTraitCollection:trait] forState:UIControlStateNormal];
[minusButton addTarget:self action:@selector(minusButtonAction) forControlEvents:UIControlEventTouchUpInside];
minusButton.frame = CGRectMake(0.0f, 0.0f, VPCounterFormFieldMinusButtonWidth, VPCounterFormFieldMinusButtonHeight);
UIButton *plusButton = [UIButton buttonWithType:UIButtonTypeCustom];
[plusButton setImage:[UIImage imageNamed:@"plus"
inBundle:bundle
compatibleWithTraitCollection:trait] forState:UIControlStateNormal];
[plusButton addTarget:self action:@selector(plusButtonAction) forControlEvents:UIControlEventTouchUpInside];
plusButton.frame = CGRectMake(0.0f, 0.0f, VPCounterFormFieldPlusButtonWidth, VPCounterFormFieldPlusButtonHeight);
self.leftView = minusButton;
self.leftViewMode = UITextFieldViewModeAlways;
self.rightView = plusButton;
self.rightViewMode = UITextFieldViewModeAlways;
self.textAlignment = NSTextAlignmentCenter;
return self;
}
#pragma mark - Setters
- (NSRange)currentRange {
NSInteger startOffset = [self offsetFromPosition:self.beginningOfDocument
toPosition:self.selectedTextRange.start];
NSInteger endOffset = [self offsetFromPosition:self.beginningOfDocument
toPosition:self.selectedTextRange.end];
NSRange range = NSMakeRange(startOffset, endOffset-startOffset);
return range;
}
- (void)setText:(NSString *)text {
UITextRange *textRange = self.selectedTextRange;
NSString *newRawText = [self.formatter formatString:text reverse:YES];
NSRange range = [self currentRange];
BOOL didAddText = (newRawText.length > self.rawText.length);
BOOL didFormat = (text.length > super.text.length);
BOOL cursorAtEnd = (newRawText.length == range.location);
if ((didAddText && didFormat) || (didAddText && cursorAtEnd)) {
self.selectedTextRange = textRange;
[super setText:text];
} else {
[super setText:text];
self.selectedTextRange = textRange;
}
}
- (void)setRawText:(NSString *)rawText {
BOOL shouldFormat = (self.formatter && (rawText.length >= _rawText.length ||
![rawText isEqualToString:_rawText]));
if (shouldFormat) {
self.text = [self.formatter formatString:rawText reverse:NO];
} else {
self.text = rawText;
}
_rawText = rawText;
}
- (void)setTypeString:(NSString *)typeString {
_typeString = typeString;
VPCounterFormFieldType type;
if ([typeString isEqualToString:@"counter"]) {
type = VPCounterFormFieldTypeCounter;
} else {
type = VPCounterFormFieldTypeUnknown;
}
self.type = type;
}
- (void)setInputTypeString:(NSString *)inputTypeString {
_inputTypeString = inputTypeString;
VPCounterFormFieldInputType inputType;
if ([inputTypeString isEqualToString:@"counter"]) {
inputType = VPCounterFormFieldInputTypeCounter;
} else {
inputType = VPCounterFormFieldInputTypeUnknown;
}
self.inputType = inputType;
}
- (void)setInputType:(VPCounterFormFieldInputType)inputType {
_inputType = inputType;
VPCounterFormFieldTypeManager *typeManager = [VPCounterFormFieldTypeManager new];
[typeManager setUpType:inputType forTextField:self];
}
#pragma mark - Getters
- (NSString *)rawText {
if (self.formatter) {
return [self.formatter formatString:_rawText reverse:YES];
}
return _rawText;
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(VPCounterFormField *)textField {
if ([self.textFieldDelegate respondsToSelector:@selector(textFormFieldDidBeginEditing:)]) {
[self.textFieldDelegate textFormFieldDidBeginEditing:self];
}
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.active = YES;
self.modified = NO;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
self.active = NO;
if ([self.textFieldDelegate respondsToSelector:@selector(textFormFieldDidEndEditing:)]) {
[self.textFieldDelegate textFormFieldDidEndEditing:self];
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (!string || [string isEqualToString:@"\n"]) return YES;
BOOL validator = (self.inputValidator &&
[self.inputValidator respondsToSelector:@selector(validateReplacementString:withText:withRange:)]);
if (validator) return [self.inputValidator validateReplacementString:string
withText:self.rawText withRange:range];
return YES;
}
#pragma mark - UIResponder Overwritables
- (BOOL)becomeFirstResponder {
if ([self.textFieldDelegate respondsToSelector:@selector(textFormFieldDidBeginEditing:)]) {
[self.textFieldDelegate textFormFieldDidBeginEditing:self];
}
return [super becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
return self.enabled ?: [super canBecomeFirstResponder];
}
#pragma mark - Notifications
- (void)textFieldDidUpdate:(UITextField *)textField {
if (!self.isValid) {
self.valid = YES;
}
self.modified = YES;
self.rawText = self.text;
if ([self.textFieldDelegate respondsToSelector:@selector(textFormField:didUpdateWithText:)]) {
[self.textFieldDelegate textFormField:self
didUpdateWithText:self.rawText];
}
}
- (void)textFieldDidReturn:(UITextField *)textField {
if ([self.textFieldDelegate respondsToSelector:@selector(textFormFieldDidReturn:)]) {
[self.textFieldDelegate textFormFieldDidReturn:self];
}
}
#pragma mark - Actions
- (void)minusButtonAction {
NSNumber *number = [NSNumber numberWithInt:[self.rawText integerValue] - 1];
if ([number integerValue] < 0) {
self.rawText = @"0";
} else {
self.rawText = [number stringValue];
}
if ([self.textFieldDelegate respondsToSelector:@selector(textFormField:didUpdateWithText:)]) {
[self.textFieldDelegate textFormField:self
didUpdateWithText:self.rawText];
}
}
- (void)plusButtonAction {
NSNumber *number = [NSNumber numberWithInt:[self.rawText integerValue] + 1];
self.rawText = [number stringValue];
if ([self.textFieldDelegate respondsToSelector:@selector(textFormField:didUpdateWithText:)]) {
[self.textFieldDelegate textFormField:self
didUpdateWithText:self.rawText];
}
}
#pragma mark - Appearance
- (void)setActive:(BOOL)active {
_active = active;
if (active) {
self.backgroundColor = activeBackgroundColor;
self.layer.borderColor = activeBorderColor.CGColor;
} else {
self.backgroundColor = inactiveBackgroundColor;
self.layer.borderColor = inactiveBorderColor.CGColor;
}
}
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
enabledProperty = enabled;
if (enabled) {
self.backgroundColor = enabledBackgroundColor;
self.layer.borderColor = enabledBorderColor.CGColor;
self.textColor = enabledTextColor;
} else {
self.backgroundColor = disabledBackgroundColor;
self.layer.borderColor = disabledBorderColor.CGColor;
self.textColor = disabledTextColor;
}
}
- (void)setValid:(BOOL)valid {
_valid = valid;
if (!self.isEnabled) return;
if (valid) {
self.backgroundColor = validBackgroundColor;
self.layer.borderColor = validBorderColor.CGColor;
} else {
self.backgroundColor = invalidBackgroundColor;
self.layer.borderColor = invalidBorderColor.CGColor;
}
}
- (void)setCustomFont:(UIFont *)font {
self.font = font;
}
- (void)setBorderWidth:(CGFloat)borderWidth {
self.layer.borderWidth = borderWidth;
}
- (void)setBorderColor:(UIColor *)borderColor {
self.layer.borderColor = borderColor.CGColor;
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
self.layer.cornerRadius = cornerRadius;
}
- (void)setActiveBackgroundColor:(UIColor *)color {
activeBackgroundColor = color;
}
- (void)setActiveBorderColor:(UIColor *)color {
activeBorderColor = color;
}
- (void)setInactiveBackgroundColor:(UIColor *)color {
inactiveBackgroundColor = color;
}
- (void)setInactiveBorderColor:(UIColor *)color {
inactiveBorderColor = color;
}
- (void)setEnabledBackgroundColor:(UIColor *)color {
enabledBackgroundColor = color;
}
- (void)setEnabledBorderColor:(UIColor *)color {
enabledBorderColor = color;
}
- (void)setEnabledTextColor:(UIColor *)color {
enabledTextColor = color;
}
- (void)setDisabledBackgroundColor:(UIColor *)color {
disabledBackgroundColor = color;
}
- (void)setDisabledBorderColor:(UIColor *)color {
disabledBorderColor = color;
}
- (void)setDisabledTextColor:(UIColor *)color {
disabledTextColor = color;
self.enabled = enabledProperty;
}
- (void)setValidBackgroundColor:(UIColor *)color {
validBackgroundColor = color;
}
- (void)setValidBorderColor:(UIColor *)color {
validBorderColor = color;
}
- (void)setInvalidBackgroundColor:(UIColor *)color {
invalidBackgroundColor = color;
}
- (void)setInvalidBorderColor:(UIColor *)color {
invalidBorderColor = color;
self.enabled = enabledProperty;
}
@end
@import UIKit;
#import "FORMBaseFieldCell.h"
static NSString * const VPResignFirstResponderNotification = @"VPResignFirstResponderNotification";
static NSString * const VPCounterFormFieldCellIdentifier = @"VPCounterFormFieldCellIdentifier";
@interface VPCounterFormFieldCell : FORMBaseFieldCell
@end
#import "VPCounterFormFieldCell.h"
#import "VPCounterFormField.h"
@interface VPCounterFormFieldCell () <VPCounterFormFieldDelegate>
@property (nonatomic) VPCounterFormField *textField;
@end
@implementation VPCounterFormFieldCell
#pragma mark - Initializers
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) return nil;
[self.contentView addSubview:self.textField];
if ([self respondsToSelector:@selector(resignFirstResponder)]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(resignFirstResponder)
name:VPResignFirstResponderNotification
object:nil];
}
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cellTapAction)];
[self addGestureRecognizer:tapGestureRecognizer];
return self;
}
- (void)dealloc {
}
#pragma mark - Getters
- (VPCounterFormField *)textField {
if (_textField) return _textField;
_textField = [[VPCounterFormField alloc] initWithFrame:[self textFieldFrame]];
_textField.textFieldDelegate = self;
return _textField;
}
- (CGRect)labelFrameUsingString:(NSString *)string {
NSArray *components = [string componentsSeparatedByString:@"\n"];
CGFloat width;
if (components.count > 1) {
NSString *longestLine;
for (NSString *line in components) {
if (longestLine) {
if (line.length > longestLine.length) {
longestLine = line;
}
} else {
longestLine = line;
}
}
width = 8.0f * longestLine.length;
} else {
width = 8.0f * string.length;
}
CGFloat height = 0.0f;
height += 11.0f * components.count;
return CGRectMake(0, 0, width, height);
}
#pragma mark - VPCounterFormFieldCell
- (void)updateFieldWithDisabled:(BOOL)disabled {
self.textField.enabled = !disabled;
}
- (void)updateWithField:(FORMField *)field {
[super updateWithField:field];
self.textField.hidden = (field.sectionSeparator);
self.textField.inputValidator = [self.field inputValidator];
self.textField.formatter = [self.field formatter];
self.textField.typeString = field.typeString;
self.textField.inputTypeString = field.inputTypeString;
self.textField.enabled = !field.disabled;
self.textField.valid = field.valid;
self.textField.rawText = [self rawTextForField:field];
self.textField.info = field.info;
}
- (void)validate {
BOOL validation = ([self.field validate] == FORMValidationResultTypeValid);
[self.textField setValid:validation];
}
#pragma mark - Private methods
- (NSString *)rawTextForField:(FORMField *)field {
NSString *rawText = field.value;
if (field.value) {
switch (field.type) {
case VPCounterFormFieldInputTypeCounter: {
if ([field.value isKindOfClass:[NSNumber class]]) {
NSNumber *value = field.value;
rawText = [value stringValue];
}
} break;
default: break;
}
}
return rawText;
}
#pragma mark - Actions
- (void)cellTapAction {
}
- (void)focusAction {
[self.textField becomeFirstResponder];
}
#pragma mark - Layout
- (void)layoutSubviews {
[super layoutSubviews];
self.textField.frame = [self textFieldFrame];
}
- (CGRect)textFieldFrame {
CGFloat marginX = FORMTextFieldCellMarginX;
CGFloat marginTop = FORMFieldCellMarginTop;
CGFloat marginBotton = FORMFieldCellMarginBottom;
CGFloat width = CGRectGetWidth(self.frame) - (marginX * 2);
CGFloat height = CGRectGetHeight(self.frame) - marginTop - marginBotton;
CGRect frame = CGRectMake(marginX, marginTop, width, height);
return frame;
}
#pragma mark - VPCounterFormFieldDelegate
- (void)textFormFieldDidBeginEditing:(VPCounterFormField *)textField {
}
- (void)textFormFieldDidEndEditing:(VPCounterFormField *)textField {
[self validate];
if (!self.textField.valid) {
[self.textField setValid:[self.field validate]];
}
}
- (void)textFormField:(VPCounterFormField *)textField didUpdateWithText:(NSString *)text {
self.field.value = text;
[self validate];
if (!self.textField.valid) {
[self.textField setValid:[self.field validate]];
}
if ([self.delegate respondsToSelector:@selector(fieldCell:updatedWithField:)]) {
[self.delegate fieldCell:self
updatedWithField:self.field];
}
}
#pragma mark - Private headers
- (BOOL)resignFirstResponder {
[self.textField resignFirstResponder];
return [super resignFirstResponder];
}
- (BOOL)becomeFirstResponder {
[self.textField becomeFirstResponder];
return [super becomeFirstResponder];
}
@end
@import Foundation;
@import UIKit;
#import "VPCounterFormField.h"
@interface VPCounterFormFieldTypeManager : NSObject
- (void)setUpType:(VPCounterFormFieldInputType)type forTextField:(UITextField *)textField;
@end
#import "VPCounterFormFieldTypeManager.h"
@implementation VPCounterFormFieldTypeManager
- (void)setUpType:(VPCounterFormFieldInputType)type forTextField:(UITextField *)textField {
switch (type) {
case VPCounterFormFieldInputTypeDefault : [self setupDefaultTextField:textField]; break;
case VPCounterFormFieldInputTypeCounter : [self setupCounterTextField:textField]; break;
case VPCounterFormFieldInputTypeUnknown : abort();
}
}
#pragma mark - VPCounterFormFieldType
- (void)setupDefaultTextField:(UITextField *)textField {
textField.autocapitalizationType = UITextAutocapitalizationTypeSentences;
textField.autocorrectionType = UITextAutocorrectionTypeDefault;
textField.keyboardType = UIKeyboardTypeDefault;
textField.secureTextEntry = NO;
}
- (void)setupCounterTextField:(UITextField *)textField {
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.autocorrectionType = UITextAutocorrectionTypeNo;
textField.keyboardType = UIKeyboardTypeNumberPad;
textField.secureTextEntry = NO;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment