效果图:
代码实现
重点是:实现<UITextInputTraits, UIKeyInput>
第一步:HXLDigitTestController.h实现
#import <UIKit/UIKit.h>
#import "HXLDigitInput.h"
@interface HXLDigitTestController :UIViewController{
HXLDigitInput *digitInput;
}
@end
第二步:HXLDigitTestController.m实现
#import "HXLDigitTestController.h"
@interface HXLDigitTestController (private)
-(void)didBeginEditing:(id)sender;
-(void)didEndEditing:(id)sender;
-(void)textDidChange:(id)sender;
-(void)valueChanged:(id)sender;
@end
@implementation HXLDigitTestController
-(void)viewDidLoad{
[superviewDidLoad];
self.view.backgroundColor = [UIColorscrollViewTexturedBackgroundColor];
digitInput = [[HXLDigitInputalloc]initWithNumberOfDigits:6];
digitInput.digitOverlayImage = [UIImageimageNamed:@"digitOverlay"];
digitInput.digitBackgroundImage = [UIImageimageNamed:@"digitControlBG"];
//digitInput.backgroundView = digitBGView;
digitInput.placeHolderCharacter =@"0";
// we are using an overlayimage, so make the bg color clear color
digitInput.digitViewBackgroundColor = [UIColorclearColor];
digitInput.digitViewHighlightedBackgroundColor = [UIColorclearColor];
digitInput.digitViewTextColor = [UIColorwhiteColor];
digitInput.digitViewHighlightedTextColor = [UIColororangeColor];
// we changed the default settings, so call redrawControl
[digitInputredrawControl];
[self.viewaddSubview:digitInput];
// adding the target,actions for available events
[digitInputaddTarget:selfaction:@selector(didBeginEditing:)forControlEvents:UIControlEventEditingDidBegin];
[digitInputaddTarget:selfaction:@selector(didEndEditing:)forControlEvents:UIControlEventEditingDidEnd];
[digitInputaddTarget:selfaction:@selector(textDidChange:)forControlEvents:UIControlEventEditingChanged];
[digitInputaddTarget:selfaction:@selector(valueChanged:)forControlEvents:UIControlEventValueChanged];
}
// dismissing the keyboard
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[digitInputresignFirstResponder];
}
-(void)viewWillLayoutSubviews{
[superviewWillLayoutSubviews];
digitInput.frame =CGRectMake(10,90,self.view.frame.size.width-20,55);
}
/// recating on demo events ///
-(void)didBeginEditing:(id)sender{
HXLDigitInput *input= (HXLDigitInput*)sender;
NSLog(@"did begin editing %i",input.value);
}
-(void)didEndEditing:(id)sender{
HXLDigitInput *input = (HXLDigitInput *)sender;
NSLog(@"did end editing %i",input.value);
}
-(void)textDidChange:(id)sender{
HXLDigitInput *input = (HXLDigitInput *)sender;
NSLog(@"text did change %i",input.value);
}
-(void)valueChanged:(id)sender{
HXLDigitInput *input = (HXLDigitInput *)sender;
NSLog(@"value changed %i",input.value);
}
@end
#import <UIKit/UIKit.h>
@interface HXLDigitInput :UIControl <UITextInputTraits,UIKeyInput>
{
@protected
NSInteger currentIndex; // The current active digitview index
NSMutableArray *digitViews; // array containt UILabels
NSString *activeString; // The current string representation of all digitviews
NSMutableArray *bgImageViews; // The background imageviews for all digitViews
}
@property (nonatomic,assign)NSInteger numberOfDigits; // number of digitViews
@property (nonatomic,assign)NSInteger value; // value represented by the digitViews
@property (nonatomic,assign)BOOL matchNumberOfDigitsWithValueLength; // sets number of digits according to value legth
Digit View Appearance /
// After changing something here call -(void)redrawControl afterwards
@property (nonatomic,strong)NSString *placeHolderCharacter; // Placeholder for empty digitView, default is 0
@property (nonatomic,assign)CGFloat digitPadding; // space between digitviews
@property (nonatomic,strong)UIView *backgroundView; // Background view of the control
@property (nonatomic,strong)UIColor *backgroundColor;
@property (nonatomic,strong)UIColor *highlightedBackgroundColor;
@property (nonatomic,strong)UIImage *digitBackgroundImage; // backgroundImage vor digitview
@property (nonatomic,strong)UIImage *digitOverlayImage; // Overlayimage vor digitview
@property (nonatomic,strong)UIColor *digitViewBackgroundColor; // set to clearcolor when using digitBackgroundView
@property (nonatomic,strong)UIColor *digitViewHighlightedBackgroundColor; // set to clearcolor when using digitBackgroundView
@property (nonatomic,strong)UIColor *digitViewTextColor;
@property (nonatomic,strong)UIColor *digitViewHighlightedTextColor;
@property (nonatomic,strong)UIFont *digitViewFont;
@property (nonatomic,strong)UIColor *digitViewShadowColor;
@property (nonatomic,assign)CGSize digitViewShadowOffset;
@property (nonatomic,strong)UIColor *digitViewHighlightedShadowColor;
@property (nonatomic,assign)CGSize digitViewHighlightedShadowOffset;
-(id)initWithNumberOfDigits:(NSInteger)digitCount;
/*
Redraws the control, needed after setting colors, fonts, and sizes
*/
-(void)redrawControl;
@end
#import "HXLDigitInput.h"
#define kDefaultDigitPadding 5.0f
#define kDefaultPlaceHolderCharacter @"0"
#define kDefaultHighlightedBackgroundColor [UIColor blueColor]
#define kDefaultNormalBackgroundColor [UIColor greenColor]
#define kDefaultHighlightedTextColor [UIColor whiteColor]
#define kDefaultNormalTextColor [UIColor blackColor]
#define kDefaultNormalShadowOffset CGSizeMake(0.0,1.0)
#define kDefaultHighlightedShadowOffset CGSizeMake(0.0,-1.0)
#define kDefaultNormalShadowColor [UIColor clearColor]
#define kDefaultHighlightedShadowColor [UIColor clearColor]
#define kDefaultFont [UIFont boldSystemFontOfSize:30]
#define kControlBackgroundColor [UIColor clearColor]
#define kControlHighlightedBackgroundColor [UIColor clearColor]
@interface HXLDigitInput (private)
-(void)setupDigitViews;
-(void)highlightIndex:(NSInteger)indexToHighlight;
-(void)unhighlightAll;
-(void)setText:(NSString *)text ForIndex:(NSInteger)index;
@end
@implementation HXLDigitInput
@synthesize value = _value;
@synthesize matchNumberOfDigitsWithValueLength =_matchNumberOfDigitsWithValueLength;
@synthesize numberOfDigits =_numberOfDigits;
@synthesize placeHolderCharacter =_placeHolderCharacter;
@synthesize digitPadding =_digitPadding;
@synthesize backgroundView =_backgroundView;
@synthesize digitOverlayImage =_digitOverlayImage;
@synthesize digitBackgroundImage =_digitBackgroundImage;
@synthesize backgroundColor =_backgroundColor;
@synthesize highlightedBackgroundColor =_highlightedBackgroundColor;
@synthesize digitViewBackgroundColor =_digitViewBackgroundColor;
@synthesize digitViewHighlightedBackgroundColor =_digitViewHighlightedBackgroundColor;
@synthesize digitViewTextColor =_digitViewTextColor;
@synthesize digitViewHighlightedTextColor =_digitViewHighlightedTextColor;
@synthesize digitViewFont =_digitViewFont;
@synthesize digitViewShadowColor =_digitViewShadowColor;
@synthesize digitViewShadowOffset =_digitViewShadowOffset;
@synthesize digitViewHighlightedShadowColor =_digitViewHighlightedShadowColor;
@synthesize digitViewHighlightedShadowOffset =_digitViewHighlightedShadowOffset;
/// Initialization ///
#pragma mark - Initializaion
-(id)init{
self = [superinit];
if (self) {
// setting a default background view
_backgroundColor = kControlBackgroundColor;
_highlightedBackgroundColor =kControlHighlightedBackgroundColor;
_backgroundView = [[UIViewalloc]init];
_backgroundView.backgroundColor =_backgroundColor;
[selfaddSubview:_backgroundView];
// creating the array that holds the digit views and the bg image views
digitViews = [[NSMutableArrayalloc]init];
bgImageViews = [[NSMutableArrayalloc]init];
// setting a dafault digitview appearance
_digitViewHighlightedBackgroundColor =kDefaultHighlightedBackgroundColor;
_digitViewBackgroundColor =kDefaultNormalBackgroundColor;
_digitViewHighlightedTextColor =kDefaultHighlightedTextColor;
_digitViewTextColor =kDefaultNormalTextColor;
_digitViewShadowColor =kDefaultNormalShadowColor;
_digitViewShadowOffset =kDefaultNormalShadowOffset;
_digitViewHighlightedShadowOffset =kDefaultHighlightedShadowOffset;
_digitViewHighlightedShadowColor =kDefaultHighlightedShadowColor;
_digitViewFont =kDefaultFont;
_matchNumberOfDigitsWithValueLength =NO;
_placeHolderCharacter =kDefaultPlaceHolderCharacter;
}
return self;
}
-(id)initWithNumberOfDigits:(NSInteger)digitCount{
self = [selfinit];
if (self) {
[self setNumberOfDigits:digitCount];
}
return self;
}
-(void)redrawControl{
[selfsetupDigitViews];
}
Overriden property setters ///
#pragma mark - overriden property setters
-(void)setPlaceHolderCharacter:(NSString *)placeHolderCharacter
{
if (placeHolderCharacter.length >1) {
NSRange range;
range.length =1;
range.location =0;
_placeHolderCharacter = [placeHolderCharactersubstringWithRange:range];
@throw [NSExceptionexceptionWithName:@"PlaceholderCharacterTooLong"reason:@"The Placeholder character must be an NSString with a maximum length of 1"userInfo:nil];
}else {
_placeHolderCharacter = placeHolderCharacter;
}
// redraw everything
[selfsetupDigitViews];
}
-(void)setMatchNumberOfDigitsWithValueLength:(BOOL)matchNumberOfDigitsWithValueLength{
_matchNumberOfDigitsWithValueLength = matchNumberOfDigitsWithValueLength;
// redraw everthing
[selfsetValue:_value];
}
// Override property setter
-(void)setNumberOfDigits:(NSInteger)numberOfDigits{
_numberOfDigits = numberOfDigits;
if (numberOfDigits > 9) {
@throw [NSExceptionexceptionWithName:@"NSRangeExeption"reason:@"To many digits for NSInteger"userInfo:nil];
}
[self setupDigitViews];
}
// Set a new integer value
-(void)setValue:(NSInteger)value{
// do magic for displaying
_value = value;
NSString *valueString = [NSStringstringWithFormat:@"%i",value];
activeString = valueString;
int valueStringLength = valueString.length;
// if lenght of value is bigger than
// the number of digit view we set up
// a new number according to the length, but
// only if matchNumberOfDigitsWithValueLength is TRUE
if (_matchNumberOfDigitsWithValueLength) {
self.numberOfDigits = valueStringLength;
}
int padding = _numberOfDigits-valueStringLength;
if (padding < 0) {
padding =0;
}
int currentPos = 0;
int stringIndex =0;
for (UILabel *digitViewindigitViews) {
if (currentPos >= padding) {
NSRange range;
range.location = stringIndex;
range.length =1;
digitView.text = [valueStringsubstringWithRange:range];
stringIndex++;
}
currentPos++;
}
}
-(void)setBackgroundView:(UIView *)backgroundView{
[_backgroundViewremoveFromSuperview];
_backgroundView = backgroundView;
[self insertSubview:_backgroundView atIndex:0];
}
// Private Methods //
#pragma mark - private methods
// generate the digit views
-(void)setupDigitViews{
// remove the old views
for (UIView *digitViewindigitViews) {
[digitViewremoveFromSuperview];
}
for (UIView *bgViewinbgImageViews) {
[bgView removeFromSuperview];
}
[digitViewsremoveAllObjects];
[bgImageViewsremoveAllObjects];
// generate new views and set default digit to 0
for (int i =0; i <_numberOfDigits; i++) {
UILabel *newDigitView = [[UILabelalloc]init];
// configuring the label
newDigitView.textAlignment = UITextAlignmentCenter;
newDigitView.textColor =_digitViewTextColor;
newDigitView.highlightedTextColor =_digitViewHighlightedTextColor;
newDigitView.shadowColor =_digitViewShadowColor;
newDigitView.shadowOffset =_digitViewShadowOffset;
newDigitView.backgroundColor =_digitViewBackgroundColor;
newDigitView.font =_digitViewFont;
newDigitView.adjustsFontSizeToFitWidth =YES;
newDigitView.text =_placeHolderCharacter;
// adding the custom background if available
if (_digitOverlayImage) {
UIImageView *overlayImageView = [[UIImageViewalloc]initWithImage:_digitOverlayImage];
[newDigitView addSubview:overlayImageView];
}
if (_digitBackgroundImage) {
UIImageView *bgDigitView = [[UIImageViewalloc]initWithImage:_digitBackgroundImage];
[self addSubview:bgDigitView];
[bgImageViewsaddObject:bgDigitView];
}
// adding the digit to the view and the digitview array
[self addSubview:newDigitView];
[digitViewsaddObject:newDigitView];
}
[self setNeedsLayout];
}
// highlights the digit view with the given index
-(void)highlightIndex:(NSInteger)indexToHighlight{
for (int i =0; i < [digitViewscount]; i++) {
UILabel *digitView = [digitViewsobjectAtIndex:i];
if (i == indexToHighlight) {
digitView.backgroundColor =_digitViewHighlightedBackgroundColor;
digitView.highlighted =YES;
digitView.shadowColor = _digitViewHighlightedShadowColor;
digitView.shadowOffset =_digitViewHighlightedShadowOffset;
}else {
digitView.backgroundColor =_digitViewBackgroundColor;
digitView.shadowColor = _digitViewShadowColor;
digitView.shadowOffset =_digitViewShadowOffset;
digitView.highlighted = NO;
}
}
}
// Unhighlights all digit views
-(void)unhighlightAll{
for (UILabel *digitViewindigitViews) {
digitView.backgroundColor = _digitViewBackgroundColor;
digitView.shadowColor =_digitViewShadowColor;
digitView.shadowOffset =_digitViewShadowOffset;
digitView.highlighted = NO;
}
}
// Set text for a given digitview index
-(void)setText:(NSString *)text ForIndex:(NSInteger)index{
UILabel *activeDigitView = [digitViewsobjectAtIndex:index];
activeDigitView.text = text;
NSString *valueString = @"";
for (UILabel *digitViewindigitViews) {
NSString *digitString = digitView.text;
if ([digitString isEqualToString:_placeHolderCharacter]) {
valueString = [valueStringstringByAppendingString:@"0"];
}else {
valueString =[valueStringstringByAppendingString:digitString];
}
}
activeString = valueString;
_value = [valueStringintegerValue];
[selfsendActionsForControlEvents:UIControlEventEditingChanged];
[selfsendActionsForControlEvents:UIControlEventValueChanged];
}
// UIKeyInput Protocol implementation ///
#pragma mark - UIKeyInput Protocol implementation
// gets called when keyboard text is entered
- (void)insertText:(NSString *)text{
// set the entered text in the digit view
// at the current index
[self setText:text ForIndex:currentIndex];
// automatically jump to the next digit
currentIndex++;
// if currentindex is bigger than
// number of digitviews we assume
// that we are done with entering
// digits and are hiding the keyboard
if (currentIndex >=_numberOfDigits) {
[self resignFirstResponder];
return;
}
[self highlightIndex:currentIndex];
}
// get called when keboard delete button is pressed
// Delete button action is just moving
// the current position on position to
// the left
- (void)deleteBackward{
/*
for (int i = 0; i <= currentIndex; i++) {
NSLog(@"current index");
[self setText:_placeHolderCharacter ForIndex:i];
}
*/
//currentIndex = 0;
/*
if (currentIndex >= 1) {
UILabel *previousDigitView = [digitViews objectAtIndex:(currentIndex-1)];
if ([previousDigitView.text isEqualToString:_placeHolderCharacter]) {
[self setText:_placeHolderCharacter ForIndex:currentIndex];
return;
}
}
*/
currentIndex--;
if (currentIndex <=0) {
currentIndex = 0;
}
[self highlightIndex:currentIndex];
}
-(BOOL)hasText{
returnactiveString !=nil;
}
/ UITextInputTraits protocol implementation
#pragma mark - UITextInputTraits protocol implementation
// always return a number pad keyboard
-(UIKeyboardType)keyboardType{
returnUIKeyboardTypeNumberPad;
}
View Stuff and other internal stuff //
#pragma mark - view stuff and other internal stuff
-(void)layoutSubviews{
[superlayoutSubviews];
_backgroundView.frame =self.bounds;
CGFloat xOffset =0;
CGFloat digitWidth = (self.bounds.size.width-((_numberOfDigits+1)*kDefaultDigitPadding)) / (CGFloat)_numberOfDigits;
for (UIView *digitViewindigitViews) {
digitView.frame = CGRectMake(kDefaultDigitPadding+xOffset,kDefaultDigitPadding, digitWidth,self.bounds.size.height-2*kDefaultDigitPadding);
xOffset = digitView.frame.origin.x+digitView.bounds.size.width;
}
xOffset =0;
// TODO: remove duplicate code and implement better solulition for setting
// frames of bgimage views
if (_digitBackgroundImage) {
for (UIView *bgDigitViewinbgImageViews) {
bgDigitView.frame = CGRectMake(kDefaultDigitPadding+xOffset,kDefaultDigitPadding, digitWidth,self.bounds.size.height-2*kDefaultDigitPadding);
xOffset = bgDigitView.frame.origin.x+bgDigitView.bounds.size.width;
}
}
}
// always return yes
-(BOOL)canBecomeFirstResponder{
[selfsetHighlighted:YES];
[selfsendActionsForControlEvents:UIControlEventEditingDidBegin];
return YES;
}
// Unhighlight all digitviews
// and hide keyboard
-(BOOL)resignFirstResponder{
[selfunhighlightAll];
if (self.highlighted) {
[selfsendActionsForControlEvents:UIControlEventEditingDidEnd];
}
[selfsetHighlighted:NO];
return [superresignFirstResponder];
}
// determine active index and show up keyboard
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touchesanyObject];
CGPoint touchPoint = [touchlocationInView:self];
// TODO: ignore padding to have a bigger touch area
// Check if a touch is inside a digitview
// if so set the currentIndex and show
// up the keyboard by becoming the first
// responder
NSInteger touchedIndex = 0;
for (UIView *digitViewindigitViews) {
if (CGRectContainsPoint(digitView.frame, touchPoint)) {
currentIndex = touchedIndex;
[self highlightIndex:currentIndex];
[self becomeFirstResponder];
break;
}
touchedIndex++;
}
}
UIControl Methods //
#pragma mark - UIControl Methods
-(void)setEnabled:(BOOL)enabled{
[supersetEnabled:enabled];
if (enabled) {
for (UIView *digitViewindigitViews) {
digitView.alpha =1.0;
}
}else {
for (UIView *digitViewindigitViews) {
digitView.alpha =0.5;
}
}
}
-(void)setHighlighted:(BOOL)highlighted{
[supersetHighlighted:highlighted];
if (highlighted) {
self.backgroundView.backgroundColor =_highlightedBackgroundColor;
}else {
self.backgroundView.backgroundColor =_backgroundColor;
}
}
@end