From e24209e31d624961cd55ac3e2f32e9ba33d47aa5 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Wed, 1 May 2013 17:20:56 -0400 Subject: [PATCH 01/15] fixed wrong method name referenced from selector --- JSTokenField/JSTokenField.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index f607c29..35fccaa 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -213,7 +213,7 @@ - (void)deleteHighlightedToken [_deletedToken removeFromSuperview]; [_tokens removeObject:_deletedToken]; - if ([self.delegate respondsToSelector:@selector(tokenField:didRemove:representedObject:)]) + if ([self.delegate respondsToSelector:@selector(tokenField:didRemoveToken:representedObject:)]) { [self.delegate tokenField:self didRemoveToken:tokenName representedObject:_deletedToken.representedObject]; } From 425456835fa24a2c2b62c84994ea453d5129e831 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Mon, 6 May 2013 18:32:39 -0400 Subject: [PATCH 02/15] Added a didBeginEditing delegate method --- JSTokenField/JSTokenField.h | 1 + JSTokenField/JSTokenField.m | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index 2aa3a83..bd4aab5 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -74,5 +74,6 @@ extern NSString *const JSDeletedTokenKey; - (BOOL)tokenFieldShouldReturn:(JSTokenField *)tokenField; - (void)tokenFieldDidEndEditing:(JSTokenField *)tokenField; +- (void)tokenFieldDidBeginEditing:(JSTokenField *)tokenField; @end diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 35fccaa..5d632b1 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -408,4 +408,12 @@ - (void)textFieldDidEndEditing:(UITextField *)textField } } +- (void)textFieldDidBeginEditing:(UITextField *)textField +{ + if ([self.delegate respondsToSelector:@selector(tokenFieldDidBeginEditing:)]) { + [self.delegate tokenFieldDidBeginEditing:self]; + return; + } +} + @end From cc409aa18fbd2ed71333a371c01ff3357eb141aa Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 7 May 2013 18:56:39 -0400 Subject: [PATCH 03/15] Added a didSelectTokenButton delegate callback method. Added a resignFirstReponder on the JSTokenField which clears first responder for text field or token button --- JSTokenField/JSTokenField.h | 3 ++- JSTokenField/JSTokenField.m | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index bd4aab5..a43806e 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -69,11 +69,12 @@ extern NSString *const JSDeletedTokenKey; - (void)tokenField:(JSTokenField *)tokenField didRemoveToken:(NSString *)title representedObject:(id)obj; - (BOOL)tokenField:(JSTokenField *)tokenField shouldRemoveToken:(NSString *)title representedObject:(id)obj; - - (void)tokenFieldTextDidChange:(JSTokenField *)tokenField; - (BOOL)tokenFieldShouldReturn:(JSTokenField *)tokenField; - (void)tokenFieldDidEndEditing:(JSTokenField *)tokenField; - (void)tokenFieldDidBeginEditing:(JSTokenField *)tokenField; +- (void)didSelectTokenButton:(JSTokenButton *)tokenButton; + @end diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 5d632b1..06bae6b 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -332,6 +332,10 @@ - (void)toggle:(id)sender JSTokenButton *token = (JSTokenButton *)sender; [token setToggled:YES]; [token becomeFirstResponder]; + + if ([self.delegate respondsToSelector:@selector(didSelectTokenButton:)]) { + [self.delegate didSelectTokenButton:token]; + } } - (void)setFrame:(CGRect)frame @@ -353,6 +357,25 @@ - (void)setFrame:(CGRect)frame } } + +- (BOOL)resignFirstResponder +{ + if ([_textField isFirstResponder]) { + return [_textField resignFirstResponder]; + }; + + for (JSTokenButton *token in _tokens) + { + if ([token isFirstResponder]) + { + [token setToggled:NO]; + return [token resignFirstResponder]; + } + } + + return NO; +} + #pragma mark - #pragma mark UITextFieldDelegate @@ -412,7 +435,6 @@ - (void)textFieldDidBeginEditing:(UITextField *)textField { if ([self.delegate respondsToSelector:@selector(tokenFieldDidBeginEditing:)]) { [self.delegate tokenFieldDidBeginEditing:self]; - return; } } From 6eeb9e2aed72f4738dcfbb0659a4eb27c8286ff4 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 21 May 2013 16:45:40 -0400 Subject: [PATCH 04/15] added ability to have a view be added to a JSTokenButton --- JSTokenField/JSTokenButton.h | 1 + JSTokenField/JSTokenButton.m | 29 ++++++++++++++++++++++++++++ JSTokenField/JSTokenField.h | 7 ++++--- JSTokenField/JSTokenField.m | 37 ++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/JSTokenField/JSTokenButton.h b/JSTokenField/JSTokenButton.h index 1223204..6a01b48 100644 --- a/JSTokenField/JSTokenButton.h +++ b/JSTokenField/JSTokenButton.h @@ -50,5 +50,6 @@ @property (nonatomic, assign) JSTokenField *parentField; + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj; ++ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj; @end diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index 783188a..c6620f7 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -64,6 +64,35 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj return button; } + ++ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj +{ + JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; + button.frame = CGRectMake(0.f, 0.f, view.frame.size.width, view.frame.size.height); + [button addSubview:view]; + +// [button setAdjustsImageWhenHighlighted:NO]; +// [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; +// [[button titleLabel] setFont:[UIFont fontWithName:@"Helvetica Neue" size:15]]; +// [[button titleLabel] setLineBreakMode:UILineBreakModeTailTruncation]; +// [button setTitleEdgeInsets:UIEdgeInsetsMake(2, 10, 0, 10)]; + +// [button setTitle:string forState:UIControlStateNormal]; + +// [button sizeToFit]; +// CGRect frame = [button frame]; +// frame.size.width += 20; +// frame.size.height = 25; +// [button setFrame:frame]; + + [button setToggled:NO]; + + [button setRepresentedObject:obj]; + + return button; +} + + - (void)setToggled:(BOOL)toggled { _toggled = toggled; diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index a43806e..38a5feb 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -55,6 +55,7 @@ extern NSString *const JSDeletedTokenKey; @property (nonatomic, assign) id delegate; - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj; +- (void)addTokenWithView:(UIView *)view representedObject:(id)obj; - (void)removeTokenForString:(NSString *)string; - (void)removeTokenWithRepresentedObject:(id)representedObject; - (void)removeAllTokens; @@ -65,9 +66,9 @@ extern NSString *const JSDeletedTokenKey; @optional -- (void)tokenField:(JSTokenField *)tokenField didAddToken:(NSString *)title representedObject:(id)obj; -- (void)tokenField:(JSTokenField *)tokenField didRemoveToken:(NSString *)title representedObject:(id)obj; -- (BOOL)tokenField:(JSTokenField *)tokenField shouldRemoveToken:(NSString *)title representedObject:(id)obj; +- (void)tokenField:(JSTokenField *)tokenField didAddToken:(id)value representedObject:(id)obj; +- (void)tokenField:(JSTokenField *)tokenField didRemoveToken:(id)value representedObject:(id)obj; +- (BOOL)tokenField:(JSTokenField *)tokenField shouldRemoveToken:(id)value representedObject:(id)obj; - (void)tokenFieldTextDidChange:(JSTokenField *)tokenField; diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 06bae6b..de80f38 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -142,6 +142,24 @@ - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj } } + +- (void)addTokenWithView:(UIView *)view representedObject:(id)obj +{ + if (view) + { + JSTokenButton *token = [self tokenWithView:view representedObject:obj]; + token.parentField = self; + [_tokens addObject:token]; + + if ([self.delegate respondsToSelector:@selector(tokenField:didAddToken:representedObject:)]) + { + [self.delegate tokenField:self didAddToken:view representedObject:obj]; + } + + [self setNeedsLayout]; + } +} + - (void)removeTokenWithTest:(BOOL (^)(JSTokenButton *token))test { JSTokenButton *tokenToRemove = nil; for (JSTokenButton *token in [_tokens reverseObjectEnumerator]) { @@ -242,6 +260,25 @@ - (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj return token; } +- (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj +{ + JSTokenButton *token = [JSTokenButton tokenWithView:view representedObject:obj]; + CGRect frame = [token frame]; + + if (frame.size.width > self.frame.size.width) + { + frame.size.width = self.frame.size.width - (WIDTH_PADDING * 2); + } + + [token setFrame:frame]; + + [token addTarget:self + action:@selector(toggle:) + forControlEvents:UIControlEventTouchUpInside]; + + return token; +} + - (void)layoutSubviews { CGRect currentRect = CGRectZero; From 213d8bfca1f1277d7ee9f49e0d7f80ccd33c7c73 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 21 May 2013 17:01:08 -0400 Subject: [PATCH 05/15] changed the deleteBackward to use removeTokenWithRepresentedObject so that token buttons that are not strings can be deleted --- JSTokenField/JSTokenButton.m | 39 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index c6620f7..0df145a 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -38,8 +38,8 @@ @implementation JSTokenButton @synthesize representedObject = _representedObject; @synthesize parentField = _parentField; -+ (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj -{ + ++ (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj { JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; [button setNormalBg:[[UIImage imageNamed:@"tokenNormal.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0]]; [button setHighlightedBg:[[UIImage imageNamed:@"tokenHighlighted.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0]]; @@ -65,28 +65,11 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj } -+ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj -{ ++ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(0.f, 0.f, view.frame.size.width, view.frame.size.height); [button addSubview:view]; - -// [button setAdjustsImageWhenHighlighted:NO]; -// [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; -// [[button titleLabel] setFont:[UIFont fontWithName:@"Helvetica Neue" size:15]]; -// [[button titleLabel] setLineBreakMode:UILineBreakModeTailTruncation]; -// [button setTitleEdgeInsets:UIEdgeInsetsMake(2, 10, 0, 10)]; - -// [button setTitle:string forState:UIControlStateNormal]; - -// [button sizeToFit]; -// CGRect frame = [button frame]; -// frame.size.width += 20; -// frame.size.height = 25; -// [button setFrame:frame]; - [button setToggled:NO]; - [button setRepresentedObject:obj]; return button; @@ -109,14 +92,15 @@ - (void)setToggled:(BOOL)toggled } } -- (void)dealloc -{ + +- (void)dealloc { self.representedObject = nil; self.highlightedBg = nil; self.normalBg = nil; [super dealloc]; } + - (BOOL)becomeFirstResponder { BOOL superReturn = [super becomeFirstResponder]; if (superReturn) { @@ -125,6 +109,7 @@ - (BOOL)becomeFirstResponder { return superReturn; } + - (BOOL)resignFirstResponder { BOOL superReturn = [super resignFirstResponder]; if (superReturn) { @@ -133,8 +118,12 @@ - (BOOL)resignFirstResponder { return superReturn; } + #pragma mark - UIKeyInput + + - (void)deleteBackward { + id delegate = _parentField.delegate; if ([delegate respondsToSelector:@selector(tokenField:shouldRemoveToken:representedObject:)]) { NSString *name = [self titleForState:UIControlStateNormal]; @@ -143,12 +132,16 @@ - (void)deleteBackward { return; } } - [_parentField removeTokenForString:[self titleForState:UIControlStateNormal]]; + + [_parentField removeTokenWithRepresentedObject:self.representedObject]; } + - (BOOL)hasText { return NO; } + + - (void)insertText:(NSString *)text { return; } From 1851e6cfd87b67da030d1c04c400bf939463e646 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 21 May 2013 19:42:04 -0400 Subject: [PATCH 06/15] removed toggle and used the native selected state for a button instead --- JSTokenField/JSTokenButton.h | 15 +++--------- JSTokenField/JSTokenButton.m | 45 +++++++++++------------------------- JSTokenField/JSTokenField.h | 1 + JSTokenField/JSTokenField.m | 19 +++++++++------ 4 files changed, 29 insertions(+), 51 deletions(-) diff --git a/JSTokenField/JSTokenButton.h b/JSTokenField/JSTokenButton.h index 6a01b48..0ddbd3f 100644 --- a/JSTokenField/JSTokenButton.h +++ b/JSTokenField/JSTokenButton.h @@ -27,25 +27,16 @@ // #import + @class JSTokenField; @interface JSTokenButton : UIButton { - - BOOL _toggled; - - UIImage *_normalBg; - UIImage *_highlightedBg; - id _representedObject; - + id _value; } -@property (nonatomic, getter=isToggled) BOOL toggled; - -@property (nonatomic, retain) UIImage *normalBg; -@property (nonatomic, retain) UIImage *highlightedBg; - @property (nonatomic, retain) id representedObject; +@property (nonatomic, retain) id value; @property (nonatomic, assign) JSTokenField *parentField; diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index 0df145a..383f705 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -32,19 +32,21 @@ @implementation JSTokenButton -@synthesize toggled = _toggled; -@synthesize normalBg = _normalBg; -@synthesize highlightedBg = _highlightedBg; +@synthesize value = _value; @synthesize representedObject = _representedObject; @synthesize parentField = _parentField; + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj { JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; - [button setNormalBg:[[UIImage imageNamed:@"tokenNormal.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0]]; - [button setHighlightedBg:[[UIImage imageNamed:@"tokenHighlighted.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0]]; + + [button setBackgroundImage:[[UIImage imageNamed:@"tokenNormal.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0] + forState:UIControlStateNormal]; + [button setBackgroundImage:[[UIImage imageNamed:@"tokenHighlighted.png"] stretchableImageWithLeftCapWidth:14 topCapHeight:0] + forState:UIControlStateSelected]; + [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; [button setAdjustsImageWhenHighlighted:NO]; - [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [[button titleLabel] setFont:[UIFont fontWithName:@"Helvetica Neue" size:15]]; [[button titleLabel] setLineBreakMode:UILineBreakModeTailTruncation]; [button setTitleEdgeInsets:UIEdgeInsetsMake(2, 10, 0, 10)]; @@ -57,8 +59,7 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj frame.size.height = 25; [button setFrame:frame]; - [button setToggled:NO]; - + [button setValue:string]; [button setRepresentedObject:obj]; return button; @@ -69,34 +70,15 @@ + (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(0.f, 0.f, view.frame.size.width, view.frame.size.height); [button addSubview:view]; - [button setToggled:NO]; + [button setValue:view]; [button setRepresentedObject:obj]; return button; } -- (void)setToggled:(BOOL)toggled -{ - _toggled = toggled; - - if (_toggled) - { - [self setBackgroundImage:self.highlightedBg forState:UIControlStateNormal]; - [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; - } - else - { - [self setBackgroundImage:self.normalBg forState:UIControlStateNormal]; - [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; - } -} - - - (void)dealloc { self.representedObject = nil; - self.highlightedBg = nil; - self.normalBg = nil; [super dealloc]; } @@ -104,7 +86,7 @@ - (void)dealloc { - (BOOL)becomeFirstResponder { BOOL superReturn = [super becomeFirstResponder]; if (superReturn) { - self.toggled = YES; + self.selected = YES; } return superReturn; } @@ -113,7 +95,7 @@ - (BOOL)becomeFirstResponder { - (BOOL)resignFirstResponder { BOOL superReturn = [super resignFirstResponder]; if (superReturn) { - self.toggled = NO; + self.selected = NO; } return superReturn; } @@ -126,8 +108,7 @@ - (void)deleteBackward { id delegate = _parentField.delegate; if ([delegate respondsToSelector:@selector(tokenField:shouldRemoveToken:representedObject:)]) { - NSString *name = [self titleForState:UIControlStateNormal]; - BOOL shouldRemove = [delegate tokenField:_parentField shouldRemoveToken:name representedObject:self.representedObject]; + BOOL shouldRemove = [delegate tokenField:_parentField shouldRemoveToken:self.value representedObject:self.representedObject]; if (!shouldRemove) { return; } diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index 38a5feb..219de64 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -77,5 +77,6 @@ extern NSString *const JSDeletedTokenKey; - (void)tokenFieldDidBeginEditing:(JSTokenField *)tokenField; - (void)didSelectTokenButton:(JSTokenButton *)tokenButton; +- (void)didUnselectTokenButton:(JSTokenButton *)tokenButton; @end diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index de80f38..b000f1c 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -216,7 +216,7 @@ - (void)deleteHighlightedToken for (int i = 0; i < [_tokens count]; i++) { _deletedToken = [[_tokens objectAtIndex:i] retain]; - if ([_deletedToken isToggled]) + if (_deletedToken.selected) { NSString *tokenName = [_deletedToken titleForState:UIControlStateNormal]; if ([self.delegate respondsToSelector:@selector(tokenField:shouldRemoveToken:representedObject:)]) { @@ -363,11 +363,11 @@ - (void)toggle:(id)sender { for (JSTokenButton *token in _tokens) { - [token setToggled:NO]; + token.selected = NO; } JSTokenButton *token = (JSTokenButton *)sender; - [token setToggled:YES]; + token.selected = YES; [token becomeFirstResponder]; if ([self.delegate respondsToSelector:@selector(didSelectTokenButton:)]) { @@ -405,8 +405,14 @@ - (BOOL)resignFirstResponder { if ([token isFirstResponder]) { - [token setToggled:NO]; - return [token resignFirstResponder]; + token.selected = NO; + + BOOL retVal = [token resignFirstResponder]; + if ([self.delegate respondsToSelector:@selector(didUnselectTokenButton:)]) { + [self.delegate didUnselectTokenButton:token]; + } + + return retVal; } } @@ -432,10 +438,9 @@ - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRang return NO; } - NSString *name = [token titleForState:UIControlStateNormal]; // If we don't allow deleting the token, don't even bother letting it highlight BOOL responds = [self.delegate respondsToSelector:@selector(tokenField:shouldRemoveToken:representedObject:)]; - if (responds == NO || [self.delegate tokenField:self shouldRemoveToken:name representedObject:token.representedObject]) { + if (responds == NO || [self.delegate tokenField:self shouldRemoveToken:token.value representedObject:token.representedObject]) { [token becomeFirstResponder]; } return NO; From b78d19b7b33ae8d2f4d4eae4c0cc5e6b3d109c6f Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 21 May 2013 20:07:58 -0400 Subject: [PATCH 07/15] call unselectToken on toggle --- JSTokenField/JSTokenField.m | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index b000f1c..7e92df2 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -363,6 +363,11 @@ - (void)toggle:(id)sender { for (JSTokenButton *token in _tokens) { + if ([token isFirstResponder]) { + if ([self.delegate respondsToSelector:@selector(didUnselectTokenButton:)]) { + [self.delegate didUnselectTokenButton:token]; + } + } token.selected = NO; } @@ -406,13 +411,7 @@ - (BOOL)resignFirstResponder if ([token isFirstResponder]) { token.selected = NO; - - BOOL retVal = [token resignFirstResponder]; - if ([self.delegate respondsToSelector:@selector(didUnselectTokenButton:)]) { - [self.delegate didUnselectTokenButton:token]; - } - - return retVal; + return [token resignFirstResponder]; } } From af9ca90d78af25b1d09ab6e18b1cfc3e0b15c6a5 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Tue, 21 May 2013 20:19:35 -0400 Subject: [PATCH 08/15] added call to didselect delegate call back when first responder is called for for selecting on backspace --- JSTokenField/JSTokenField.m | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 7e92df2..6d843a2 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -91,9 +91,6 @@ - (void)commonSetup { [self addSubview:_label]; - // self.layer.borderColor = [[UIColor blueColor] CGColor]; - // self.layer.borderWidth = 1.0; - _tokens = [[NSMutableArray alloc] init]; frame.origin.y += HEIGHT_PADDING; @@ -104,10 +101,7 @@ - (void)commonSetup { [_textField setBackground:nil]; [_textField setBackgroundColor:[UIColor clearColor]]; [_textField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter]; - - // [_textField.layer setBorderColor:[[UIColor redColor] CGColor]]; - // [_textField.layer setBorderWidth:1.0]; - + [self addSubview:_textField]; [self.textField addTarget:self action:@selector(textFieldWasUpdated:) forControlEvents:UIControlEventEditingChanged]; @@ -441,6 +435,9 @@ - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRang BOOL responds = [self.delegate respondsToSelector:@selector(tokenField:shouldRemoveToken:representedObject:)]; if (responds == NO || [self.delegate tokenField:self shouldRemoveToken:token.value representedObject:token.representedObject]) { [token becomeFirstResponder]; + if ([self.delegate respondsToSelector:@selector(didSelectTokenButton:)]) { + [self.delegate didSelectTokenButton:token]; + } } return NO; } From b282176b1f20ddda8b165d3345213d47aab2a1d0 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Wed, 22 May 2013 15:42:45 -0400 Subject: [PATCH 09/15] ARC supported - updated to work with ARC. --- JSTokenField/JSTokenButton.m | 3 +-- JSTokenField/JSTokenField.h | 14 +++----------- JSTokenField/JSTokenField.m | 23 +++-------------------- 3 files changed, 7 insertions(+), 33 deletions(-) diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index 383f705..fca6e2e 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -78,8 +78,7 @@ + (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { - (void)dealloc { - self.representedObject = nil; - [super dealloc]; + } diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index 219de64..965388b 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -38,21 +38,13 @@ extern NSString *const JSDeletedTokenKey; @interface JSTokenField : UIView { - NSMutableArray *_tokens; - - UITextField *_textField; - - id _delegate; - JSTokenButton *_deletedToken; - - UILabel *_label; } @property (nonatomic, readonly) UITextField *textField; -@property (nonatomic, retain) UILabel *label; -@property (nonatomic, readonly, copy) NSMutableArray *tokens; -@property (nonatomic, assign) id delegate; +@property (nonatomic) UILabel *label; +@property (nonatomic, readonly) NSMutableArray *tokens; +@property (nonatomic, weak) id delegate; - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj; - (void)addTokenWithView:(UIView *)view representedObject:(id)obj; diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 6d843a2..a5d019d 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -52,10 +52,6 @@ - (void)commonSetup; @implementation JSTokenField -@synthesize tokens = _tokens; -@synthesize textField = _textField; -@synthesize label = _label; -@synthesize delegate = _delegate; - (id)initWithFrame:(CGRect)frame { @@ -107,16 +103,6 @@ - (void)commonSetup { [self.textField addTarget:self action:@selector(textFieldWasUpdated:) forControlEvents:UIControlEventEditingChanged]; } -- (void)dealloc -{ - [_textField release], _textField = nil; - [_label release], _label = nil; - [_tokens release], _tokens = nil; - - [super dealloc]; -} - - - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj { NSString *aString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; @@ -168,7 +154,6 @@ - (void)removeTokenWithTest:(BOOL (^)(JSTokenButton *token))test { [_textField becomeFirstResponder]; } [tokenToRemove removeFromSuperview]; - [[tokenToRemove retain] autorelease]; // removing it from the array will dealloc the object, but we want to keep it around for the delegate method below [_tokens removeObject:tokenToRemove]; if ([self.delegate respondsToSelector:@selector(tokenField:didRemoveToken:representedObject:)]) @@ -202,14 +187,13 @@ - (void)removeAllTokens { return token == button; }]; } - [tokensCopy release]; } - (void)deleteHighlightedToken { for (int i = 0; i < [_tokens count]; i++) { - _deletedToken = [[_tokens objectAtIndex:i] retain]; + _deletedToken = [_tokens objectAtIndex:i]; if (_deletedToken.selected) { NSString *tokenName = [_deletedToken titleForState:UIControlStateNormal]; @@ -384,12 +368,11 @@ - (void)setFrame:(CGRect)frame [userInfo setObject:[NSValue valueWithCGRect:oldFrame] forKey:JSTokenFieldOldFrameKey]; if (_deletedToken) { - [userInfo setObject:_deletedToken forKey:JSDeletedTokenKey]; - [_deletedToken release], _deletedToken = nil; + [userInfo setObject:_deletedToken forKey:JSDeletedTokenKey]; } if (CGRectEqualToRect(oldFrame, frame) == NO) { - [[NSNotificationCenter defaultCenter] postNotificationName:JSTokenFieldFrameDidChangeNotification object:self userInfo:[[userInfo copy] autorelease]]; + [[NSNotificationCenter defaultCenter] postNotificationName:JSTokenFieldFrameDidChangeNotification object:self userInfo:[userInfo copy]]; } } From 0993d5809e70bf7456e42d82976d22d8d94dbcb7 Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Wed, 22 May 2013 18:54:47 -0400 Subject: [PATCH 10/15] changed adding new tokens to the subview until after frame has resized - this eliminates the token appearing first and the frame resizing under it --- JSTokenField/JSTokenField.m | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index a5d019d..228322b 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -136,7 +136,7 @@ - (void)addTokenWithView:(UIView *)view representedObject:(id)obj [self.delegate tokenField:self didAddToken:view representedObject:obj]; } - [self setNeedsLayout]; + [self setNeedsLayout]; } } @@ -286,10 +286,6 @@ - (void)layoutSubviews [token setFrame:frame]; - if (![token superview]) - { - [self addSubview:token]; - } [lastLineTokens addObject:token]; currentRect.origin.x += frame.size.width + WIDTH_PADDING; @@ -327,16 +323,31 @@ - (void)layoutSubviews if (self.layer.presentationLayer == nil) { [self setFrame:selfFrame]; + [self addMissingTokensAsSubview:_tokens]; } else { - [UIView animateWithDuration:0.3 - animations:^{ - [self setFrame:selfFrame]; - } - completion:nil]; + [UIView animateWithDuration:0.3 + animations:^{ + [self setFrame:selfFrame]; + } + completion:^(BOOL finished) { + [self addMissingTokensAsSubview:_tokens]; + }]; } } + +- (void)addMissingTokensAsSubview:(NSArray *)tokens +{ + for (UIButton *token in _tokens) { + if (![token superview]) + { + [self addSubview:token]; + } + } +} + + - (void)toggle:(id)sender { for (JSTokenButton *token in _tokens) @@ -361,10 +372,10 @@ - (void)toggle:(id)sender - (void)setFrame:(CGRect)frame { CGRect oldFrame = self.frame; + + [super setFrame:frame]; - [super setFrame:frame]; - - NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSValue valueWithCGRect:frame] forKey:JSTokenFieldNewFrameKey]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSValue valueWithCGRect:frame] forKey:JSTokenFieldNewFrameKey]; [userInfo setObject:[NSValue valueWithCGRect:oldFrame] forKey:JSTokenFieldOldFrameKey]; if (_deletedToken) { From ede8bb136f6fce016963ef9a484d0ee00dd8fc2e Mon Sep 17 00:00:00 2001 From: Andy Lister Date: Wed, 22 May 2013 21:24:07 -0400 Subject: [PATCH 11/15] added a editing state --- JSTokenField/JSTokenField.h | 1 + JSTokenField/JSTokenField.m | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index 965388b..0fd17da 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -45,6 +45,7 @@ extern NSString *const JSDeletedTokenKey; @property (nonatomic) UILabel *label; @property (nonatomic, readonly) NSMutableArray *tokens; @property (nonatomic, weak) id delegate; +@property (nonatomic) BOOL editing; - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj; - (void)addTokenWithView:(UIView *)view representedObject:(id)obj; diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 228322b..23f08f4 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -296,7 +296,7 @@ - (void)layoutSubviews textFieldFrame.origin = currentRect.origin; - if ((self.frame.size.width - textFieldFrame.origin.x) >= 60) + if ((self.frame.size.width - textFieldFrame.origin.x) >= ((self.editing)? 60 : 0)) { textFieldFrame.size.width = self.frame.size.width - textFieldFrame.origin.x; } @@ -406,6 +406,13 @@ - (BOOL)resignFirstResponder return NO; } + +- (void)setEditing:(BOOL)editing { + _editing = editing; + [self setNeedsLayout]; +} + + #pragma mark - #pragma mark UITextFieldDelegate @@ -452,9 +459,9 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField - (void)textFieldDidEndEditing:(UITextField *)textField { + self.editing = NO; if ([self.delegate respondsToSelector:@selector(tokenFieldDidEndEditing:)]) { [self.delegate tokenFieldDidEndEditing:self]; - return; } else if ([[textField text] length] > 1) { @@ -465,6 +472,7 @@ - (void)textFieldDidEndEditing:(UITextField *)textField - (void)textFieldDidBeginEditing:(UITextField *)textField { + self.editing = YES; if ([self.delegate respondsToSelector:@selector(tokenFieldDidBeginEditing:)]) { [self.delegate tokenFieldDidBeginEditing:self]; } From 2bd8ad9bd8c4da123a89f0c81f18f5fd32b276a8 Mon Sep 17 00:00:00 2001 From: Cameron Mulhern Date: Tue, 4 Mar 2014 16:23:30 -0500 Subject: [PATCH 12/15] Updates the use of a deprecated line break mode --- JSTokenField/JSTokenButton.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index fca6e2e..486f0cd 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -48,7 +48,7 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj [button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; [button setAdjustsImageWhenHighlighted:NO]; [[button titleLabel] setFont:[UIFont fontWithName:@"Helvetica Neue" size:15]]; - [[button titleLabel] setLineBreakMode:UILineBreakModeTailTruncation]; + [[button titleLabel] setLineBreakMode:NSLineBreakByTruncatingTail]; [button setTitleEdgeInsets:UIEdgeInsetsMake(2, 10, 0, 10)]; [button setTitle:string forState:UIControlStateNormal]; From cf554c649d764c393c6f752de1e28b1fb321acfc Mon Sep 17 00:00:00 2001 From: Jeff Forbes Date: Fri, 5 Jun 2015 17:46:23 -0400 Subject: [PATCH 13/15] Add (hacky) delegate for custom view support --- JSTokenField/JSTokenButton.h | 8 +++++++- JSTokenField/JSTokenButton.m | 10 +++++++++- JSTokenField/JSTokenField.h | 4 ++-- JSTokenField/JSTokenField.m | 4 ++-- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/JSTokenField/JSTokenButton.h b/JSTokenField/JSTokenButton.h index 0ddbd3f..a219026 100644 --- a/JSTokenField/JSTokenButton.h +++ b/JSTokenField/JSTokenButton.h @@ -30,6 +30,11 @@ @class JSTokenField; +@protocol JSTokenButtonCustomView +@optional +- (void)buttonStateChanged:(BOOL)selected; +@end + @interface JSTokenButton : UIButton { id _representedObject; id _value; @@ -38,9 +43,10 @@ @property (nonatomic, retain) id representedObject; @property (nonatomic, retain) id value; +@property (nonatomic, retain) UIView *customView; @property (nonatomic, assign) JSTokenField *parentField; + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj; -+ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj; ++ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj; @end diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index 486f0cd..4ff95c9 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -35,6 +35,7 @@ @implementation JSTokenButton @synthesize value = _value; @synthesize representedObject = _representedObject; @synthesize parentField = _parentField; +@synthesize customView = _customView; + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj { @@ -66,9 +67,10 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj } -+ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { ++ (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { JSTokenButton *button = (JSTokenButton *)[self buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(0.f, 0.f, view.frame.size.width, view.frame.size.height); + button.customView = view; [button addSubview:view]; [button setValue:view]; [button setRepresentedObject:obj]; @@ -86,6 +88,9 @@ - (BOOL)becomeFirstResponder { BOOL superReturn = [super becomeFirstResponder]; if (superReturn) { self.selected = YES; + if ([_customView respondsToSelector:@selector(buttonStateChanged:)]) { + [_customView buttonStateChanged:self.selected]; + } } return superReturn; } @@ -95,6 +100,9 @@ - (BOOL)resignFirstResponder { BOOL superReturn = [super resignFirstResponder]; if (superReturn) { self.selected = NO; + if ([_customView respondsToSelector:@selector(buttonStateChanged:)]) { + [_customView buttonStateChanged:self.selected]; + } } return superReturn; } diff --git a/JSTokenField/JSTokenField.h b/JSTokenField/JSTokenField.h index 0fd17da..58d796b 100644 --- a/JSTokenField/JSTokenField.h +++ b/JSTokenField/JSTokenField.h @@ -29,7 +29,7 @@ #import @class JSTokenButton; -@protocol JSTokenFieldDelegate; +@protocol JSTokenFieldDelegate, JSTokenButtonCustomView; extern NSString *const JSTokenFieldFrameDidChangeNotification; extern NSString *const JSTokenFieldNewFrameKey; @@ -48,7 +48,7 @@ extern NSString *const JSDeletedTokenKey; @property (nonatomic) BOOL editing; - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj; -- (void)addTokenWithView:(UIView *)view representedObject:(id)obj; +- (void)addTokenWithView:(UIView *)view representedObject:(id)obj; - (void)removeTokenForString:(NSString *)string; - (void)removeTokenWithRepresentedObject:(id)representedObject; - (void)removeAllTokens; diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 23f08f4..cb62b1a 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -123,7 +123,7 @@ - (void)addTokenWithTitle:(NSString *)string representedObject:(id)obj } -- (void)addTokenWithView:(UIView *)view representedObject:(id)obj +- (void)addTokenWithView:(UIView *)view representedObject:(id)obj { if (view) { @@ -238,7 +238,7 @@ - (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj return token; } -- (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj +- (JSTokenButton *)tokenWithView:(UIView *)view representedObject:(id)obj { JSTokenButton *token = [JSTokenButton tokenWithView:view representedObject:obj]; CGRect frame = [token frame]; From 0db58295dd51cd9ea8deb1b774b9ed1c8070cbd1 Mon Sep 17 00:00:00 2001 From: Brian Dorfman Date: Mon, 31 Aug 2015 16:34:38 -0700 Subject: [PATCH 14/15] Fix warning about missing super call. --- JSTokenField/JSTokenField.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index cb62b1a..8a1822f 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -390,6 +390,8 @@ - (void)setFrame:(CGRect)frame - (BOOL)resignFirstResponder { + BOOL ourReturnValue = [super resignFirstResponder]; + if ([_textField isFirstResponder]) { return [_textField resignFirstResponder]; }; @@ -403,7 +405,7 @@ - (BOOL)resignFirstResponder } } - return NO; + return ourReturnValue; } From d9b637010de9591a4b0dbeb79bff27428b3b8d69 Mon Sep 17 00:00:00 2001 From: Jack Wong Date: Fri, 17 Nov 2023 12:08:50 -0500 Subject: [PATCH 15/15] Update font to system font --- JSTokenField/JSTokenButton.m | 2 +- JSTokenField/JSTokenField.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/JSTokenField/JSTokenButton.m b/JSTokenField/JSTokenButton.m index 4ff95c9..3a115f0 100644 --- a/JSTokenField/JSTokenButton.m +++ b/JSTokenField/JSTokenButton.m @@ -48,7 +48,7 @@ + (JSTokenButton *)tokenWithString:(NSString *)string representedObject:(id)obj [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; [button setAdjustsImageWhenHighlighted:NO]; - [[button titleLabel] setFont:[UIFont fontWithName:@"Helvetica Neue" size:15]]; + [[button titleLabel] setFont:[UIFont systemFontOfSize:15]]; [[button titleLabel] setLineBreakMode:NSLineBreakByTruncatingTail]; [button setTitleEdgeInsets:UIEdgeInsetsMake(2, 10, 0, 10)]; diff --git a/JSTokenField/JSTokenField.m b/JSTokenField/JSTokenField.m index 8a1822f..9593a7b 100644 --- a/JSTokenField/JSTokenField.m +++ b/JSTokenField/JSTokenField.m @@ -83,7 +83,7 @@ - (void)commonSetup { _label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, frame.size.height)]; [_label setBackgroundColor:[UIColor clearColor]]; [_label setTextColor:[UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0]]; - [_label setFont:[UIFont fontWithName:@"Helvetica Neue" size:17.0]]; + [_label setFont:[UIFont systemFontOfSize:17.0]]; [self addSubview:_label];