From 5da4f7b265713c0f15c17fb4053fbf22aa90caca Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Mon, 11 Aug 2014 10:54:30 -0400 Subject: [PATCH 01/14] WIP: add autocomplete to token field Thus far, we have the concept of autocomplete, the data source API, and callbacks where we would be showing and hiding the table view so far. Definitely more work to complete it, but this is a good checkpoint. --- VENAutocompleteTableViewManager.h | 24 +++++++ VENAutocompleteTableViewManager.m | 71 +++++++++++++++++++ VENTokenField/VENTokenField.h | 2 + VENTokenField/VENTokenField.m | 32 ++++++++- VENTokenFieldSample/ViewController.m | 12 ++++ .../VENTokenFieldSampleTests.m | 6 ++ 6 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 VENAutocompleteTableViewManager.h create mode 100644 VENAutocompleteTableViewManager.m diff --git a/VENAutocompleteTableViewManager.h b/VENAutocompleteTableViewManager.h new file mode 100644 index 0000000..8f128ee --- /dev/null +++ b/VENAutocompleteTableViewManager.h @@ -0,0 +1,24 @@ +// +// VENAutocompleteTableViewManager.h +// Pods +// +// Created by bnicholas on 8/8/14. +// +// + +#import + +@protocol VENAutocompleteTableViewManagerDelegate + +@end + +@interface VENAutocompleteTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate > + +@property (strong, nonatomic) UITableView *tableView; +@property (strong, nonatomic) NSArray *autocompleteOptions; +@property (assign, nonatomic) id delegate; + +- (void)displayTableView; +- (void)hideTableView; + +@end diff --git a/VENAutocompleteTableViewManager.m b/VENAutocompleteTableViewManager.m new file mode 100644 index 0000000..4d2243d --- /dev/null +++ b/VENAutocompleteTableViewManager.m @@ -0,0 +1,71 @@ +// +// VENAutocompleteTableViewManager.m +// Pods +// +// Created by bnicholas on 8/8/14. +// +// + +#import "VENAutocompleteTableViewManager.h" + +@implementation VENAutocompleteTableViewManager + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.autocompleteOptions.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"autocompleteCell" forIndexPath:indexPath]; + + cell.detailTextLabel.text = self.autocompleteOptions[indexPath.row]; + + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ +#warning implement this +} + +- (void)setAutocompleteOptions:(NSArray *)autocompleteOptions +{ + if (!_autocompleteOptions) { + [self displayTableView]; + } + _autocompleteOptions = autocompleteOptions; + if (autocompleteOptions != nil) { + [self.tableView reloadData]; + } else { + [self hideTableView]; + } +} + +- (void)displayTableView +{ + [[[UIAlertView alloc] initWithTitle:@"Would show table" + message:@"" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; +#warning implement this +#warning add a reusable basic cell with @"autocompleteCell" identifier +} + +- (void)hideTableView +{ + [[[UIAlertView alloc] initWithTitle:@"Would hide table" + message:@"" + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil] show]; +#warning implement this +} + +@end diff --git a/VENTokenField/VENTokenField.h b/VENTokenField/VENTokenField.h index 5d5557e..a6746f2 100644 --- a/VENTokenField/VENTokenField.h +++ b/VENTokenField/VENTokenField.h @@ -36,6 +36,8 @@ - (NSString *)tokenField:(VENTokenField *)tokenField titleForTokenAtIndex:(NSUInteger)index; - (NSUInteger)numberOfTokensInTokenField:(VENTokenField *)tokenField; - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField; +- (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField; +- (NSArray *)tokenField:(VENTokenField *)tokenField autocompleteTitlesForText:(NSString *)text; @end diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index dc3cba4..f6d78b6 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -25,6 +25,7 @@ #import #import "VENToken.h" #import "VENBackspaceTextField.h" +#import "VENAutocompleteTableViewManager.h" static const CGFloat VENTokenFieldDefaultVerticalInset = 7.0; static const CGFloat VENTokenFieldDefaultHorizontalInset = 15.0; @@ -34,7 +35,7 @@ static const CGFloat VENTokenFieldDefaultMaxHeight = 150.0; -@interface VENTokenField () +@interface VENTokenField () @property (strong, nonatomic) UIScrollView *scrollView; @property (strong, nonatomic) NSMutableArray *tokens; @@ -44,6 +45,7 @@ @interface VENTokenField () @property (strong, nonatomic) VENBackspaceTextField *inputTextField; @property (strong, nonatomic) UIColor *colorScheme; @property (strong, nonatomic) UILabel *collapsedLabel; +@property (strong, nonatomic) VENAutocompleteTableViewManager *tableViewManager; @end @@ -259,7 +261,6 @@ - (void)layoutTokensWithCurrentX:(CGFloat *)currentX currentY:(CGFloat *)current } } - #pragma mark - Private - (CGFloat)heightForToken @@ -345,6 +346,25 @@ - (void)inputTextFieldDidChange:(UITextField *)textField if ([self.delegate respondsToSelector:@selector(tokenField:didChangeText:)]) { [self.delegate tokenField:self didChangeText:textField.text]; } + if ([self autocompletes]) { + if (textField.text.length > 0) { + if ([self.dataSource respondsToSelector:@selector(tokenField:autocompleteTitlesForText:)]) { + self.tableViewManager.autocompleteOptions = [self.dataSource tokenField:self autocompleteTitlesForText:textField.text]; + } + } else { + self.tableViewManager.autocompleteOptions = nil; + } + + } +} + +- (VENAutocompleteTableViewManager *)tableViewManager +{ + if (!_tableViewManager) { + _tableViewManager = [[VENAutocompleteTableViewManager alloc] init]; + _tableViewManager.delegate = self; + } + return _tableViewManager; } - (void)handleSingleTap:(UITapGestureRecognizer *)gestureRecognizer @@ -418,6 +438,14 @@ - (NSUInteger)numberOfTokens return 0; } +- (BOOL)autocompletes +{ + if ([self.dataSource respondsToSelector:@selector(tokenFieldShouldPresentAutocompleteSelection:)]) { + return [self.dataSource tokenFieldShouldPresentAutocompleteSelection:self]; + } + return NO; +} + - (NSString *)collapsedText { if ([self.dataSource respondsToSelector:@selector(tokenFieldCollapsedText:)]) { diff --git a/VENTokenFieldSample/ViewController.m b/VENTokenFieldSample/ViewController.m index b50205d..06e0b0e 100644 --- a/VENTokenFieldSample/ViewController.m +++ b/VENTokenFieldSample/ViewController.m @@ -12,6 +12,7 @@ @interface ViewController () @property (weak, nonatomic) IBOutlet VENTokenField *tokenField; @property (strong, nonatomic) NSMutableArray *names; +@property (strong, nonatomic) NSArray *knownNames; @end @implementation ViewController @@ -20,6 +21,7 @@ - (void)viewDidLoad { [super viewDidLoad]; self.names = [NSMutableArray array]; + self.knownNames = @[@"Ayaka", @"Mark", @"Neeraj", @"Octocat", @"Octavius", @"Ben"]; self.tokenField.delegate = self; self.tokenField.dataSource = self; self.tokenField.placeholderText = NSLocalizedString(@"Enter names here", nil); @@ -70,4 +72,14 @@ - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField return [NSString stringWithFormat:@"%lu people", [self.names count]]; } +- (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField +{ + return YES; +} + +- (NSArray *)tokenField:(VENTokenField *)tokenField autocompleteTitlesForText:(NSString *)text +{ + return [self.knownNames filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH[c] %@", text]]; +} + @end diff --git a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m index 89746d9..957e77c 100644 --- a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m +++ b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m @@ -38,6 +38,12 @@ - (void)testBasicFlow [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Octocat,"]; } +- (void)testAutocompletePresence +{ + [tester enterTextIntoCurrentFirstResponder:@"Be"]; + [tester waitForViewWithAccessibilityLabel:@"Ben"]; +} + - (void)testResignFirstResponder { [tester tapViewWithAccessibilityLabel:@"To"]; From f3c2edeea7a4aa5c57e91adc906849d7e9c3b8bd Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Mon, 11 Aug 2014 13:58:41 -0400 Subject: [PATCH 02/14] WIP: Display autocomplete table This is still a work in progress, but the table view will actually render in the appropriate location now. At some point I appear to have broken the VENTokenField resizing, and actually taking an auto complete suggestion is still not supported. Just another checkpoint in the implementation. --- VENAutocompleteTableViewManager.h | 11 +++++- VENAutocompleteTableViewManager.m | 65 ++++++++++++++++++++++--------- VENTokenField/VENTokenField.m | 7 +++- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/VENAutocompleteTableViewManager.h b/VENAutocompleteTableViewManager.h index 8f128ee..30979f3 100644 --- a/VENAutocompleteTableViewManager.h +++ b/VENAutocompleteTableViewManager.h @@ -8,17 +8,24 @@ #import +@class VENTokenField; + @protocol VENAutocompleteTableViewManagerDelegate @end -@interface VENAutocompleteTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate > +@interface VENAutocompleteTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate > @property (strong, nonatomic) UITableView *tableView; -@property (strong, nonatomic) NSArray *autocompleteOptions; +@property (strong, nonatomic) VENTokenField *tokenField; +//@property (strong, nonatomic) NSArray *autocompleteOptions; @property (assign, nonatomic) id delegate; +- (instancetype)initWithTokenField:(VENTokenField *)tokenField; + - (void)displayTableView; - (void)hideTableView; +- (void)setAutocompleteOptions:(NSArray *)autocompleteOptions; + @end diff --git a/VENAutocompleteTableViewManager.m b/VENAutocompleteTableViewManager.m index 4d2243d..bba3b23 100644 --- a/VENAutocompleteTableViewManager.m +++ b/VENAutocompleteTableViewManager.m @@ -7,9 +7,25 @@ // #import "VENAutocompleteTableViewManager.h" +#import "VENTokenField.h" + +@interface VENAutocompleteTableViewManager () + +@property (nonatomic, strong) NSArray *options; + +@end @implementation VENAutocompleteTableViewManager +- (instancetype)initWithTokenField:(VENTokenField *)tokenField +{ + self = [super init]; + if (self) { + self.tokenField = tokenField; + } + return self; +} + - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; @@ -17,14 +33,18 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.autocompleteOptions.count; + return self.options.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"autocompleteCell" forIndexPath:indexPath]; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"autocompleteCell"]; + + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"autocompleteCell"]; + } - cell.detailTextLabel.text = self.autocompleteOptions[indexPath.row]; + cell.textLabel.text = self.options[indexPath.row]; return cell; } @@ -34,12 +54,18 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath #warning implement this } + +- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView +{ + [self.tokenField resignFirstResponder]; +} + - (void)setAutocompleteOptions:(NSArray *)autocompleteOptions { - if (!_autocompleteOptions) { + if (!self.options) { [self displayTableView]; } - _autocompleteOptions = autocompleteOptions; + self.options = autocompleteOptions; if (autocompleteOptions != nil) { [self.tableView reloadData]; } else { @@ -49,23 +75,26 @@ - (void)setAutocompleteOptions:(NSArray *)autocompleteOptions - (void)displayTableView { - [[[UIAlertView alloc] initWithTitle:@"Would show table" - message:@"" - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; -#warning implement this -#warning add a reusable basic cell with @"autocompleteCell" identifier + [self.tokenField.superview addSubview:self.tableView]; } - (void)hideTableView { - [[[UIAlertView alloc] initWithTitle:@"Would hide table" - message:@"" - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil] show]; -#warning implement this + [self.tableView removeFromSuperview]; +} + +- (UITableView *)tableView +{ + if (!_tableView) { + _tableView = [[UITableView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.tokenField.frame), + CGRectGetMaxY(self.tokenField.frame), + CGRectGetWidth(self.tokenField.frame), + CGRectGetHeight(self.tokenField.superview.frame) - CGRectGetHeight(self.tokenField.frame)) + style:UITableViewStylePlain]; + _tableView.delegate = self; + _tableView.dataSource = self; + } + return _tableView; } @end diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index f6d78b6..295684e 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -104,6 +104,8 @@ - (void)collapse [self.collapsedLabel removeFromSuperview]; self.scrollView.hidden = YES; [self setHeight:self.originalHeight]; + + [self.tableViewManager hideTableView]; CGFloat currentX = 0; @@ -361,7 +363,7 @@ - (void)inputTextFieldDidChange:(UITextField *)textField - (VENAutocompleteTableViewManager *)tableViewManager { if (!_tableViewManager) { - _tableViewManager = [[VENAutocompleteTableViewManager alloc] init]; + _tableViewManager = [[VENAutocompleteTableViewManager alloc] initWithTokenField:self]; _tableViewManager.delegate = self; } return _tableViewManager; @@ -462,6 +464,9 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { if ([textField.text length]) { [self.delegate tokenField:self didEnterText:textField.text]; + if ([self autocompletes]) { + self.tableViewManager.autocompleteOptions = nil; + } } } return NO; From 567d470ba056e0fe7c2dd11ee6c0665c43836437 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Mon, 11 Aug 2014 16:07:12 -0400 Subject: [PATCH 03/14] Fix a layout bug related to the autocomplete table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By adding the table to the active window rather than to the tokenField’s super view, I’m avoiding a bug where the frame of the token field is reset when it should be growing. This should also help make sure the table view is on top of any other views that happen to exist in the super view. --- VENAutocompleteTableViewManager.m | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/VENAutocompleteTableViewManager.m b/VENAutocompleteTableViewManager.m index bba3b23..4cbd4bd 100644 --- a/VENAutocompleteTableViewManager.m +++ b/VENAutocompleteTableViewManager.m @@ -75,7 +75,7 @@ - (void)setAutocompleteOptions:(NSArray *)autocompleteOptions - (void)displayTableView { - [self.tokenField.superview addSubview:self.tableView]; + [self.tokenField.window addSubview:self.tableView]; } - (void)hideTableView @@ -85,14 +85,17 @@ - (void)hideTableView - (UITableView *)tableView { + CGRect newFrame = CGRectMake(CGRectGetMinX(self.tokenField.frame), + CGRectGetMaxY(self.tokenField.frame), + CGRectGetWidth(self.tokenField.frame), + CGRectGetHeight(self.tokenField.window.frame) - CGRectGetMaxY(self.tokenField.frame)); if (!_tableView) { - _tableView = [[UITableView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.tokenField.frame), - CGRectGetMaxY(self.tokenField.frame), - CGRectGetWidth(self.tokenField.frame), - CGRectGetHeight(self.tokenField.superview.frame) - CGRectGetHeight(self.tokenField.frame)) + _tableView = [[UITableView alloc] initWithFrame:newFrame style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; + } else { + _tableView.frame = newFrame; } return _tableView; } From 556b413559342684264e67418954ffaf27edb470 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Mon, 11 Aug 2014 16:59:44 -0400 Subject: [PATCH 04/14] WIP: Allow selecting from autocomplete options I made the decision that autocomplete is just a typing accelerator, meaning that there is no dedicated callback for selecting an autocompleted value, instead the actual value selected is sent back just as if it was typed manually. I may change this before everything is complete, as this pushes more (potential) verification on the client without a lot saved for us. --- VENAutocompleteTableViewManager.h | 2 +- VENAutocompleteTableViewManager.m | 11 +++++++++-- VENTokenField/VENTokenField.m | 9 +++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/VENAutocompleteTableViewManager.h b/VENAutocompleteTableViewManager.h index 30979f3..0ab8ac6 100644 --- a/VENAutocompleteTableViewManager.h +++ b/VENAutocompleteTableViewManager.h @@ -11,7 +11,7 @@ @class VENTokenField; @protocol VENAutocompleteTableViewManagerDelegate - +- (void)autocompleteManagerDidSelectValue:(NSString *)value; @end @interface VENAutocompleteTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate > diff --git a/VENAutocompleteTableViewManager.m b/VENAutocompleteTableViewManager.m index 4cbd4bd..676009b 100644 --- a/VENAutocompleteTableViewManager.m +++ b/VENAutocompleteTableViewManager.m @@ -26,6 +26,11 @@ - (instancetype)initWithTokenField:(VENTokenField *)tokenField return self; } +- (NSString *)valueForIndexPath:(NSIndexPath *)indexPath +{ + return self.options[indexPath.row]; +} + - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; @@ -44,14 +49,16 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"autocompleteCell"]; } - cell.textLabel.text = self.options[indexPath.row]; + cell.textLabel.text = [self valueForIndexPath:indexPath]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { -#warning implement this + [self.delegate autocompleteManagerDidSelectValue:[self valueForIndexPath:indexPath]]; + + [tableView deselectRowAtIndexPath:indexPath animated:NO]; } diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index 295684e..fa9daa4 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -125,6 +125,7 @@ - (void)reloadData [self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; self.scrollView.hidden = NO; [self removeGestureRecognizer:self.tapGestureRecognizer]; + [self.tableViewManager setAutocompleteOptions:nil]; self.tokens = [NSMutableArray array]; @@ -456,6 +457,14 @@ - (NSString *)collapsedText return @""; } +#pragma mark - VENAutocompleteTableViewManagerDelegate + +- (void)autocompleteManagerDidSelectValue:(NSString *)value +{ + if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { + [self.delegate tokenField:self didEnterText:value]; + } +} #pragma mark - UITextFieldDelegate From b06a6c8cf20500c95ba05673cf1766c934ab49ce Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 10:16:48 -0400 Subject: [PATCH 05/14] Redesign autocomplete API Instead of having an API that involves handing over a full array (that may be thousands of items long) I switched over to one more similar to a table view. I also broke out all of the autocomplete stuff to a separate protocol instead of expanding the current data source. --- VENAutocompleteTableViewManager.h | 5 ++--- VENAutocompleteTableViewManager.m | 18 +++--------------- VENTokenField/VENTokenField.h | 8 +++++++- VENTokenField/VENTokenField.m | 20 ++++++++++++-------- VENTokenFieldSample/ViewController.m | 15 ++++++++++++--- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/VENAutocompleteTableViewManager.h b/VENAutocompleteTableViewManager.h index 0ab8ac6..78f2aa2 100644 --- a/VENAutocompleteTableViewManager.h +++ b/VENAutocompleteTableViewManager.h @@ -9,6 +9,7 @@ #import @class VENTokenField; +@protocol VENTokenAutocompleteDataSource; @protocol VENAutocompleteTableViewManagerDelegate - (void)autocompleteManagerDidSelectValue:(NSString *)value; @@ -18,7 +19,7 @@ @property (strong, nonatomic) UITableView *tableView; @property (strong, nonatomic) VENTokenField *tokenField; -//@property (strong, nonatomic) NSArray *autocompleteOptions; +@property (assign, nonatomic) id dataSource; @property (assign, nonatomic) id delegate; - (instancetype)initWithTokenField:(VENTokenField *)tokenField; @@ -26,6 +27,4 @@ - (void)displayTableView; - (void)hideTableView; -- (void)setAutocompleteOptions:(NSArray *)autocompleteOptions; - @end diff --git a/VENAutocompleteTableViewManager.m b/VENAutocompleteTableViewManager.m index 676009b..85d1f28 100644 --- a/VENAutocompleteTableViewManager.m +++ b/VENAutocompleteTableViewManager.m @@ -28,7 +28,7 @@ - (instancetype)initWithTokenField:(VENTokenField *)tokenField - (NSString *)valueForIndexPath:(NSIndexPath *)indexPath { - return self.options[indexPath.row]; + return [self.dataSource tokenField:self.tokenField suggestionTitleForPartialText:self.tokenField.inputText atIndex:indexPath.row]; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView @@ -38,7 +38,7 @@ - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return self.options.count; + return [self.dataSource tokenField:self.tokenField numberOfSuggestionsForPartialText:self.tokenField.inputText]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath @@ -67,21 +67,9 @@ - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView [self.tokenField resignFirstResponder]; } -- (void)setAutocompleteOptions:(NSArray *)autocompleteOptions -{ - if (!self.options) { - [self displayTableView]; - } - self.options = autocompleteOptions; - if (autocompleteOptions != nil) { - [self.tableView reloadData]; - } else { - [self hideTableView]; - } -} - - (void)displayTableView { + [self.tableView reloadData]; [self.tokenField.window addSubview:self.tableView]; } diff --git a/VENTokenField/VENTokenField.h b/VENTokenField/VENTokenField.h index a6746f2..fddde57 100644 --- a/VENTokenField/VENTokenField.h +++ b/VENTokenField/VENTokenField.h @@ -36,8 +36,13 @@ - (NSString *)tokenField:(VENTokenField *)tokenField titleForTokenAtIndex:(NSUInteger)index; - (NSUInteger)numberOfTokensInTokenField:(VENTokenField *)tokenField; - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField; +@end + +@protocol VENTokenAutocompleteDataSource +@optional - (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField; -- (NSArray *)tokenField:(VENTokenField *)tokenField autocompleteTitlesForText:(NSString *)text; +- (NSInteger)tokenField:(VENTokenField *)tokenField numberOfSuggestionsForPartialText:(NSString *)text; +- (NSString *)tokenField:(VENTokenField *)tokenField suggestionTitleForPartialText:(NSString *)text atIndex:(NSInteger)index; @end @@ -45,6 +50,7 @@ @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +@property (weak, nonatomic) id autocompleteDataSource; - (void)reloadData; - (void)collapse; diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index fa9daa4..b4f85e2 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -125,7 +125,7 @@ - (void)reloadData [self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; self.scrollView.hidden = NO; [self removeGestureRecognizer:self.tapGestureRecognizer]; - [self.tableViewManager setAutocompleteOptions:nil]; + [self.tableViewManager hideTableView]; self.tokens = [NSMutableArray array]; @@ -176,6 +176,12 @@ - (void)setColorScheme:(UIColor *)color } } +- (void)setAutocompleteDataSource:(id)autocompleteDataSource +{ + _autocompleteDataSource = autocompleteDataSource; + self.tableViewManager.dataSource = autocompleteDataSource; +} + - (NSString *)inputText { return self.inputTextField.text; @@ -351,11 +357,9 @@ - (void)inputTextFieldDidChange:(UITextField *)textField } if ([self autocompletes]) { if (textField.text.length > 0) { - if ([self.dataSource respondsToSelector:@selector(tokenField:autocompleteTitlesForText:)]) { - self.tableViewManager.autocompleteOptions = [self.dataSource tokenField:self autocompleteTitlesForText:textField.text]; - } + [self.tableViewManager displayTableView]; } else { - self.tableViewManager.autocompleteOptions = nil; + [self.tableViewManager hideTableView]; } } @@ -443,8 +447,8 @@ - (NSUInteger)numberOfTokens - (BOOL)autocompletes { - if ([self.dataSource respondsToSelector:@selector(tokenFieldShouldPresentAutocompleteSelection:)]) { - return [self.dataSource tokenFieldShouldPresentAutocompleteSelection:self]; + if ([self.autocompleteDataSource respondsToSelector:@selector(tokenFieldShouldPresentAutocompleteSelection:)]) { + return [self.autocompleteDataSource tokenFieldShouldPresentAutocompleteSelection:self]; } return NO; } @@ -474,7 +478,7 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField if ([textField.text length]) { [self.delegate tokenField:self didEnterText:textField.text]; if ([self autocompletes]) { - self.tableViewManager.autocompleteOptions = nil; + [self.tableViewManager hideTableView]; } } } diff --git a/VENTokenFieldSample/ViewController.m b/VENTokenFieldSample/ViewController.m index 06e0b0e..184aac1 100644 --- a/VENTokenFieldSample/ViewController.m +++ b/VENTokenFieldSample/ViewController.m @@ -9,10 +9,11 @@ #import "ViewController.h" #import "VENTokenField.h" -@interface ViewController () +@interface ViewController () @property (weak, nonatomic) IBOutlet VENTokenField *tokenField; @property (strong, nonatomic) NSMutableArray *names; @property (strong, nonatomic) NSArray *knownNames; +@property (strong, nonatomic) NSArray *filteredNames; @end @implementation ViewController @@ -24,6 +25,7 @@ - (void)viewDidLoad self.knownNames = @[@"Ayaka", @"Mark", @"Neeraj", @"Octocat", @"Octavius", @"Ben"]; self.tokenField.delegate = self; self.tokenField.dataSource = self; + self.tokenField.autocompleteDataSource = self; self.tokenField.placeholderText = NSLocalizedString(@"Enter names here", nil); [self.tokenField setColorScheme:[UIColor colorWithRed:61/255.0f green:149/255.0f blue:206/255.0f alpha:1.0f]]; [self.tokenField becomeFirstResponder]; @@ -77,9 +79,16 @@ - (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField return YES; } -- (NSArray *)tokenField:(VENTokenField *)tokenField autocompleteTitlesForText:(NSString *)text +- (NSInteger)tokenField:(VENTokenField *)tokenField numberOfSuggestionsForPartialText:(NSString *)text { - return [self.knownNames filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH[c] %@", text]]; + self.filteredNames = [self.knownNames filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF BEGINSWITH[c] %@", text]]; + + return self.filteredNames.count; +} + +- (NSString *)tokenField:(VENTokenField *)tokenField suggestionTitleForPartialText:(NSString *)text atIndex:(NSInteger)index +{ + return self.filteredNames[index]; } @end From 62a97b2c4c96578a5185d4fe33b2c69449d10a33 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 10:27:51 -0400 Subject: [PATCH 06/14] Add seperate call back for autocomplete Rather than making the client distinguish whether the entered text came from a suggestion or not, we now have a delegate method for having selected a suggestion. This is fired in addition to tokenField:didEnterText: in order to allow users to opt in or to simply enable autocomplete and use the same delegate methods as before. --- VENTokenField/VENTokenField.h | 1 + VENTokenField/VENTokenField.m | 6 +++--- VENTokenFieldSample/ViewController.m | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/VENTokenField/VENTokenField.h b/VENTokenField/VENTokenField.h index fddde57..88e1ec4 100644 --- a/VENTokenField/VENTokenField.h +++ b/VENTokenField/VENTokenField.h @@ -26,6 +26,7 @@ @protocol VENTokenFieldDelegate @optional - (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text; +- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion; - (void)tokenField:(VENTokenField *)tokenField didDeleteTokenAtIndex:(NSUInteger)index; - (void)tokenField:(VENTokenField *)tokenField didChangeText:(NSString *)text; - (void)tokenFieldDidBeginEditing:(VENTokenField *)tokenField; diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index b4f85e2..8f21edb 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -468,6 +468,9 @@ - (void)autocompleteManagerDidSelectValue:(NSString *)value if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { [self.delegate tokenField:self didEnterText:value]; } + if ([self.delegate respondsToSelector:@selector(tokenField:didSelectSuggestion:)]) { + [self.delegate tokenField:self didSelectSuggestion:value]; + } } #pragma mark - UITextFieldDelegate @@ -477,9 +480,6 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { if ([textField.text length]) { [self.delegate tokenField:self didEnterText:textField.text]; - if ([self autocompletes]) { - [self.tableViewManager hideTableView]; - } } } return NO; diff --git a/VENTokenFieldSample/ViewController.m b/VENTokenFieldSample/ViewController.m index 184aac1..dd2488d 100644 --- a/VENTokenFieldSample/ViewController.m +++ b/VENTokenFieldSample/ViewController.m @@ -50,6 +50,11 @@ - (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text [self.tokenField reloadData]; } +- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion +{ + NSLog(@"Added value through autocomplete: %@", suggestion); +} + - (void)tokenField:(VENTokenField *)tokenField didDeleteTokenAtIndex:(NSUInteger)index { [self.names removeObjectAtIndex:index]; @@ -74,6 +79,8 @@ - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField return [NSString stringWithFormat:@"%lu people", [self.names count]]; } +#pragma mark - VENTokenAutocompleteDataSource + - (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField { return YES; From b7d49d3477d2425ae4cc3186fbb503f956ea331f Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 10:50:36 -0400 Subject: [PATCH 07/14] Update documentation with new autosuggest API --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 481f447..fc9506a 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,13 @@ Usage If you've ever used a ```UITableView```, using ```VENTokenField``` should be a breeze. -Similar to ```UITableView```, ```VENTokenField``` provides two protocols: `````` and ``````. +Similar to ```UITableView```, ```VENTokenField``` provides three protocols: ``````, `````` and ``````. ### VENTokenFieldDelegate This protocol notifies you when things happen in the token field that you might want to know about. -* ```tokenField:didEnterText:``` is called when a user hits the return key on the input field. +* ```tokenField:didEnterText:``` is called when a user hits the return key on the input field or after a suggestion is tapped.. +* ```tokenField:didSelectSuggestion:``` is called when a user taps on a suggested value in the suggestion list. * ```tokenField:didDeleteTokenAtIndex:``` is called when a user deletes a token at a particular index. * ```tokenField:didChangeText:``` is called when a user changes the text in the input field. * ```tokenFieldDidBeginEditing:``` is called when the input field becomes first responder. @@ -37,6 +38,14 @@ Implement... * ```numberOfTokensInTokenField:``` to specify how many tokens you have. * ```tokenFieldCollapsedText:``` to specify what you want the token field should say in the collapsed state. +### VENTokenAutocompleteDataSource +This entirely optional protocol allows you to provide info for the autosuggest feature. + +Implement... +* ```tokenFieldShouldPresentAutocompleteSelection:``` to specify that you want to use autocomplete in the token field. +* ```tokenField:numberOfSuggestionsForPartialText:``` to specify the number of suggestions for a given input. +* ```tokenField:suggestionTitleForPartialText:atIndex:``` to specify what the title for a suggestion at a particular index should be. + Sample Project -------------- Check out the [sample project](https://github.com/venmo/VENTokenField/tree/master/VENTokenFieldSample) in this repo for sample usage. From 12da455bc6a6f0e86a08ee124643bfb8ca6a5d0e Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 10:58:46 -0400 Subject: [PATCH 08/14] Update tests to leave the field in a good state --- VENTokenFieldSampleTests/VENTokenFieldSampleTests.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m index 957e77c..efb9071 100644 --- a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m +++ b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m @@ -38,10 +38,12 @@ - (void)testBasicFlow [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Octocat,"]; } -- (void)testAutocompletePresence +- (void)testAutocompleteFlow { [tester enterTextIntoCurrentFirstResponder:@"Be"]; [tester waitForViewWithAccessibilityLabel:@"Ben"]; + [tester tapViewWithAccessibilityLabel:@"Ben"]; + [tester waitForViewWithAccessibilityLabel:@"Ben,"]; } - (void)testResignFirstResponder From 144aaf8c9b49cea957b67f2ce58f21c2b188d290 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 11:13:27 -0400 Subject: [PATCH 09/14] Switch from autocomplete to suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit autocomplete has a bad connotation, and isn’t really what we’re looking for in this case. This is really just a suggestion engine. --- README.md | 8 ++--- VENAutocompleteTableViewManager.h | 30 ------------------- VENSuggestionTableViewManager.h | 30 +++++++++++++++++++ ...nager.m => VENSuggestionTableViewManager.m | 16 +++++----- VENTokenField/VENTokenField.h | 6 ++-- VENTokenField/VENTokenField.m | 28 ++++++++--------- VENTokenFieldSample/ViewController.m | 10 +++---- .../VENTokenFieldSampleTests.m | 2 +- 8 files changed, 65 insertions(+), 65 deletions(-) delete mode 100644 VENAutocompleteTableViewManager.h create mode 100644 VENSuggestionTableViewManager.h rename VENAutocompleteTableViewManager.m => VENSuggestionTableViewManager.m (85%) diff --git a/README.md b/README.md index fc9506a..5e2abb4 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Usage If you've ever used a ```UITableView```, using ```VENTokenField``` should be a breeze. -Similar to ```UITableView```, ```VENTokenField``` provides three protocols: ``````, `````` and ``````. +Similar to ```UITableView```, ```VENTokenField``` provides three protocols: ``````, `````` and ``````. ### VENTokenFieldDelegate This protocol notifies you when things happen in the token field that you might want to know about. @@ -38,11 +38,11 @@ Implement... * ```numberOfTokensInTokenField:``` to specify how many tokens you have. * ```tokenFieldCollapsedText:``` to specify what you want the token field should say in the collapsed state. -### VENTokenAutocompleteDataSource -This entirely optional protocol allows you to provide info for the autosuggest feature. +### VENTokenSuggestionDataSource +This entirely optional protocol allows you to provide info for any suggestions presented to the user. Implement... -* ```tokenFieldShouldPresentAutocompleteSelection:``` to specify that you want to use autocomplete in the token field. +* ```tokenFieldShouldPresentSuggestions:``` to specify that you want to present suggested values for tokens. * ```tokenField:numberOfSuggestionsForPartialText:``` to specify the number of suggestions for a given input. * ```tokenField:suggestionTitleForPartialText:atIndex:``` to specify what the title for a suggestion at a particular index should be. diff --git a/VENAutocompleteTableViewManager.h b/VENAutocompleteTableViewManager.h deleted file mode 100644 index 78f2aa2..0000000 --- a/VENAutocompleteTableViewManager.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// VENAutocompleteTableViewManager.h -// Pods -// -// Created by bnicholas on 8/8/14. -// -// - -#import - -@class VENTokenField; -@protocol VENTokenAutocompleteDataSource; - -@protocol VENAutocompleteTableViewManagerDelegate -- (void)autocompleteManagerDidSelectValue:(NSString *)value; -@end - -@interface VENAutocompleteTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate > - -@property (strong, nonatomic) UITableView *tableView; -@property (strong, nonatomic) VENTokenField *tokenField; -@property (assign, nonatomic) id dataSource; -@property (assign, nonatomic) id delegate; - -- (instancetype)initWithTokenField:(VENTokenField *)tokenField; - -- (void)displayTableView; -- (void)hideTableView; - -@end diff --git a/VENSuggestionTableViewManager.h b/VENSuggestionTableViewManager.h new file mode 100644 index 0000000..b8c1152 --- /dev/null +++ b/VENSuggestionTableViewManager.h @@ -0,0 +1,30 @@ +// +// VENSuggestionTableViewManager.h +// Pods +// +// Created by Ben Nicholas on 8/8/14. +// +// + +#import + +@class VENTokenField; +@protocol VENTokenSuggestionDataSource; + +@protocol VENSuggestionTableViewManagerDelegate +- (void)suggestionManagerDidSelectValue:(NSString *)value; +@end + +@interface VENSuggestionTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate > + +@property (strong, nonatomic) UITableView *tableView; +@property (strong, nonatomic) VENTokenField *tokenField; +@property (assign, nonatomic) id dataSource; +@property (assign, nonatomic) id delegate; + +- (instancetype)initWithTokenField:(VENTokenField *)tokenField; + +- (void)displayTableView; +- (void)hideTableView; + +@end diff --git a/VENAutocompleteTableViewManager.m b/VENSuggestionTableViewManager.m similarity index 85% rename from VENAutocompleteTableViewManager.m rename to VENSuggestionTableViewManager.m index 85d1f28..8835dc3 100644 --- a/VENAutocompleteTableViewManager.m +++ b/VENSuggestionTableViewManager.m @@ -1,21 +1,21 @@ // -// VENAutocompleteTableViewManager.m +// VENSuggestionTableViewManager // Pods // -// Created by bnicholas on 8/8/14. +// Created by Ben Nicholas on 8/8/14. // // -#import "VENAutocompleteTableViewManager.h" +#import "VENSuggestionTableViewManager.h" #import "VENTokenField.h" -@interface VENAutocompleteTableViewManager () +@interface VENSuggestionTableViewManager () @property (nonatomic, strong) NSArray *options; @end -@implementation VENAutocompleteTableViewManager +@implementation VENSuggestionTableViewManager - (instancetype)initWithTokenField:(VENTokenField *)tokenField { @@ -43,10 +43,10 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"autocompleteCell"]; + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"suggestionCell"]; if (!cell) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"autocompleteCell"]; + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"suggestionCell"]; } cell.textLabel.text = [self valueForIndexPath:indexPath]; @@ -56,7 +56,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self.delegate autocompleteManagerDidSelectValue:[self valueForIndexPath:indexPath]]; + [self.delegate suggestionManagerDidSelectValue:[self valueForIndexPath:indexPath]]; [tableView deselectRowAtIndexPath:indexPath animated:NO]; } diff --git a/VENTokenField/VENTokenField.h b/VENTokenField/VENTokenField.h index 88e1ec4..6321027 100644 --- a/VENTokenField/VENTokenField.h +++ b/VENTokenField/VENTokenField.h @@ -39,9 +39,9 @@ - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField; @end -@protocol VENTokenAutocompleteDataSource +@protocol VENTokenSuggestionDataSource @optional -- (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField; +- (BOOL)tokenFieldShouldPresentSuggestions:(VENTokenField *)tokenField; - (NSInteger)tokenField:(VENTokenField *)tokenField numberOfSuggestionsForPartialText:(NSString *)text; - (NSString *)tokenField:(VENTokenField *)tokenField suggestionTitleForPartialText:(NSString *)text atIndex:(NSInteger)index; @end @@ -51,7 +51,7 @@ @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; -@property (weak, nonatomic) id autocompleteDataSource; +@property (weak, nonatomic) id suggestionDataSource; - (void)reloadData; - (void)collapse; diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index 8f21edb..8e39d3b 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -25,7 +25,7 @@ #import #import "VENToken.h" #import "VENBackspaceTextField.h" -#import "VENAutocompleteTableViewManager.h" +#import "VENSuggestionTableViewManager.h" static const CGFloat VENTokenFieldDefaultVerticalInset = 7.0; static const CGFloat VENTokenFieldDefaultHorizontalInset = 15.0; @@ -35,7 +35,7 @@ static const CGFloat VENTokenFieldDefaultMaxHeight = 150.0; -@interface VENTokenField () +@interface VENTokenField () @property (strong, nonatomic) UIScrollView *scrollView; @property (strong, nonatomic) NSMutableArray *tokens; @@ -45,7 +45,7 @@ @interface VENTokenField () )autocompleteDataSource +- (void)setSuggestionDataSource:(id)suggestionDataSource { - _autocompleteDataSource = autocompleteDataSource; - self.tableViewManager.dataSource = autocompleteDataSource; + _suggestionDataSource = suggestionDataSource; + self.tableViewManager.dataSource = suggestionDataSource; } - (NSString *)inputText @@ -355,7 +355,7 @@ - (void)inputTextFieldDidChange:(UITextField *)textField if ([self.delegate respondsToSelector:@selector(tokenField:didChangeText:)]) { [self.delegate tokenField:self didChangeText:textField.text]; } - if ([self autocompletes]) { + if ([self suggests]) { if (textField.text.length > 0) { [self.tableViewManager displayTableView]; } else { @@ -365,10 +365,10 @@ - (void)inputTextFieldDidChange:(UITextField *)textField } } -- (VENAutocompleteTableViewManager *)tableViewManager +- (VENSuggestionTableViewManager *)tableViewManager { if (!_tableViewManager) { - _tableViewManager = [[VENAutocompleteTableViewManager alloc] initWithTokenField:self]; + _tableViewManager = [[VENSuggestionTableViewManager alloc] initWithTokenField:self]; _tableViewManager.delegate = self; } return _tableViewManager; @@ -445,10 +445,10 @@ - (NSUInteger)numberOfTokens return 0; } -- (BOOL)autocompletes +- (BOOL)suggests { - if ([self.autocompleteDataSource respondsToSelector:@selector(tokenFieldShouldPresentAutocompleteSelection:)]) { - return [self.autocompleteDataSource tokenFieldShouldPresentAutocompleteSelection:self]; + if ([self.suggestionDataSource respondsToSelector:@selector(tokenFieldShouldPresentSuggestions:)]) { + return [self.suggestionDataSource tokenFieldShouldPresentSuggestions:self]; } return NO; } @@ -461,9 +461,9 @@ - (NSString *)collapsedText return @""; } -#pragma mark - VENAutocompleteTableViewManagerDelegate +#pragma mark - VENSuggestionTableViewManagerDelegate -- (void)autocompleteManagerDidSelectValue:(NSString *)value +- (void)suggestionManagerDidSelectValue:(NSString *)value { if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { [self.delegate tokenField:self didEnterText:value]; diff --git a/VENTokenFieldSample/ViewController.m b/VENTokenFieldSample/ViewController.m index dd2488d..19ec862 100644 --- a/VENTokenFieldSample/ViewController.m +++ b/VENTokenFieldSample/ViewController.m @@ -9,7 +9,7 @@ #import "ViewController.h" #import "VENTokenField.h" -@interface ViewController () +@interface ViewController () @property (weak, nonatomic) IBOutlet VENTokenField *tokenField; @property (strong, nonatomic) NSMutableArray *names; @property (strong, nonatomic) NSArray *knownNames; @@ -25,7 +25,7 @@ - (void)viewDidLoad self.knownNames = @[@"Ayaka", @"Mark", @"Neeraj", @"Octocat", @"Octavius", @"Ben"]; self.tokenField.delegate = self; self.tokenField.dataSource = self; - self.tokenField.autocompleteDataSource = self; + self.tokenField.suggestionDataSource = self; self.tokenField.placeholderText = NSLocalizedString(@"Enter names here", nil); [self.tokenField setColorScheme:[UIColor colorWithRed:61/255.0f green:149/255.0f blue:206/255.0f alpha:1.0f]]; [self.tokenField becomeFirstResponder]; @@ -52,7 +52,7 @@ - (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text - (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion { - NSLog(@"Added value through autocomplete: %@", suggestion); + NSLog(@"Added suggested value: %@", suggestion); } - (void)tokenField:(VENTokenField *)tokenField didDeleteTokenAtIndex:(NSUInteger)index @@ -79,9 +79,9 @@ - (NSString *)tokenFieldCollapsedText:(VENTokenField *)tokenField return [NSString stringWithFormat:@"%lu people", [self.names count]]; } -#pragma mark - VENTokenAutocompleteDataSource +#pragma mark - VENTokenSuggestionDataSource -- (BOOL)tokenFieldShouldPresentAutocompleteSelection:(VENTokenField *)tokenField +- (BOOL)tokenFieldShouldPresentSuggestions:(VENTokenField *)tokenField { return YES; } diff --git a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m index efb9071..a5732b2 100644 --- a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m +++ b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m @@ -38,7 +38,7 @@ - (void)testBasicFlow [tester waitForAbsenceOfViewWithAccessibilityLabel:@"Octocat,"]; } -- (void)testAutocompleteFlow +- (void)testSuggestionFlow { [tester enterTextIntoCurrentFirstResponder:@"Be"]; [tester waitForViewWithAccessibilityLabel:@"Ben"]; From 3ce200b0a0c902e782a6f246a0905adbf88c4d0d Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 13:56:25 -0400 Subject: [PATCH 10/14] Move suggestion files into the appropriate folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Xcode dropped them in the wrong folder and I didn’t notice that until now. Fixing it… --- .../VENSuggestionTableViewManager.h | 0 .../VENSuggestionTableViewManager.m | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename VENSuggestionTableViewManager.h => VENTokenField/VENSuggestionTableViewManager.h (100%) rename VENSuggestionTableViewManager.m => VENTokenField/VENSuggestionTableViewManager.m (100%) diff --git a/VENSuggestionTableViewManager.h b/VENTokenField/VENSuggestionTableViewManager.h similarity index 100% rename from VENSuggestionTableViewManager.h rename to VENTokenField/VENSuggestionTableViewManager.h diff --git a/VENSuggestionTableViewManager.m b/VENTokenField/VENSuggestionTableViewManager.m similarity index 100% rename from VENSuggestionTableViewManager.m rename to VENTokenField/VENSuggestionTableViewManager.m From cd7ea56b568fc6599ba79a859a22cb7ebef0230e Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 15:05:44 -0400 Subject: [PATCH 11/14] Rename test to fix order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit XCTest runners test in what appears to be alphabetical order, or something close to it. Because of the naming, this test runs after resignFirstResponder, which leaves the system in a state where that test’s preconditions aren’t met - the keyboard is not up and ready to input in the text field. By changing from assuming the first responder to entering into the known good field, we stop worrying about order. I also applied this fix to the testBasicFlow test, which had to be broken into two steps due to the token field clearing properties. --- VENTokenFieldSampleTests/VENTokenFieldSampleTests.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m index a5732b2..000e0ef 100644 --- a/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m +++ b/VENTokenFieldSampleTests/VENTokenFieldSampleTests.m @@ -16,7 +16,8 @@ @implementation VENTokenFieldSampleTests - (void)testBasicFlow { - [tester enterTextIntoCurrentFirstResponder:@"Ayaka\n"]; + [tester enterText:@"Ayaka" intoViewWithAccessibilityLabel:@"To"]; + [tester enterTextIntoCurrentFirstResponder:@"\n"]; [tester waitForViewWithAccessibilityLabel:@"Ayaka,"]; [tester enterTextIntoCurrentFirstResponder:@"Mark\n"]; @@ -40,7 +41,7 @@ - (void)testBasicFlow - (void)testSuggestionFlow { - [tester enterTextIntoCurrentFirstResponder:@"Be"]; + [tester enterText:@"Be" intoViewWithAccessibilityLabel:@"To"]; [tester waitForViewWithAccessibilityLabel:@"Ben"]; [tester tapViewWithAccessibilityLabel:@"Ben"]; [tester waitForViewWithAccessibilityLabel:@"Ben,"]; From d88eea094fd55cc5314de96949929b357a46748c Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Tue, 12 Aug 2014 15:14:51 -0400 Subject: [PATCH 12/14] Update target frame of the table view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to translate the frame of the tokenView into our destination view’s(the window) coordinates to keep the view properly aligned. Luckily, convertRect:toView: exists and does this hard work for us! --- VENTokenField/VENSuggestionTableViewManager.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/VENTokenField/VENSuggestionTableViewManager.m b/VENTokenField/VENSuggestionTableViewManager.m index 8835dc3..a66d508 100644 --- a/VENTokenField/VENSuggestionTableViewManager.m +++ b/VENTokenField/VENSuggestionTableViewManager.m @@ -80,10 +80,11 @@ - (void)hideTableView - (UITableView *)tableView { - CGRect newFrame = CGRectMake(CGRectGetMinX(self.tokenField.frame), - CGRectGetMaxY(self.tokenField.frame), - CGRectGetWidth(self.tokenField.frame), - CGRectGetHeight(self.tokenField.window.frame) - CGRectGetMaxY(self.tokenField.frame)); + CGRect globalTokenViewFrame = [self.tokenField convertRect:self.tokenField.bounds toView:self.tokenField.window]; + CGRect newFrame = CGRectMake(CGRectGetMinX(globalTokenViewFrame), + CGRectGetMaxY(globalTokenViewFrame), + CGRectGetWidth(globalTokenViewFrame), + CGRectGetHeight(self.tokenField.window.frame) - CGRectGetMaxY(globalTokenViewFrame)); if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:newFrame style:UITableViewStylePlain]; From cc352dfb7c241bc805a74b49f931c34bcad7d416 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Wed, 13 Aug 2014 14:44:55 -0400 Subject: [PATCH 13/14] Add additional information to didSelectSuggestion: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in real use, tokenField:didSelectSuggestion: didn’t provide the necessary information to map back to a list of full data objects without a lot of inefficient searching. This adds some information back to allow easy indexing into an underlying data store to avoid this pitfall. --- VENTokenField/VENSuggestionTableViewManager.h | 2 +- VENTokenField/VENSuggestionTableViewManager.m | 2 +- VENTokenField/VENTokenField.h | 2 +- VENTokenField/VENTokenField.m | 7 ++++--- VENTokenFieldSample/ViewController.m | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/VENTokenField/VENSuggestionTableViewManager.h b/VENTokenField/VENSuggestionTableViewManager.h index b8c1152..baadd65 100644 --- a/VENTokenField/VENSuggestionTableViewManager.h +++ b/VENTokenField/VENSuggestionTableViewManager.h @@ -12,7 +12,7 @@ @protocol VENTokenSuggestionDataSource; @protocol VENSuggestionTableViewManagerDelegate -- (void)suggestionManagerDidSelectValue:(NSString *)value; +- (void)suggestionManagerDidSelectValue:(NSString *)value atIndex:(NSInteger)index; @end @interface VENSuggestionTableViewManager : NSObject < UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate > diff --git a/VENTokenField/VENSuggestionTableViewManager.m b/VENTokenField/VENSuggestionTableViewManager.m index a66d508..697eb35 100644 --- a/VENTokenField/VENSuggestionTableViewManager.m +++ b/VENTokenField/VENSuggestionTableViewManager.m @@ -56,7 +56,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self.delegate suggestionManagerDidSelectValue:[self valueForIndexPath:indexPath]]; + [self.delegate suggestionManagerDidSelectValue:[self valueForIndexPath:indexPath] atIndex:indexPath.row]; [tableView deselectRowAtIndexPath:indexPath animated:NO]; } diff --git a/VENTokenField/VENTokenField.h b/VENTokenField/VENTokenField.h index 6321027..10e9363 100644 --- a/VENTokenField/VENTokenField.h +++ b/VENTokenField/VENTokenField.h @@ -26,7 +26,7 @@ @protocol VENTokenFieldDelegate @optional - (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text; -- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion; +- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion forPartialText:(NSString *)text atIndex:(NSInteger) index; - (void)tokenField:(VENTokenField *)tokenField didDeleteTokenAtIndex:(NSUInteger)index; - (void)tokenField:(VENTokenField *)tokenField didChangeText:(NSString *)text; - (void)tokenFieldDidBeginEditing:(VENTokenField *)tokenField; diff --git a/VENTokenField/VENTokenField.m b/VENTokenField/VENTokenField.m index 8e39d3b..cbaa03d 100644 --- a/VENTokenField/VENTokenField.m +++ b/VENTokenField/VENTokenField.m @@ -463,13 +463,14 @@ - (NSString *)collapsedText #pragma mark - VENSuggestionTableViewManagerDelegate -- (void)suggestionManagerDidSelectValue:(NSString *)value +- (void)suggestionManagerDidSelectValue:(NSString *)value atIndex:(NSInteger)index { + NSString *fieldText = self.inputText; if ([self.delegate respondsToSelector:@selector(tokenField:didEnterText:)]) { [self.delegate tokenField:self didEnterText:value]; } - if ([self.delegate respondsToSelector:@selector(tokenField:didSelectSuggestion:)]) { - [self.delegate tokenField:self didSelectSuggestion:value]; + if ([self.delegate respondsToSelector:@selector(tokenField:didSelectSuggestion:forPartialText:atIndex:)]) { + [self.delegate tokenField:self didSelectSuggestion:value forPartialText:fieldText atIndex:index]; } } diff --git a/VENTokenFieldSample/ViewController.m b/VENTokenFieldSample/ViewController.m index 19ec862..c124748 100644 --- a/VENTokenFieldSample/ViewController.m +++ b/VENTokenFieldSample/ViewController.m @@ -50,7 +50,7 @@ - (void)tokenField:(VENTokenField *)tokenField didEnterText:(NSString *)text [self.tokenField reloadData]; } -- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion +- (void)tokenField:(VENTokenField *)tokenField didSelectSuggestion:(NSString *)suggestion forPartialText:(NSString *)text atIndex:(NSInteger)index { NSLog(@"Added suggested value: %@", suggestion); } From 8691525f6d5e52df17724fd59831b4c95ff9a125 Mon Sep 17 00:00:00 2001 From: Ben Nicholas Date: Wed, 13 Aug 2014 14:47:24 -0400 Subject: [PATCH 14/14] Updating readme with the new delegate method --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e2abb4..665f1b1 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Similar to ```UITableView```, ```VENTokenField``` provides three protocols: ```< This protocol notifies you when things happen in the token field that you might want to know about. * ```tokenField:didEnterText:``` is called when a user hits the return key on the input field or after a suggestion is tapped.. -* ```tokenField:didSelectSuggestion:``` is called when a user taps on a suggested value in the suggestion list. +* ```tokenField:didSelectSuggestion:forPartialText:atIndex:``` is called when a user taps on a suggested value in the suggestion list. * ```tokenField:didDeleteTokenAtIndex:``` is called when a user deletes a token at a particular index. * ```tokenField:didChangeText:``` is called when a user changes the text in the input field. * ```tokenFieldDidBeginEditing:``` is called when the input field becomes first responder.