Last active
February 24, 2019 15:48
-
-
Save soxjke/4f5a29f2e411140d3811bfd577edf130 to your computer and use it in GitHub Desktop.
PINCodeField
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
@interface PINCodeView : UIView | |
- (instancetype)initWithNumberOfDigits:(NSUInteger)digits | |
keyboardType:(UIKeyboardType)keyboardType | |
autocapitalizationType:(UITextAutocapitalizationType)autocapitalizationType; | |
@property (nonatomic, copy) NSString *code; | |
- (void)becameFirstResponder; | |
@end |
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 "PINCodeView.h" | |
#import "PINTextField.h" | |
@interface PINCodeView() <PINTextFieldDelegate> | |
@property (nonatomic, copy) NSArray<PINTextField *> *textFields; | |
@end | |
@implementation PINCodeView | |
@synthesize code = _code; | |
- (instancetype)initWithNumberOfDigits:(NSUInteger)digits | |
keyboardType:(UIKeyboardType)keyboardType | |
autocapitalizationType:(UITextAutocapitalizationType)autocapitalizationType { | |
self = [super initWithFrame:CGRectZero]; | |
if (self) { | |
_code = @""; | |
NSMutableArray *array = [NSMutableArray new]; | |
for(NSUInteger i = 0; i < digits; i++) { | |
PINTextField *textField = [PINTextField new]; | |
textField.keyboardType = keyboardType; | |
textField.autocapitalizationType = autocapitalizationType; | |
textField.keyboardAppearance = UIKeyboardAppearanceDark; | |
textField.delegate = self; | |
[array addObject:textField]; | |
[self addSubview:textField]; | |
} | |
self.textFields = array; | |
} | |
return self; | |
} | |
- (void)layoutSubviews { | |
[super layoutSubviews]; | |
NSUInteger count = self.textFields.count; | |
NSUInteger spaces = count - 1; | |
CGFloat totalWidth = self.frame.size.width; | |
CGFloat totalHeight = self.frame.size.height; | |
CGFloat viewWidth = totalWidth / (count + spaces * 0.25); | |
CGFloat spaceWidth = (totalWidth - (count * viewWidth)) / spaces; | |
[self.textFields eachWithIndex:^(PINTextField *field, NSUInteger index) { | |
field.frame = CGRectMake(index * (viewWidth + spaceWidth), 0, viewWidth, totalHeight); | |
}]; | |
} | |
- (void)becameFirstResponder { | |
self.code = [self.code substringToIndex:0]; | |
[self.textFields[0] becomeFirstResponder]; | |
} | |
#pragma mark - UITextFieldDelegate | |
- (void)textFieldDidDelete:(PINTextField *)textField { | |
NSUInteger index = [self.textFields indexOfObject:textField]; | |
if (index > 0) { | |
if (index <= self.code.length) { | |
[self.code substringToIndex:index - 1]; | |
} | |
[self.textFields[index - 1] becomeFirstResponder]; | |
} | |
} | |
- (void)textField:(PINTextField *)textField didInsert:(NSString *)text { | |
NSUInteger index = [self.textFields indexOfObject:textField]; | |
if (index < self.code.length) { | |
self.code = [[self.code substringToIndex:index] stringByAppendingString:text]; | |
} | |
else { | |
self.code = [self.code stringByAppendingString:text]; | |
} | |
if (index < self.textFields.count - 1) { | |
[self.textFields[index + 1] becomeFirstResponder]; | |
} | |
else { | |
[textField resignFirstResponder]; | |
} | |
} | |
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { | |
NSUInteger index = [self.textFields indexOfObject:(PINTextField *)textField]; | |
if (index > self.code.length) { | |
@weakify(self); | |
dispatch_async(dispatch_get_main_queue(), ^{ | |
@strongify(self); | |
if (self.code.length < self.textFields.count) { | |
[self.textFields[self.code.length] becomeFirstResponder]; | |
} | |
}); | |
return NO; | |
} else if (index != self.code.length) { | |
self.code = [self.code substringToIndex:index]; | |
} | |
return YES; | |
} | |
- (void)textFieldDidBeginEditing:(UITextField *)textField { | |
textField.text = @""; | |
_code = [self collectCode]; | |
} | |
- (void)setCode:(NSString *)code { | |
_code = code; | |
[self propagateCode:code]; | |
} | |
- (NSString *)collectCode { | |
return [[self.textFields.rac_sequence map:^NSString *(PINTextField *textField) { | |
return textField.text ?: @""; | |
}] foldLeftWithStart:@"" reduce:^NSString *(NSString *accumulator, NSString *value) { | |
return [accumulator stringByAppendingString:value]; | |
}]; | |
} | |
- (void)propagateCode:(NSString *)code { | |
NSMutableArray *placeholderStrings = [[self.textFields map:^id(id _) { | |
return @""; | |
}] mutableCopy]; | |
[placeholderStrings insertObjects:code.rac_sequence.array | |
atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, code.length)]]; | |
[placeholderStrings removeObjectsInRange:NSMakeRange(self.textFields.count, | |
placeholderStrings.count - self.textFields.count)]; | |
NSArray *tuples = [[placeholderStrings.rac_sequence zipWith:self.textFields.rac_sequence] array]; | |
[tuples each:^(RACTuple *tuple) { | |
RACTupleUnpack(NSString *text, PINTextField *textField) = tuple; | |
textField.text = text; | |
}]; | |
} | |
@end |
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
@class PINTextField; | |
@protocol PINTextFieldDelegate <UITextFieldDelegate> | |
- (void)textFieldDidDelete:(PINTextField *)textField; | |
- (void)textField:(PINTextField *)textField didInsert:(NSString *)text; | |
@end | |
@interface PINTextField : UITextField | |
@property (nonatomic, weak) id<PINTextFieldDelegate> delegate; | |
@end |
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 "PINTextField.h" | |
#import "UIColor+Additions.h" | |
#import "UIFont+Additions.h" | |
@implementation PINTextField | |
@dynamic delegate; | |
- (instancetype)initWithFrame:(CGRect)frame { | |
self = [super initWithFrame:frame]; | |
if (self) { | |
self.autocorrectionType = UITextAutocorrectionTypeNo; | |
self.tintColor = UIColor.nl_tintColor; | |
self.font = [UIFont systemFontOfSize:24 weight:UIFontWeightMedium]; | |
self.textColor = UIColor.nl_titleColor; | |
self.textAlignment = NSTextAlignmentCenter; | |
self.layer.cornerRadius = 2; | |
self.backgroundColor = [UIColor.nl_highlightedBackgroundColor colorWithAlphaComponent:0.8]; | |
self.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; | |
self.textAlignment = NSTextAlignmentCenter; | |
NSDictionary *attributes = @{ | |
NSForegroundColorAttributeName: [UIColor.nl_titleColor colorWithAlphaComponent:0.4], | |
NSFontAttributeName: [UIFont systemFontOfSize:24 weight:UIFontWeightMedium] | |
}; | |
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"*" attributes:attributes]; | |
if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) { | |
[self addTarget:self | |
action:@selector(editingChanged:) | |
forControlEvents:UIControlEventEditingChanged]; | |
} | |
} | |
return self; | |
} | |
- (void)editingChanged:(PINTextField *)sender { | |
[self insertText:sender.text]; | |
} | |
- (void)deleteBackward { | |
[self.delegate textFieldDidDelete:self]; | |
} | |
- (void)insertText:(NSString *)text { | |
[self.delegate textField:self didInsert:(NSString *)text]; | |
} | |
- (void)select:(id)sender {} // do nothing | |
- (void)selectAll:(id)sender {} // do nothing | |
- (void)paste:(id)sender {} // do nothing | |
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; } // no popup menu actions | |
- (BOOL)becomeFirstResponder { | |
self.selectedTextRange = [self textRangeFromPosition:self.endOfDocument toPosition:self.endOfDocument]; | |
return [super becomeFirstResponder]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment