diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBar/ACEAutocompleteBar.h b/ACEAutocompleteBar/ACEAutocompleteBar.h old mode 100644 new mode 100755 index 5130f8b..65aa6f6 --- a/ACEAutocompleteBar/ACEAutocompleteBar.h +++ b/ACEAutocompleteBar/ACEAutocompleteBar.h @@ -20,6 +20,29 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +/* + Jimmy Jose + Added : + + - Extended support for UITextView + - Extended support for every word in the sentence + - Retain cursor position after selecting suggestion + - Removed the need for implementing delegates for input field + - Added ignoreCase option to ignore case of the input and suggestion list + - Added property separatorColor to customizeView + - Added dataSourceContent to do the matching internally [can be made smooth, probably in next update] + - Added support to hide suggestion list on single tap or if selection changed + - Added clearButton to dismiss the suggestion list + - a) Set custom image for clear button + - b) Disable clear button + - Added 'text' NSString object in textfield delegate for result manipulation + - Added 'range' UITextRange object in textfield delegate for cursor manipulation + - Added 'text' NSString object in textview delegate for result manipulation + - Added 'range' NSRange type in textview delegate for cursor manipulation + + */ + + #import @@ -34,10 +57,13 @@ #pragma mark - -@protocol ACEAutocompleteDelegate +@protocol ACEAutocompleteDelegate +@optional // called when the user tap on one of the suggestions -- (void)textField:(UITextField *)textField didSelectObject:(id)object inInputView:(ACEAutocompleteInputView *)inputView; +- (void)textField:(UITextField *)textField didSelectObject:(id)object inInputView:(ACEAutocompleteInputView *)inputView newTextForInputField:(NSString *)text withRange:(UITextRange *)range; + +- (void)textView:(UITextView *)textView didSelectObject:(id)object inInputView:(ACEAutocompleteInputView *)inputView newTextForInputField:(NSString *)text withRange:(NSRange)range; @end @@ -49,7 +75,7 @@ - (NSUInteger)minimumCharactersToTrigger:(ACEAutocompleteInputView *)inputView; // use the block to return the array of items asynchronously based on the query string -- (void)inputView:(ACEAutocompleteInputView *)inputView itemsFor:(NSString *)query result:(void (^)(NSArray *items))resultBlock; +- (void)inputView:(ACEAutocompleteInputView *)inputView itemsFor:(NSString *)query ignoreCase:(BOOL)ignoreCase withResult:(NSArray *)resultArray result:(void (^)(NSArray *items))resultBlock; @optional @@ -66,5 +92,6 @@ #import "ACEAutocompleteInputView.h" #import "UITextField+ACEAutocompleteBar.h" +#import "UITextView+ACEAutocompleteBar.h" diff --git a/ACEAutocompleteBar/ACEAutocompleteInputView.h b/ACEAutocompleteBar/ACEAutocompleteInputView.h old mode 100644 new mode 100755 index 08ff334..322de65 --- a/ACEAutocompleteBar/ACEAutocompleteInputView.h +++ b/ACEAutocompleteBar/ACEAutocompleteInputView.h @@ -23,19 +23,24 @@ #import -@interface ACEAutocompleteInputView : UIView +@interface ACEAutocompleteInputView : UIView @property (nonatomic, assign) UITextField *textField; +@property (nonatomic, assign) UITextView *textView; +@property (nonatomic, assign) BOOL ignoreCase; +@property (nonatomic, assign) NSArray *dataSourceContent; // delegate @property (nonatomic, assign) id delegate; @property (nonatomic, assign) id dataSource; // customization (ignored when the optional methods of the data source are implemeted) -@property (nonatomic, strong) UIFont * font; +@property (nonatomic, strong) UIFont * font; @property (nonatomic, strong) UIColor * textColor; +@property (nonatomic, strong) UIColor * separatorColor; - (id)initWithHeight:(CGFloat)height; +-(id)initWithClearButtonImage:(UIImage *)clearButtonImage andShouldShowClearButton:(BOOL)shouldShowClearButton; - (void)show:(BOOL)show withAnimation:(BOOL)animated; diff --git a/ACEAutocompleteBar/ACEAutocompleteInputView.m b/ACEAutocompleteBar/ACEAutocompleteInputView.m old mode 100644 new mode 100755 index bdac4c0..fb48e05 --- a/ACEAutocompleteBar/ACEAutocompleteInputView.m +++ b/ACEAutocompleteBar/ACEAutocompleteInputView.m @@ -32,6 +32,14 @@ @interface ACEAutocompleteInputView () @property (nonatomic, strong) NSArray *suggestionList; @property (nonatomic, strong) UITableView *suggestionListView; +@property (nonatomic, retain) NSString *leftString; +@property (nonatomic, retain) NSString *rightString; +@property (nonatomic, assign) NSRange range; +@property (nonatomic ,strong) UIView *clearListView; +@property (nonatomic, retain) UIImage *clearButtonImage; +@property (nonatomic, assign) BOOL shouldShowClearButton; +@property (nonatomic, assign) int paddingForClearButton; + @end #pragma mark - @@ -43,14 +51,41 @@ - (id)init return [self initWithHeight:kDefaultHeight]; } +-(id)initWithClearButtonImage:(UIImage *)clearButtonImage andShouldShowClearButton:(BOOL)shouldShowClearButton{ + + _shouldShowClearButton = shouldShowClearButton; + _clearButtonImage = clearButtonImage; + + _paddingForClearButton = 0; + + if (_shouldShowClearButton) { + + _paddingForClearButton = kDefaultHeight * 2; + self = [self initWithHeight:kDefaultHeight]; + [self addHideViewButton]; + } + else + self = [self initWithHeight:kDefaultHeight]; + + return self; +} + - (id)initWithHeight:(CGFloat)height { self = [super initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, height)]; if (self) { // create the table view with the suggestions - _suggestionListView = [[UITableView alloc] initWithFrame:CGRectMake((self.bounds.size.width - self.bounds.size.height) / 2, - (self.bounds.size.height - self.bounds.size.width) / 2, - self.bounds.size.height, self.bounds.size.width)]; + + CGFloat xCord = (self.bounds.size.width - self.bounds.size.height + _paddingForClearButton) / 2; + + CGFloat yCord = (self.bounds.size.height - self.bounds.size.width) / 2; + + CGFloat width = self.bounds.size.height; + + CGFloat height = self.bounds.size.width; + + CGRect frame = CGRectMake(xCord, yCord, width, height); + _suggestionListView = [[UITableView alloc] initWithFrame:frame]; // init the bar the hidden state self.hidden = YES; @@ -66,14 +101,47 @@ - (id)initWithHeight:(CGFloat)height _suggestionListView.delegate = self; // clean the rest of separators - _suggestionListView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f)]; + _suggestionListView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 1.0f, 1.0f+_paddingForClearButton/2)]; // add the table as subview + [self addSubview:_suggestionListView]; + } return self; } +-(void)addHideViewButton{ + + CGRect frame = CGRectMake(0.0f, 0.0f, kDefaultHeight, kDefaultHeight); + _clearListView = [[UIView alloc] initWithFrame:frame]; + [_clearListView setBackgroundColor:[UIColor clearColor]]; + [self addSubview:_clearListView]; + + UIButton *clearButton = [UIButton buttonWithType:UIButtonTypeCustom]; + + + if (_clearButtonImage) { + [clearButton setBackgroundImage:_clearButtonImage forState:UIControlStateNormal]; + }else{ + + UIImage *buttonImage = [UIImage imageNamed:@"btn_clear_white"]; + [clearButton setBackgroundImage:buttonImage forState:UIControlStateNormal]; + + } + + [clearButton addTarget:self action:@selector(hideBar) forControlEvents:UIControlEventTouchUpInside]; + + [clearButton setFrame:frame]; + [clearButton setBackgroundColor:[UIColor clearColor]]; + + [_clearListView addSubview:clearButton]; + + + +} + + - (void)show:(BOOL)show withAnimation:(BOOL)animated { if (show && self.hidden) { @@ -114,6 +182,16 @@ - (UIColor *)textColor return _textColor; } +- (UIColor *)separatorColor +{ + if (_separatorColor == nil) { + _separatorColor = [UIColor whiteColor]; + } + return _separatorColor; +} + + + #pragma makr - Helpers - (NSString *)stringForObjectAtIndex:(NSUInteger)index @@ -124,7 +202,7 @@ - (NSString *)stringForObjectAtIndex:(NSUInteger)index } else if ([object isKindOfClass:[NSString class]]) { return object; - + } else { return nil; } @@ -133,8 +211,21 @@ - (NSString *)stringForObjectAtIndex:(NSUInteger)index #pragma mark - Text Field Delegate +- (BOOL)canBecomeFirstResponder +{ + [self hideBar]; + + return NO; +} + + - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { + [self hideBar]; + [textField addTarget:self + action:@selector(hideBar) + forControlEvents:UIControlEventTouchDown]; + if ([self.delegate respondsToSelector:_cmd]) { return [self.delegate textFieldShouldBeginEditing:textField]; } @@ -150,7 +241,6 @@ - (void)textFieldDidBeginEditing:(UITextField *)textField - (BOOL)textFieldShouldEndEditing:(UITextField *)textField { - [self show: NO withAnimation: NO]; if ([self.delegate respondsToSelector:_cmd]) { return [self.delegate textFieldShouldEndEditing:textField]; } @@ -166,17 +256,11 @@ - (void)textFieldDidEndEditing:(UITextField *)textField - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { - NSString * query = [textField.text stringByReplacingCharactersInRange:range withString:string]; - if (query.length >= [self.dataSource minimumCharactersToTrigger:self]) { - [self.dataSource inputView:self itemsFor:query result:^(NSArray *items) { - self.suggestionList = items; - [self.suggestionListView reloadData]; - }]; - - } else { - self.suggestionList = nil; - [self.suggestionListView reloadData]; - } + + NSString *inputFieldText = textField.text; + + if ([self shouldShowBarForText:string]) [self showAutocompleteBarForInputFieldText:inputFieldText changeText:string andRange:range]; + else [self show:NO withAnimation:YES]; if ([self.delegate respondsToSelector:_cmd]) { return [self.delegate textField:textField shouldChangeCharactersInRange:range replacementString:string]; @@ -201,16 +285,78 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField return NO; } +- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{ + + [self hideBar]; + + if ([self.delegate respondsToSelector:_cmd]) { + return [self.delegate textViewShouldBeginEditing:textView]; + } + return YES; + +} +- (BOOL)textViewShouldEndEditing:(UITextView *)textView{ + + if ([self.delegate respondsToSelector:_cmd]) { + return [self.delegate textViewShouldEndEditing:textView]; + } + return YES; +} + +- (void)textViewDidBeginEditing:(UITextView *)textView{ + + if ([self.delegate respondsToSelector:_cmd]) { + [self.delegate textViewDidBeginEditing:textView]; + } +} +- (void)textViewDidEndEditing:(UITextView *)textView{ + + if ([self.delegate respondsToSelector:_cmd]) { + [self.delegate textViewDidEndEditing:textView]; + } +} + +- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ + + NSString *inputFieldText = textView.text; + if ([self shouldShowBarForText:text]) [self showAutocompleteBarForInputFieldText:inputFieldText changeText:text andRange:range]; + else [self show:NO withAnimation:YES]; + + if ([self.delegate respondsToSelector:_cmd]) { + return [self.delegate textView:textView shouldChangeTextInRange:range replacementText:text]; + } + + return YES; +} +- (void)textViewDidChange:(UITextView *)textView{ + + if ([self.delegate respondsToSelector:_cmd]) { + return [self.delegate textViewDidChange:textView]; + } + +} + +- (void)textViewDidChangeSelection:(UITextView *)textView{ + + [self hideBar]; + if ([self.delegate respondsToSelector:_cmd]) { + return [self.delegate textViewDidChangeSelection:textView]; + } +} + #pragma mark - Table View Delegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + //if (indexPath.row == 0) return 35 + (kDefaultMargin * 2) + 1.0f; + //int idx = indexPath.row - 1; + int idx = indexPath.row; if ([self.dataSource respondsToSelector:@selector(inputView:widthForObject:)]) { - return [self.dataSource inputView:self widthForObject:[self.suggestionList objectAtIndex:indexPath.row]]; + return [self.dataSource inputView:self widthForObject:[self.suggestionList objectAtIndex:idx]]; } else { - NSString * string = [self stringForObjectAtIndex:indexPath.row]; + NSString * string = [self stringForObjectAtIndex:idx]; CGFloat width = [string sizeWithFont:self.font constrainedToSize:self.frame.size].width; if (width == 0) { // bigger than the screen @@ -224,11 +370,50 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [self.delegate textField:self.textField didSelectObject:[self.suggestionList objectAtIndex:indexPath.row] inInputView:self]; + /* + if (indexPath.row == 0) { + [self hideBar]; + return; + } + + + NSString *selectedWord = [self.suggestionList objectAtIndex:indexPath.row-1]; + */ + NSString *selectedWord = [self.suggestionList objectAtIndex:indexPath.row]; + NSString *text = nil; + if (_leftString) text = _leftString; + + if (_rightString) + text = [[text stringByAppendingFormat:@" %@%@",selectedWord,_rightString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + else + text = selectedWord; + + //Retain the cursor position + _range = NSMakeRange(_range.location+selectedWord.length, 0); + + UITextRange *textRange = [self calculateSelectionRangeForTextFieldWithText:text]; + if ([self.delegate respondsToSelector:@selector(textField:didSelectObject:inInputView:newTextForInputField:withRange:)]) { + + [self.delegate textField:self.textField didSelectObject:selectedWord inInputView:self newTextForInputField:text withRange:textRange]; + + } + + self.textView.text = text; + [self.textView setSelectedRange:_range]; + + if ([self.delegate respondsToSelector:@selector(textView:didSelectObject:inInputView:newTextForInputField:withRange:)]) { + +// self.textView.text = text; +// [self.textView setSelectedRange:_range]; + [self.delegate textView:self.textView didSelectObject:selectedWord inInputView:self newTextForInputField:text withRange:_range]; + + } + + // hide the bar [self show:NO withAnimation:YES]; - + [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @@ -237,14 +422,20 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - NSInteger suggestions = self.suggestionList.count; + /* + NSInteger suggestions = self.suggestionList.count+1;// plus one for clear button + [self show:suggestions > 1 withAnimation:YES]; + */ + + NSInteger suggestions = self.suggestionList.count;// plus one for clear button [self show:suggestions > 0 withAnimation:YES]; + return suggestions; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; { - static NSString * cellId = @"cell"; + NSString * cellId = [NSString stringWithFormat:@"cell_%d_%d",indexPath.row,indexPath.section]; UIView *rotatedView = nil; @@ -266,16 +457,35 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [cell.contentView addSubview:rotatedView]; + [tableView setSeparatorColor:self.separatorColor]; // customization [self customizeView:rotatedView]; - + } else { rotatedView = [cell.contentView viewWithTag:kTagRotatedView]; } // customize the cell view if the data source support it, just use the text otherwise + /* + if (indexPath.row == 0) { + UIButton *button = [UIButton buttonWithType:UIButtonTypeContactAdd]; + [button addTarget:self action:@selector(hideBar) forControlEvents:UIControlEventTouchUpInside]; + + if (_clearButtonImage) { + cell.accessoryView = [[UIImageView alloc] initWithImage:_clearButtonImage]; + } + else + cell.accessoryView = button; + return cell; + } + + int idx = indexPath.row - 1; + */ + + int idx = indexPath.row; + if ([self.dataSource respondsToSelector:@selector(inputView:setObject:forView:)]) { - [self.dataSource inputView:self setObject:[self.suggestionList objectAtIndex:indexPath.row] forView:rotatedView]; + [self.dataSource inputView:self setObject:[self.suggestionList objectAtIndex:idx] forView:rotatedView]; } else { UILabel * textLabel = (UILabel *)[rotatedView viewWithTag:kTagLabelView]; @@ -283,12 +493,20 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // set the default properties textLabel.font = self.font; textLabel.textColor = self.textColor; - textLabel.text = [self stringForObjectAtIndex:indexPath.row]; + textLabel.text = [self stringForObjectAtIndex:idx]; } + + return cell; } +-(void)hideBar{ + + [self initiVars]; + [self show:NO withAnimation:YES]; +} + - (void)customizeView:(UIView *)rotatedView { // customization @@ -305,4 +523,129 @@ - (void)customizeView:(UIView *)rotatedView } } +-(void)showAutocompleteBarForInputFieldText:(NSString *)inputFieldText changeText:(NSString *)text andRange:(NSRange)range{ + + [self initiVars]; + + NSString * query = [inputFieldText stringByReplacingCharactersInRange:range withString:text]; + + //NSLog(@"query %@ text %@ loc %d len %d",query,text,range.location,range.length); + + if (range.location < UINT32_MAX) { + + NSArray *words = [[query substringToIndex:range.location+1] componentsSeparatedByString:@" "]; + NSString *word = [words lastObject]; + + NSMutableArray *wordsMut = [[[query substringToIndex:range.location+1] componentsSeparatedByString:@" "] mutableCopy]; + [wordsMut removeLastObject]; + _leftString = [wordsMut componentsJoinedByString:@" "]; + _rightString = [query substringFromIndex:range.location+1]; + //NSLog(@"_leftString %@ word %@ _rightString %@",_leftString,word,_rightString); + query= word; + + NSString *next = [_rightString substringFromIndex:0]; + if (![next isEqualToString:@" "]) { + + NSString *appendString = [[_rightString componentsSeparatedByString:@" "] objectAtIndex:0]; + query = [query stringByAppendingString:appendString]; + } + + _range = NSMakeRange(range.location-query.length+1, 0); + //NSLog(@"query %@ loc %d",query,range.location); + + } + + + if (query.length >= [self.dataSource minimumCharactersToTrigger:self]) { + + NSArray *resultArray = [self resultArrayForQuery:query ignoreCase:self.ignoreCase]; + + [self.dataSource inputView:self itemsFor:query ignoreCase:self.ignoreCase withResult:resultArray result:^(NSArray *items) { + self.suggestionList = [items sortedArrayUsingSelector:@selector(compare:options:)]; + + [self.suggestionListView reloadData]; + }]; + + } else { + self.suggestionList = nil; + [self.suggestionListView reloadData]; + } + +} + +-(BOOL)shouldShowBarForText:(NSString *)text{ + + BOOL shouldShow = NO; + [self initiVars]; + if (text.length && ![text isEqualToString:@" "]) shouldShow = YES; + + return shouldShow; + +} + +-(void)initiVars{ + + _leftString = nil; + _rightString = nil; + _range = NSMakeRange(0, 0); + +} + +-(UITextRange *)calculateSelectionRangeForTextFieldWithText:(NSString *)text{ + + //UITextField *textField = [[UITextField alloc] initWithFrame:self.textField.frame]; + + self.textField.text = text; + + UITextPosition *beginning = self.textField.beginningOfDocument; + UITextPosition *start = [self.textField positionFromPosition:beginning offset:_range.location]; + UITextPosition *end = [self.textField positionFromPosition:start offset:_range.length]; + UITextRange *textRange = [self.textField textRangeFromPosition:start toPosition:end]; + + //NSLog(@"range %d %d %@ %@ %@ %@",_range.length,_range.location, beginning.description,start.description,end.description,textRange.start.description); + [self.textField setSelectedTextRange:textRange]; + return textRange; + + +} + +-(NSArray *)resultArrayForQuery:(NSString *)query ignoreCase:(BOOL)ignoreCase{ + + __block NSMutableArray *data = [NSMutableArray array]; + + ^{ + NSArray *words = [query componentsSeparatedByString:@" "]; + + for (NSString *s in self.dataSourceContent) { + NSString *word = nil; + if ([words count]>1) { + word = words.lastObject; + + } + else{ + word = query; + } + + if (ignoreCase) { + if ([s.lowercaseString hasPrefix:word.lowercaseString]) { + [data addObject:s]; + + } + } + else{ + + if ([s hasPrefix:word]) { + [data addObject:s]; + + } + } + + } + }(); + + + return data; + +} + @end diff --git a/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.h b/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.h old mode 100644 new mode 100755 index 19f1f30..8adb4b2 --- a/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.h +++ b/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.h @@ -27,6 +27,10 @@ - (void)setAutocompleteWithDataSource:(id)dataSource delegate:(id)delegate - customize:(void (^)(ACEAutocompleteInputView *inputView))customizeView; + customize:(void (^)(ACEAutocompleteInputView *inputView))customizeView + ignoreCase:(BOOL)ignoreCase + dataSourceContent:(NSArray *)dataSourceContent + clearButtonImage:(UIImage *)clearButtonImage + andShouldShowClearButton:(BOOL)shouldShowClearButton; @end diff --git a/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.m b/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.m old mode 100644 new mode 100755 index bd97968..a026c11 --- a/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.m +++ b/ACEAutocompleteBar/UITextField+ACEAutocompleteBar.m @@ -34,8 +34,12 @@ @implementation UITextField (ACEAutocompleteBar) - (void)setAutocompleteWithDataSource:(id)dataSource delegate:(id)delegate customize:(void (^)(ACEAutocompleteInputView *inputView))customizeView + ignoreCase:(BOOL)ignoreCase + dataSourceContent:(NSArray *)dataSourceContent + clearButtonImage:(UIImage *)clearButtonImage + andShouldShowClearButton:(BOOL)shouldShowClearButton { - ACEAutocompleteInputView * autocompleteBarView = [ACEAutocompleteInputView new]; + ACEAutocompleteInputView * autocompleteBarView = [[ACEAutocompleteInputView alloc] initWithClearButtonImage:clearButtonImage andShouldShowClearButton:shouldShowClearButton]; self.inputAccessoryView = autocompleteBarView; self.delegate = autocompleteBarView; @@ -44,11 +48,13 @@ - (void)setAutocompleteWithDataSource:(id)dataSource customizeView(autocompleteBarView); } + // set the protocols autocompleteBarView.textField = self; autocompleteBarView.delegate = delegate; autocompleteBarView.dataSource = dataSource; - + autocompleteBarView.ignoreCase = ignoreCase; + autocompleteBarView.dataSourceContent = dataSourceContent; // init state is not visible [autocompleteBarView show:NO withAnimation:NO]; } diff --git a/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.h b/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.h new file mode 100644 index 0000000..a393cda --- /dev/null +++ b/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.h @@ -0,0 +1,21 @@ +// +// UITextView+ACEAutocompleteBar.h +// ACEAutocompleteBarDemo +// +// Created by Jimmy on 24/07/13. +// Copyright (c) 2013 Varshyl Mobile Pvt. Ltd. All rights reserved. +// + +#import + +@interface UITextView (ACEAutocompleteBar) + +- (void)setAutocompleteWithDataSource:(id)dataSource + delegate:(id)delegate + customize:(void (^)(ACEAutocompleteInputView *inputView))customizeView + ignoreCase:(BOOL)ignoreCase + dataSourceContent:(NSArray *)dataSourceContent + clearButtonImage:(UIImage *)clearButtonImage + andShouldShowClearButton:(BOOL)shouldShowClearButton; + +@end diff --git a/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.m b/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.m new file mode 100644 index 0000000..11bdd8b --- /dev/null +++ b/ACEAutocompleteBar/UITextView+ACEAutocompleteBar.m @@ -0,0 +1,43 @@ +// +// UITextView+ACEAutocompleteBar.m +// ACEAutocompleteBarDemo +// +// Created by Jimmy on 24/07/13. +// Copyright (c) 2013 Varshyl Mobile Pvt. Ltd. All rights reserved. +// + +#import "ACEAutocompleteBar.h" +@interface UITextView (private) +@end + + +@implementation UITextView (ACEAutocompleteBar) + +- (void)setAutocompleteWithDataSource:(id)dataSource + delegate:(id)delegate + customize:(void (^)(ACEAutocompleteInputView *inputView))customizeView + ignoreCase:(BOOL)ignoreCase + dataSourceContent:(NSArray *)dataSourceContent + clearButtonImage:(UIImage *)clearButtonImage + andShouldShowClearButton:(BOOL)shouldShowClearButton +{ + ACEAutocompleteInputView * autocompleteBarView = [[ACEAutocompleteInputView alloc] initWithClearButtonImage:clearButtonImage andShouldShowClearButton:shouldShowClearButton]; + self.inputAccessoryView = autocompleteBarView; + self.delegate = autocompleteBarView; + + // pass the view to the caller to customize it + if (customizeView) { + customizeView(autocompleteBarView); + } + + // set the protocols + autocompleteBarView.textView = self; + autocompleteBarView.delegate = delegate; + autocompleteBarView.dataSource = dataSource; + autocompleteBarView.ignoreCase = ignoreCase; + autocompleteBarView.dataSourceContent = dataSourceContent; + + // init state is not visible + [autocompleteBarView show:NO withAnimation:NO]; +} +@end diff --git a/ACEAutocompleteBar/btn_clear_black@2x.png b/ACEAutocompleteBar/btn_clear_black@2x.png new file mode 100644 index 0000000..aff861e Binary files /dev/null and b/ACEAutocompleteBar/btn_clear_black@2x.png differ diff --git a/ACEAutocompleteBar/btn_clear_white@2x.png b/ACEAutocompleteBar/btn_clear_white@2x.png new file mode 100644 index 0000000..dc0c5d7 Binary files /dev/null and b/ACEAutocompleteBar/btn_clear_white@2x.png differ diff --git a/ACEAutocompleteBarDemo.xcodeproj/project.pbxproj b/ACEAutocompleteBarDemo.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 006ec9f..5aa3b9a --- a/ACEAutocompleteBarDemo.xcodeproj/project.pbxproj +++ b/ACEAutocompleteBarDemo.xcodeproj/project.pbxproj @@ -20,6 +20,9 @@ 7BAA5F3D1742E296004F055F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7BAA5F3C1742E296004F055F /* Default-568h@2x.png */; }; 7BAA5F401742E296004F055F /* ACEViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BAA5F3F1742E296004F055F /* ACEViewController.m */; }; 7BAA5F431742E296004F055F /* ACEViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7BAA5F411742E296004F055F /* ACEViewController.xib */; }; + BF6AB28217A7EDDE001A751C /* btn_clear_black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BF6AB28017A7EDDD001A751C /* btn_clear_black@2x.png */; }; + BF6AB28317A7EDDE001A751C /* btn_clear_white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BF6AB28117A7EDDE001A751C /* btn_clear_white@2x.png */; }; + BF84C07D179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.m in Sources */ = {isa = PBXBuildFile; fileRef = BF84C07C179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +47,10 @@ 7BAA5F3E1742E296004F055F /* ACEViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACEViewController.h; sourceTree = ""; }; 7BAA5F3F1742E296004F055F /* ACEViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ACEViewController.m; sourceTree = ""; }; 7BAA5F421742E296004F055F /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ACEViewController.xib; sourceTree = ""; }; + BF6AB28017A7EDDD001A751C /* btn_clear_black@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btn_clear_black@2x.png"; sourceTree = ""; }; + BF6AB28117A7EDDE001A751C /* btn_clear_white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "btn_clear_white@2x.png"; sourceTree = ""; }; + BF84C07B179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UITextView+ACEAutocompleteBar.h"; sourceTree = ""; }; + BF84C07C179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UITextView+ACEAutocompleteBar.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -63,11 +70,15 @@ 7B7CD439174315F20086C346 /* ACEAutocompleteBar */ = { isa = PBXGroup; children = ( + BF6AB28017A7EDDD001A751C /* btn_clear_black@2x.png */, + BF6AB28117A7EDDE001A751C /* btn_clear_white@2x.png */, 7B7CD43A174315F20086C346 /* ACEAutocompleteBar.h */, 7B7CD43B174315F20086C346 /* ACEAutocompleteInputView.h */, 7B7CD43C174315F20086C346 /* ACEAutocompleteInputView.m */, 7B7CD4411744368D0086C346 /* UITextField+ACEAutocompleteBar.h */, 7B7CD4421744368D0086C346 /* UITextField+ACEAutocompleteBar.m */, + BF84C07B179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.h */, + BF84C07C179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.m */, ); path = ACEAutocompleteBar; sourceTree = ""; @@ -184,6 +195,8 @@ 7BAA5F3B1742E296004F055F /* Default@2x.png in Resources */, 7BAA5F3D1742E296004F055F /* Default-568h@2x.png in Resources */, 7BAA5F431742E296004F055F /* ACEViewController.xib in Resources */, + BF6AB28217A7EDDE001A751C /* btn_clear_black@2x.png in Resources */, + BF6AB28317A7EDDE001A751C /* btn_clear_white@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -199,6 +212,7 @@ 7BAA5F401742E296004F055F /* ACEViewController.m in Sources */, 7B7CD43F174315F20086C346 /* ACEAutocompleteInputView.m in Sources */, 7B7CD4431744368D0086C346 /* UITextField+ACEAutocompleteBar.m in Sources */, + BF84C07D179FF304002BD9C0 /* UITextView+ACEAutocompleteBar.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -286,6 +300,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Prefix.pch"; INFOPLIST_FILE = "ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -297,6 +312,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Prefix.pch"; INFOPLIST_FILE = "ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/ACEAutocompleteBarDemo/ACEAppDelegate.h b/ACEAutocompleteBarDemo/ACEAppDelegate.h old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/ACEAppDelegate.m b/ACEAutocompleteBarDemo/ACEAppDelegate.m old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Info.plist b/ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Info.plist old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Prefix.pch b/ACEAutocompleteBarDemo/ACEAutocompleteBarDemo-Prefix.pch old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/ACEViewController.h b/ACEAutocompleteBarDemo/ACEViewController.h old mode 100644 new mode 100755 index 4992940..6e9dcd7 --- a/ACEAutocompleteBarDemo/ACEViewController.h +++ b/ACEAutocompleteBarDemo/ACEViewController.h @@ -11,5 +11,7 @@ @interface ACEViewController : UIViewController @property (nonatomic, strong) IBOutlet UITextField * textField; +@property (nonatomic, strong) IBOutlet UITextField * textField2; +@property (nonatomic, strong) IBOutlet UITextView *textView; @end diff --git a/ACEAutocompleteBarDemo/ACEViewController.m b/ACEAutocompleteBarDemo/ACEViewController.m old mode 100644 new mode 100755 index db8e4ca..581b000 --- a/ACEAutocompleteBarDemo/ACEViewController.m +++ b/ACEAutocompleteBarDemo/ACEViewController.m @@ -2,8 +2,8 @@ // ACEViewController.m // ACEAutocompleteBarDemo // -// Created by Stefano Acerbetti on 5/14/13. -// Copyright (c) 2013 Stefano Acerbetti. All rights reserved. +// Created by Jimmy on 24/07/13. +// Copyright (c) 2013 Varshyl Mobile Pvt. Ltd. All rights reserved. // #import "ACEViewController.h" @@ -19,7 +19,7 @@ - (void)viewDidLoad { [super viewDidLoad]; - self.sampleStrings = @[@"one", @"two", @"three", @"four"]; + self.sampleStrings = @[@"One", @"two", @"three", @"Four", @"five", @"six", @"Seven", @"eight", @"nine", @"Ten", @"oone"]; // set the autocomplete data [self.textField setAutocompleteWithDataSource:self @@ -29,9 +29,48 @@ - (void)viewDidLoad // customize the view (optional) inputView.font = [UIFont systemFontOfSize:20]; inputView.textColor = [UIColor whiteColor]; - inputView.backgroundColor = [UIColor colorWithRed:0.2 green:0.3 blue:0.9 alpha:0.8]; + inputView.backgroundColor = [UIColor grayColor]; + inputView.alpha = 0.8; + inputView.separatorColor = [UIColor redColor]; - }]; + } + ignoreCase:YES dataSourceContent:self.sampleStrings + clearButtonImage:[UIImage imageNamed:@"btn_clear_black"] + andShouldShowClearButton:YES + ]; + + + [self.textField2 setAutocompleteWithDataSource:self + delegate:self + customize:^(ACEAutocompleteInputView *inputView) { + + // customize the view (optional) + inputView.font = [UIFont systemFontOfSize:20]; + inputView.textColor = [UIColor whiteColor]; + inputView.backgroundColor = [UIColor greenColor]; + inputView.alpha = 0.8; + + } + ignoreCase:YES dataSourceContent:self.sampleStrings + clearButtonImage:[UIImage imageNamed:@"btn_clear_white"] + andShouldShowClearButton:YES]; + + + [self.textView setAutocompleteWithDataSource:self + delegate:self + customize:^(ACEAutocompleteInputView *inputView) { + + // customize the view (optional) + inputView.font = [UIFont systemFontOfSize:20]; + inputView.textColor = [UIColor whiteColor]; + inputView.backgroundColor = [UIColor colorWithRed:0.2 green:0.3 blue:0.9 alpha:0.8]; + inputView.separatorColor = [UIColor brownColor]; + + } + ignoreCase:YES dataSourceContent:self.sampleStrings + clearButtonImage:nil + andShouldShowClearButton:YES]; + // show the keyboard [self.textField becomeFirstResponder]; @@ -44,20 +83,6 @@ - (void)didReceiveMemoryWarning } -#pragma mark - Autocomplete Delegate - -- (void)textField:(UITextField *)textField didSelectObject:(id)object inInputView:(ACEAutocompleteInputView *)inputView -{ - textField.text = object; // NSString -} - -- (BOOL)textFieldShouldReturn:(UITextField *)textField -{ - [textField resignFirstResponder]; - return NO; -} - - #pragma mark - Autocomplete Data Source - (NSUInteger)minimumCharactersToTrigger:(ACEAutocompleteInputView *)inputView @@ -65,25 +90,11 @@ - (NSUInteger)minimumCharactersToTrigger:(ACEAutocompleteInputView *)inputView return 1; } -- (void)inputView:(ACEAutocompleteInputView *)inputView itemsFor:(NSString *)query result:(void (^)(NSArray *items))resultBlock; -{ +-(void)inputView:(ACEAutocompleteInputView *)inputView itemsFor:(NSString *)query ignoreCase:(BOOL)ignoreCase withResult:(NSArray *)resultArray result:(void (^)(NSArray *))resultBlock{ + if (resultBlock != nil) { - // execute the filter on a background thread to demo the asynchronous capability - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ - - // execute the filter - NSMutableArray *data = [NSMutableArray array]; - for (NSString *s in self.sampleStrings) { - if ([s hasPrefix:query]) { - [data addObject:s]; - } - } - - // return the filtered array in the main thread - dispatch_async(dispatch_get_main_queue(), ^{ - resultBlock(data); - }); - }); + + resultBlock(resultArray); } } diff --git a/ACEAutocompleteBarDemo/Default-568h@2x.png b/ACEAutocompleteBarDemo/Default-568h@2x.png old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/Default.png b/ACEAutocompleteBarDemo/Default.png old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/Default@2x.png b/ACEAutocompleteBarDemo/Default@2x.png old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/en.lproj/ACEViewController.xib b/ACEAutocompleteBarDemo/en.lproj/ACEViewController.xib old mode 100644 new mode 100755 index 435ed00..85da4b4 --- a/ACEAutocompleteBarDemo/en.lproj/ACEViewController.xib +++ b/ACEAutocompleteBarDemo/en.lproj/ACEViewController.xib @@ -2,19 +2,19 @@ 1552 - 12D78 + 12E55 3084 - 1187.37 + 1187.39 626.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 2083 - IBNSLayoutConstraint IBProxyObject IBUILabel IBUITextField + IBUITextView IBUIView @@ -42,8 +42,7 @@ 292 {{20, 55}, {280, 30}} - - + _NS:9 NO YES @@ -63,11 +62,11 @@ IBCocoaTouchFramework - + 1 14 - + Helvetica 14 16 @@ -78,7 +77,6 @@ 292 {{20, 20}, {280, 21}} - _NS:9 NO @@ -105,10 +103,55 @@ NO + + + 292 + {{40, 140}, {240, 81}} + + _NS:9 + + 1 + MSAxIDEAA + + YES + YES + IBCocoaTouchFramework + labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut + + 2 + IBCocoaTouchFramework + + + + + + + 292 + {{28, 93}, {264, 30}} + + _NS:9 + NO + YES + IBCocoaTouchFramework + 0 + + 3 + + 3 + MAA + + + YES + 17 + + IBCocoaTouchFramework + + + + {{0, 20}, {320, 548}} - 3 @@ -155,6 +198,22 @@ 21 + + + textView + + + + 35 + + + + textField2 + + + + 46 + @@ -179,104 +238,10 @@ 6 - - - 3 - 0 - - 3 - 1 - - 55 - - 1000 - - 3 - 9 - 3 - - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 5 - 0 - - 5 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 5 - 0 - - 5 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - - - - 3 - 0 - - 3 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - + + @@ -286,62 +251,22 @@ - - 10 - - - 12 - - - - 8 - 0 - - 0 - 1 - - 21 - - 1000 - - 3 - 9 - 1 - - - - - - 13 - - - - - 14 - - - - - 16 - - - - - 17 - + - 19 - + 22 + + - 20 - + 39 + + @@ -351,72 +276,23 @@ com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - 21 - - - - - ACEViewController - UIViewController - - textField - UITextField - - - textField - - textField - UITextField - - - - IBProjectSource - ./Classes/ACEViewController.h - - - - NSLayoutConstraint - NSObject - - IBProjectSource - ./Classes/NSLayoutConstraint.h - - - + 46 + 0 IBCocoaTouchFramework YES 3 - YES 2083 diff --git a/ACEAutocompleteBarDemo/en.lproj/InfoPlist.strings b/ACEAutocompleteBarDemo/en.lproj/InfoPlist.strings old mode 100644 new mode 100755 diff --git a/ACEAutocompleteBarDemo/main.m b/ACEAutocompleteBarDemo/main.m old mode 100644 new mode 100755 diff --git a/Example.png b/Example.png deleted file mode 100644 index ea79a12..0000000 Binary files a/Example.png and /dev/null differ diff --git a/LICENSE b/LICENSE index 1972eed..1b0a292 100755 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013 Stefano Acerbetti +Copyright (c) 2013 Varshyl Mobile Pvt. Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md old mode 100644 new mode 100755 index f1aae17..9d792c6 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ACEAutocompleteBar Purpose -------------- -ACEAutocompleteBar is a simple category of the UITextField. +Forked from ACEAutocompleteBar and extended category for UITextView and added other misc features.(See change log) It automatically displays text suggestions in real-time on the top of the virtual keyboard. It uses a block to create the data source and can work well will an asynchronous API request. How-To @@ -30,23 +30,27 @@ This component requires ARC Change Log ------------------ -05/20/2013 - v1.0.0 -- Support for custom height -- Fixed change orientation bug - -05/18/2013 - v0.1.2 -- Extend the UITextField delegate to support more customization - -05/17/2013 - v0.1.1 -- Better implementation of the asynchronous data source - -05/16/2013 - v0.1 -- Initial release +06/24/2013 - v1.1.0 + - Extended support for UITextView + - Extended support for every word in the sentence + - Retain cursor position after selecting suggestion + - Removed the need for implementing delegates for input field + - Added ignoreCase option to ignore case of the input and suggestion list + - Added property separatorColor to customizeView + - Added dataSourceContent to do the matching internally [can be made smooth, probably in next update] + - Added support to hide suggestion list on single tap or if selection changed + - Added clearButton to dismiss the suggestion list + - a) Set custom image for clear button + - b) Disable clear button + - Added 'text' NSString object in textfield delegate for result manipulation + - Added 'range' UITextRange object in textfield delegate for cursor manipulation + - Added 'text' NSString object in textview delegate for result manipulation + - Added 'range' NSRange type in textview delegate for cursor manipulation License ------------------ -Copyright (c) 2013 Stefano Acerbetti +Copyright (c) 2013 Varshyl Mobile Pvt. Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: