Repository: nulrich/RCTAutoComplete Branch: master Commit: 9efe9ac88405 Files: 25 Total size: 112.4 KB Directory structure: gitextract_dsah9vym/ ├── .flowconfig ├── .gitignore ├── .npmignore ├── AutoCompleteView.h ├── AutoCompleteView.m ├── DictionaryAutoCompleteObject.h ├── DictionaryAutoCompleteObject.m ├── LICENSE ├── MLPAutoCompleteTextField/ │ ├── MLPAutoCompleteTextField.h │ ├── MLPAutoCompleteTextField.m │ ├── MLPAutoCompleteTextFieldDataSource.h │ ├── MLPAutoCompleteTextFieldDelegate.h │ ├── MLPAutoCompletionObject.h │ ├── NSString+Levenshtein.h │ └── NSString+Levenshtein.m ├── RCTAutoComplete.android.js ├── RCTAutoComplete.h ├── RCTAutoComplete.ios.js ├── RCTAutoComplete.m ├── RCTAutoComplete.xcodeproj/ │ └── project.pbxproj ├── RCTTableViewCell.h ├── RCTTableViewCell.m ├── README.md ├── package.json └── react-native-autocomplete.podspec ================================================ FILE CONTENTS ================================================ ================================================ FILE: .flowconfig ================================================ [ignore] # We fork some components by platform. .*/*.web.js .*/*.android.js # Some modules have their own node_modules with overlap .*/node_modules/node-haste/.* # Ignore react-tools where there are overlaps, but don't ignore anything that # react-native relies on .*/node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js .*/node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js .*/node_modules/react-tools/src/browser/ui/React.js .*/node_modules/react-tools/src/core/ReactInstanceHandles.js .*/node_modules/react-tools/src/event/EventPropagators.js # Ignore commoner tests .*/node_modules/commoner/test/.* # See https://github.com/facebook/flow/issues/442 .*/react-tools/node_modules/commoner/lib/reader.js # Ignore jest .*/react-native/node_modules/jest-cli/.* [include] [libs] node_modules/react-native/Libraries/react-native/react-native-interface.js [options] module.system=haste [version] 0.11.0 ================================================ FILE: .gitignore ================================================ # OSX # .DS_Store # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate project.xcworkspace # node.js # node_modules/ npm-debug.log ================================================ FILE: .npmignore ================================================ # OSX # .DS_Store # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate # node.js # node_modules/ npm-debug.log ================================================ FILE: AutoCompleteView.h ================================================ #import #import "MLPAutoCompleteTextField/MLPAutoCompleteTextField.h" @class RCTEventDispatcher; @interface AutoCompleteView : MLPAutoCompleteTextField @property (nonatomic, copy) NSString *cellComponent; @property (retain, nonatomic) NSArray *suggestions; @property (copy) void (^handler)(NSArray *); @property (nonatomic, assign) BOOL caretHidden; @property (nonatomic, assign) BOOL autoCorrect; @property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) BOOL blurOnSubmit; @property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, strong) UIColor *placeholderTextColor; @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, strong) NSNumber *maxLength; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - (void)textFieldDidChange; @end ================================================ FILE: AutoCompleteView.m ================================================ #import "AutoCompleteView.h" #import "DictionaryAutoCompleteObject.h" #import "MLPAutoCompleteTextField/MLPAutoCompleteTextField.h" #import #import #import #import "UIView+React.h" @implementation AutoCompleteView { RCTEventDispatcher *_eventDispatcher; NSMutableArray *_reactSubviews; BOOL _jsRequestingFirstResponder; NSInteger _nativeEventCount; NSInteger _mostRecentEventCount; } - (void) setSuggestions:(NSArray *)completions { if (self.handler) { if (self.cellComponent !=nil) { NSMutableArray *mutableJsonObjects = [NSMutableArray new]; for(NSDictionary *json in completions){ DictionaryAutoCompleteObject *jsonObject = [[DictionaryAutoCompleteObject alloc] initWithDictionnary:json]; [mutableJsonObjects addObject:jsonObject]; } self.handler([NSArray arrayWithArray:mutableJsonObjects]); } else { self.handler(completions); } } } - (void) setMostRecentEventCount:(NSInteger)mostRecentEventCount { if (mostRecentEventCount > 0) { _mostRecentEventCount = mostRecentEventCount; } } - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { if ((self = [super initWithFrame:CGRectZero])) { RCTAssert(eventDispatcher, @"eventDispatcher is a required parameter"); _eventDispatcher = eventDispatcher; [self addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged]; [self addTarget:self action:@selector(textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin]; [self addTarget:self action:@selector(textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd]; [self addTarget:self action:@selector(textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit]; _reactSubviews = [NSMutableArray new]; } return self; } RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setText:(NSString *)text { NSInteger eventLag = _nativeEventCount - _mostRecentEventCount; if (eventLag == 0 && ![text isEqualToString:self.text]) { UITextRange *selection = self.selectedTextRange; super.text = text; self.selectedTextRange = selection; // maintain cursor position/selection - this is robust to out of bounds } else if (eventLag > RCTTextUpdateLagWarningThreshold) { RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); } } static void RCTUpdatePlaceholder(AutoCompleteView *self) { if (self.placeholder.length > 0 && self.placeholderTextColor) { self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder attributes:@{ NSForegroundColorAttributeName : self.placeholderTextColor }]; } else if (self.placeholder.length) { self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder]; } } - (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor { _placeholderTextColor = placeholderTextColor; RCTUpdatePlaceholder(self); } - (void)setPlaceholder:(NSString *)placeholder { super.placeholder = placeholder; RCTUpdatePlaceholder(self); } - (NSArray *)reactSubviews { // TODO: do we support subviews of textfield in React? // In any case, we should have a better approach than manually // maintaining array in each view subclass like this return _reactSubviews; } - (void)removeReactSubview:(UIView *)subview { // TODO: this is a bit broken - if the TextField inserts any of // its own views below or between React's, the indices won't match [_reactSubviews removeObject:subview]; [subview removeFromSuperview]; } - (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex { // TODO: this is a bit broken - if the TextField inserts any of // its own views below or between React's, the indices won't match [_reactSubviews insertObject:view atIndex:atIndex]; [super insertSubview:view atIndex:atIndex]; } - (CGRect)caretRectForPosition:(UITextPosition *)position { if (_caretHidden) { return CGRectZero; } return [super caretRectForPosition:position]; } - (CGRect)textRectForBounds:(CGRect)bounds { CGRect rect = [super textRectForBounds:bounds]; return UIEdgeInsetsInsetRect(rect, _contentInset); } - (CGRect)editingRectForBounds:(CGRect)bounds { return [self textRectForBounds:bounds]; } - (void)setAutoCorrect:(BOOL)autoCorrect { self.autocorrectionType = (autoCorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo); } - (BOOL)autoCorrect { return self.autocorrectionType == UITextAutocorrectionTypeYes; } - (void)textFieldDidChange { _nativeEventCount++; [_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange reactTag:self.reactTag text:self.text key:nil eventCount:_nativeEventCount]; } - (void)textFieldEndEditing { [_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd reactTag:self.reactTag text:self.text key:nil eventCount:_nativeEventCount]; } - (void)textFieldSubmitEditing { [_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit reactTag:self.reactTag text:self.text key:nil eventCount:_nativeEventCount]; } - (void)textFieldBeginEditing { if (_selectTextOnFocus) { dispatch_async(dispatch_get_main_queue(), ^{ [self selectAll:nil]; }); } [_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus reactTag:self.reactTag text:self.text key:nil eventCount:_nativeEventCount]; } - (BOOL)becomeFirstResponder { _jsRequestingFirstResponder = YES; BOOL result = [super becomeFirstResponder]; _jsRequestingFirstResponder = NO; return result; } - (BOOL)resignFirstResponder { BOOL result = [super resignFirstResponder]; if (result) { [_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur reactTag:self.reactTag text:self.text key:nil eventCount:_nativeEventCount]; } return result; } - (BOOL)canBecomeFirstResponder { return _jsRequestingFirstResponder; } @end ================================================ FILE: DictionaryAutoCompleteObject.h ================================================ #import #import "MLPAutoCompletionObject.h" @interface DictionaryAutoCompleteObject : NSObject @property (strong) NSDictionary *json; - (id)initWithDictionnary:(NSDictionary *)json; @end ================================================ FILE: DictionaryAutoCompleteObject.m ================================================ #import "DictionaryAutoCompleteObject.h" @interface DictionaryAutoCompleteObject () @end @implementation DictionaryAutoCompleteObject - (id)initWithDictionnary:(NSDictionary *)json { self = [super init]; if (self) { [self setJson:json]; } return self; } #pragma mark - MLPAutoCompletionObject Protocol - (NSString *)autocompleteString { return @""; } @end ================================================ FILE: LICENSE ================================================ Copyright (c) 2015 Nicolas Ulrich 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MLPAutoCompleteTextField/MLPAutoCompleteTextField.h ================================================ /* // MLPAutoCompleteTextField.h // // // Created by Eddy Borja on 12/29/12. // Copyright (c) 2013 Mainloop LLC. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import #import "MLPAutoCompleteTextFieldDataSource.h" #import "MLPAutoCompleteTextFieldDelegate.h" @protocol MLPAutoCompleteSortOperationDelegate - (void)autoCompleteTermsDidSort:(NSArray *)completions; - (NSInteger)maximumEditDistanceForAutoCompleteTerms; @end @protocol MLPAutoCompleteFetchOperationDelegate - (void)autoCompleteTermsDidFetch:(NSDictionary *)fetchInfo; @end @interface MLPAutoCompleteTextField : UITextField + (NSString *) accessibilityLabelForIndexPath:(NSIndexPath *)indexPath; @property (strong, readonly) UITableView *autoCompleteTableView; // all delegates and datasources should be weak referenced @property (weak) IBOutlet id autoCompleteDataSource; @property (weak) IBOutlet id autoCompleteDelegate; @property (assign) NSTimeInterval autoCompleteFetchRequestDelay; //default is 0.1, if you fetch from a web service you may want this higher to prevent multiple calls happening very quickly. @property (assign) BOOL sortAutoCompleteSuggestionsByClosestMatch; @property (assign) BOOL applyBoldEffectToAutoCompleteSuggestions; @property (assign) BOOL reverseAutoCompleteSuggestionsBoldEffect; @property (assign) BOOL showTextFieldDropShadowWhenAutoCompleteTableIsOpen; @property (assign) BOOL showAutoCompleteTableWhenEditingBegins; //only applies for drop down style autocomplete tables. @property (assign) BOOL disableAutoCompleteTableUserInteractionWhileFetching; @property (assign) BOOL autoCompleteTableAppearsAsKeyboardAccessory; //if set to TRUE, the autocomplete table will appear as a keyboard input accessory view rather than a drop down. @property (assign) BOOL shouldResignFirstResponderFromKeyboardAfterSelectionOfAutoCompleteRows; // default is TRUE @property (assign) NSInteger maximumEditDistance; //This is the maximum amount of edits allowed for a word to be considered as a possible autocomplete suggestion to the user input. Set this to 0 to require an exact match of the user input so far. Defaults to 100. @property (assign) BOOL requireAutoCompleteSuggestionsToMatchInputExactly; //If true, the only suggestions that are shown will be words that complete the currently user input (This is the same as a maximumEditDistance of 0). Defaults to false to take typos into consideration. @property (assign) BOOL autoCompleteTableViewHidden; @property (assign) CGFloat autoCompleteFontSize; @property (strong) NSString *autoCompleteBoldFontName; @property (strong) NSString *autoCompleteRegularFontName; @property (assign) NSInteger maximumNumberOfAutoCompleteRows; @property (assign) CGFloat partOfAutoCompleteRowHeightToCut; // this number multiplied by autoCompleteRowHeight will be subtracted from total tableView height. @property (assign) CGFloat autoCompleteRowHeight; @property (nonatomic, assign) CGRect autoCompleteTableFrame; @property (assign) CGSize autoCompleteTableOriginOffset; @property (assign) CGSize autoCompleteTableSizeOffset; @property (assign) CGFloat autoCompleteTableCornerRadius; //only applies for drop down style autocomplete tables. @property (nonatomic, assign) UIEdgeInsets autoCompleteContentInsets; @property (nonatomic, assign) UIEdgeInsets autoCompleteScrollIndicatorInsets; @property (nonatomic, strong) UIColor *autoCompleteTableBorderColor; @property (nonatomic, assign) CGFloat autoCompleteTableBorderWidth; @property (nonatomic, strong) UIColor *autoCompleteTableBackgroundColor; @property (strong) UIColor *autoCompleteTableCellBackgroundColor; @property (strong) UIColor *autoCompleteTableCellTextColor; - (void)registerAutoCompleteCellNib:(UINib *)nib forCellReuseIdentifier:(NSString *)reuseIdentifier; - (void)registerAutoCompleteCellClass:(Class)cellClass forCellReuseIdentifier:(NSString *)reuseIdentifier; - (void)reloadData; //it will ask DataSource for data again @end ================================================ FILE: MLPAutoCompleteTextField/MLPAutoCompleteTextField.m ================================================ /* // MLPAutoCompleteTextField.m // // // Created by Eddy Borja on 12/29/12. // Copyright (c) 2013 Mainloop LLC. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import "MLPAutoCompleteTextField.h" #import "MLPAutoCompletionObject.h" #import "NSString+Levenshtein.h" #import static NSString *kSortInputStringKey = @"sortInputString"; static NSString *kSortEditDistancesKey = @"editDistances"; static NSString *kSortObjectKey = @"sortObject"; static NSString *kKeyboardAccessoryInputKeyPath = @"autoCompleteTableAppearsAsKeyboardAccessory"; const NSTimeInterval DefaultAutoCompleteRequestDelay = 0.1; @interface MLPAutoCompleteSortOperation: NSOperation @property (strong) NSString *incompleteString; @property (strong) NSArray *possibleCompletions; @property (strong) id delegate; @property (strong) NSDictionary *boldTextAttributes; @property (strong) NSDictionary *regularTextAttributes; - (id)initWithDelegate:(id)aDelegate incompleteString:(NSString *)string possibleCompletions:(NSArray *)possibleStrings; - (NSArray *)sortedCompletionsForString:(NSString *)inputString withPossibleStrings:(NSArray *)possibleTerms; @end static NSString *kFetchedTermsKey = @"terms"; static NSString *kFetchedStringKey = @"fetchInputString"; @interface MLPAutoCompleteFetchOperation: NSOperation @property (strong) NSString *incompleteString; @property (strong) MLPAutoCompleteTextField *textField; @property (strong) id delegate; @property (strong) id dataSource; - (id)initWithDelegate:(id)aDelegate completionsDataSource:(id)aDataSource autoCompleteTextField:(MLPAutoCompleteTextField *)aTextField; @end static NSString *kBorderStyleKeyPath = @"borderStyle"; static NSString *kAutoCompleteTableViewHiddenKeyPath = @"autoCompleteTableView.hidden"; static NSString *kBackgroundColorKeyPath = @"backgroundColor"; static NSString *kDefaultAutoCompleteCellIdentifier = @"_DefaultAutoCompleteCellIdentifier"; @interface MLPAutoCompleteTextField () @property (strong, readwrite) UITableView *autoCompleteTableView; @property (strong) NSArray *autoCompleteSuggestions; @property (strong) NSOperationQueue *autoCompleteSortQueue; @property (strong) NSOperationQueue *autoCompleteFetchQueue; @property (strong) NSString *reuseIdentifier; @property (assign) CGColorRef originalShadowColor; @property (assign) CGSize originalShadowOffset; @property (assign) CGFloat originalShadowOpacity; @end @implementation MLPAutoCompleteTextField #pragma mark - Init - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self initialize]; } return self; } - (id)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self initialize]; } return self; } - (void)dealloc { [self closeAutoCompleteTableView]; [self stopObservingKeyPathsAndNotifications]; } - (void)initialize { [self beginObservingKeyPathsAndNotifications]; [self setDefaultValuesForVariables]; UITableView *newTableView = [self newAutoCompleteTableViewForTextField:self]; [self setAutoCompleteTableView:newTableView]; } #pragma mark - Notifications and KVO - (void)beginObservingKeyPathsAndNotifications { [self addObserver:self forKeyPath:kBorderStyleKeyPath options:NSKeyValueObservingOptionNew context:nil]; [self addObserver:self forKeyPath:kAutoCompleteTableViewHiddenKeyPath options:NSKeyValueObservingOptionNew context:nil]; [self addObserver:self forKeyPath:kBackgroundColorKeyPath options:NSKeyValueObservingOptionNew context:nil]; [self addObserver:self forKeyPath:kKeyboardAccessoryInputKeyPath options:NSKeyValueObservingOptionNew context:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChangeWithNotification:) name:UITextFieldTextDidChangeNotification object:self]; } - (void)stopObservingKeyPathsAndNotifications { [[NSNotificationCenter defaultCenter] removeObserver:self]; [self removeObserver:self forKeyPath:kBorderStyleKeyPath]; [self removeObserver:self forKeyPath:kAutoCompleteTableViewHiddenKeyPath]; [self removeObserver:self forKeyPath:kBackgroundColorKeyPath]; [self removeObserver:self forKeyPath:kKeyboardAccessoryInputKeyPath]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if([keyPath isEqualToString:kBorderStyleKeyPath]) { //[self styleAutoCompleteTableForBorderStyle:self.borderStyle]; } else if ([keyPath isEqualToString:kAutoCompleteTableViewHiddenKeyPath]) { if(self.autoCompleteTableView.hidden){ [self closeAutoCompleteTableView]; } else { [self.autoCompleteTableView reloadData]; } } else if ([keyPath isEqualToString:kBackgroundColorKeyPath]){ //[self styleAutoCompleteTableForBorderStyle:self.borderStyle]; } else if ([keyPath isEqualToString:kKeyboardAccessoryInputKeyPath]){ if(self.autoCompleteTableAppearsAsKeyboardAccessory){ [self setAutoCompleteTableForKeyboardAppearance]; } else { [self setAutoCompleteTableForDropDownAppearance]; } } } #pragma mark - TableView Datasource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSInteger numberOfRows = [self.autoCompleteSuggestions count]; [self expandAutoCompleteTableViewForNumberOfRows:numberOfRows]; return numberOfRows; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = nil; NSString *cellIdentifier = kDefaultAutoCompleteCellIdentifier; if(!self.reuseIdentifier){ cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { cell = [self autoCompleteTableViewCellWithReuseIdentifier:cellIdentifier]; } } else { cell = [tableView dequeueReusableCellWithIdentifier:self.reuseIdentifier]; } NSAssert(cell, @"Unable to create cell for autocomplete table"); id autoCompleteObject = self.autoCompleteSuggestions[indexPath.row]; NSString *suggestedString; if([autoCompleteObject isKindOfClass:[NSString class]]){ suggestedString = (NSString *)autoCompleteObject; } else if ([autoCompleteObject conformsToProtocol:@protocol(MLPAutoCompletionObject)]){ suggestedString = [(id )autoCompleteObject autocompleteString]; } else { NSAssert(0, @"Autocomplete suggestions must either be NSString or objects conforming to the MLPAutoCompletionObject protocol."); } [self configureCell:cell atIndexPath:indexPath withAutoCompleteString:suggestedString]; return cell; } - (UITableViewCell *)autoCompleteTableViewCellWithReuseIdentifier:(NSString *)identifier { UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier]; [cell setBackgroundColor:[UIColor clearColor]]; [cell.textLabel setTextColor:self.textColor]; [cell.textLabel setFont:self.font]; return cell; } + (NSString *) accessibilityLabelForIndexPath:(NSIndexPath *)indexPath { return [NSString stringWithFormat:@"{%ld,%ld}",(long)indexPath.section,(long)indexPath.row]; } - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath withAutoCompleteString:(NSString *)string { NSAttributedString *boldedString = nil; BOOL attributedTextSupport = [cell.textLabel respondsToSelector:@selector(setAttributedText:)]; if(attributedTextSupport && self.applyBoldEffectToAutoCompleteSuggestions){ NSRange boldedRange = [string rangeOfString:self.text options:NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch]; boldedString = [self boldedString:string withRange:boldedRange]; } id autoCompleteObject = self.autoCompleteSuggestions[indexPath.row]; if(![autoCompleteObject conformsToProtocol:@protocol(MLPAutoCompletionObject)]){ autoCompleteObject = nil; } if([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:shouldConfigureCell:withAutoCompleteString:withAttributedString:forAutoCompleteObject:forRowAtIndexPath:)]) { if(![self.autoCompleteDelegate autoCompleteTextField:self shouldConfigureCell:cell withAutoCompleteString:string withAttributedString:boldedString forAutoCompleteObject:autoCompleteObject forRowAtIndexPath:indexPath]) { return; } } [cell.textLabel setTextColor:self.textColor]; if(boldedString){ if ([cell.textLabel respondsToSelector:@selector(setAttributedText:)]) { [cell.textLabel setAttributedText:boldedString]; } else{ [cell.textLabel setText:string]; [cell.textLabel setFont:[UIFont fontWithName:self.font.fontName size:self.autoCompleteFontSize]]; } } else { [cell.textLabel setText:string]; [cell.textLabel setFont:[UIFont fontWithName:self.font.fontName size:self.autoCompleteFontSize]]; } cell.accessibilityLabel = [[self class] accessibilityLabelForIndexPath:indexPath]; if(self.autoCompleteTableCellTextColor){ [cell.textLabel setTextColor:self.autoCompleteTableCellTextColor]; } } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { [cell setBackgroundColor:self.autoCompleteTableCellBackgroundColor]; } #pragma mark - TableView Delegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return self.autoCompleteRowHeight; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(!self.autoCompleteTableAppearsAsKeyboardAccessory){ [self closeAutoCompleteTableView]; } UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath]; NSString *autoCompleteString = selectedCell.textLabel.text; self.text = autoCompleteString; id autoCompleteObject = self.autoCompleteSuggestions[indexPath.row]; if(![autoCompleteObject conformsToProtocol:@protocol(MLPAutoCompletionObject)]){ autoCompleteObject = nil; } if([self.autoCompleteDelegate respondsToSelector: @selector(autoCompleteTextField:didSelectAutoCompleteString:withAutoCompleteObject:forRowAtIndexPath:)]){ [self.autoCompleteDelegate autoCompleteTextField:self didSelectAutoCompleteString:autoCompleteString withAutoCompleteObject:autoCompleteObject forRowAtIndexPath:indexPath]; } [self finishedSearching]; } #pragma mark - AutoComplete Sort Operation Delegate - (void)autoCompleteTermsDidSort:(NSArray *)completions { [self setAutoCompleteSuggestions:completions]; [self.autoCompleteTableView reloadData]; if ([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:didChangeNumberOfSuggestions:)]) { [self.autoCompleteDelegate autoCompleteTextField:self didChangeNumberOfSuggestions:self.autoCompleteSuggestions.count]; } } - (NSInteger)maximumEditDistanceForAutoCompleteTerms { return self.requireAutoCompleteSuggestionsToMatchInputExactly ? 0 : self.maximumEditDistance; } #pragma mark - AutoComplete Fetch Operation Delegate - (void)autoCompleteTermsDidFetch:(NSDictionary *)fetchInfo { NSString *inputString = fetchInfo[kFetchedStringKey]; NSArray *completions = fetchInfo[kFetchedTermsKey]; [self.autoCompleteSortQueue cancelAllOperations]; if(self.sortAutoCompleteSuggestionsByClosestMatch){ MLPAutoCompleteSortOperation *operation = [[MLPAutoCompleteSortOperation alloc] initWithDelegate:self incompleteString:inputString possibleCompletions:completions]; [self.autoCompleteSortQueue addOperation:operation]; } else { [self autoCompleteTermsDidSort:completions]; } } #pragma mark - Events - (void)textFieldDidChangeWithNotification:(NSNotification *)aNotification { if(aNotification.object == self){ [self reloadData]; } } - (BOOL)becomeFirstResponder { [self saveCurrentShadowProperties]; if(self.showAutoCompleteTableWhenEditingBegins || self.autoCompleteTableAppearsAsKeyboardAccessory){ [self fetchAutoCompleteSuggestions]; } return [super becomeFirstResponder]; } - (void) finishedSearching { if (self.shouldResignFirstResponderFromKeyboardAfterSelectionOfAutoCompleteRows) { [self resignFirstResponder]; } } - (BOOL)resignFirstResponder { [self restoreOriginalShadowProperties]; if(!self.autoCompleteTableAppearsAsKeyboardAccessory){ [self closeAutoCompleteTableView]; } return [super resignFirstResponder]; } #pragma mark - Open/Close Actions - (void)expandAutoCompleteTableViewForNumberOfRows:(NSInteger)numberOfRows { NSAssert(numberOfRows >= 0, @"Number of rows given for auto complete table was negative, this is impossible."); if(!self.isFirstResponder){ return; } if(self.autoCompleteTableAppearsAsKeyboardAccessory){ [self expandKeyboardAutoCompleteTableForNumberOfRows:numberOfRows]; } else { [self expandDropDownAutoCompleteTableForNumberOfRows:numberOfRows]; } } - (void)expandKeyboardAutoCompleteTableForNumberOfRows:(NSInteger)numberOfRows { if(numberOfRows && (self.autoCompleteTableViewHidden == NO)){ [self.autoCompleteTableView setAlpha:1]; } else { [self.autoCompleteTableView setAlpha:0]; } } - (void)expandDropDownAutoCompleteTableForNumberOfRows:(NSInteger)numberOfRows { [self resetDropDownAutoCompleteTableFrameForNumberOfRows:numberOfRows]; if(numberOfRows && (self.autoCompleteTableViewHidden == NO)){ [self.autoCompleteTableView setAlpha:1]; BOOL tableViewWillBeAddedToViewHierarchy = !self.autoCompleteTableView.superview; if(tableViewWillBeAddedToViewHierarchy){ if([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:willShowAutoCompleteTableView:)]){ [self.autoCompleteDelegate autoCompleteTextField:self willShowAutoCompleteTableView:self.autoCompleteTableView]; } } [self.superview bringSubviewToFront:self]; #if BROKEN UIView *rootView = [self.window.subviews objectAtIndex:0]; [rootView insertSubview:self.autoCompleteTableView belowSubview:self]; #else [self.superview insertSubview:self.autoCompleteTableView belowSubview:self]; #endif [self.autoCompleteTableView setUserInteractionEnabled:YES]; if(self.showTextFieldDropShadowWhenAutoCompleteTableIsOpen){ [self.layer setShadowColor:[[UIColor blackColor] CGColor]]; [self.layer setShadowOffset:CGSizeMake(0, 1)]; [self.layer setShadowOpacity:0.35]; } if (tableViewWillBeAddedToViewHierarchy && [self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:didShowAutoCompleteTableView:)]) { [self.autoCompleteDelegate autoCompleteTextField:self didShowAutoCompleteTableView:self.autoCompleteTableView]; } } else { [self closeAutoCompleteTableView]; [self restoreOriginalShadowProperties]; [self.autoCompleteTableView.layer setShadowOpacity:0.0]; } } - (void)closeAutoCompleteTableView { if ([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:willHideAutoCompleteTableView:)]) { [self.autoCompleteDelegate autoCompleteTextField:self willHideAutoCompleteTableView:self.autoCompleteTableView]; } [self.autoCompleteTableView removeFromSuperview]; [self restoreOriginalShadowProperties]; if ([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:didHideAutoCompleteTableView:)]) { [self.autoCompleteDelegate autoCompleteTextField:self didHideAutoCompleteTableView:self.autoCompleteTableView]; } } #pragma mark - Setters - (void)setDefaultValuesForVariables { [self setClipsToBounds:NO]; [self setAutoCompleteFetchRequestDelay:DefaultAutoCompleteRequestDelay]; [self setSortAutoCompleteSuggestionsByClosestMatch:YES]; [self setApplyBoldEffectToAutoCompleteSuggestions:YES]; [self setShowTextFieldDropShadowWhenAutoCompleteTableIsOpen:YES]; [self setShouldResignFirstResponderFromKeyboardAfterSelectionOfAutoCompleteRows:YES]; [self setAutoCompleteRowHeight:40]; [self setAutoCompleteFontSize:13]; [self setMaximumNumberOfAutoCompleteRows:3]; [self setPartOfAutoCompleteRowHeightToCut:0.5f]; [self setMaximumEditDistance:100]; [self setRequireAutoCompleteSuggestionsToMatchInputExactly:NO]; [self setAutoCompleteTableCellBackgroundColor:[UIColor clearColor]]; UIFont *regularFont = [UIFont systemFontOfSize:13]; [self setAutoCompleteRegularFontName:regularFont.fontName]; UIFont *boldFont = [UIFont boldSystemFontOfSize:13]; [self setAutoCompleteBoldFontName:boldFont.fontName]; [self setAutoCompleteSuggestions:[NSMutableArray array]]; [self setAutoCompleteSortQueue:[NSOperationQueue new]]; self.autoCompleteSortQueue.name = [NSString stringWithFormat:@"Autocomplete Queue %i", arc4random()]; [self setAutoCompleteFetchQueue:[NSOperationQueue new]]; self.autoCompleteFetchQueue.name = [NSString stringWithFormat:@"Fetch Queue %i", arc4random()]; } - (void)setAutoCompleteTableForKeyboardAppearance { [self resetKeyboardAutoCompleteTableFrameForNumberOfRows:self.maximumNumberOfAutoCompleteRows]; [self.autoCompleteTableView setContentInset:UIEdgeInsetsZero]; [self.autoCompleteTableView setScrollIndicatorInsets:UIEdgeInsetsZero]; [self setInputAccessoryView:self.autoCompleteTableView]; } - (void)setAutoCompleteTableForDropDownAppearance { [self resetDropDownAutoCompleteTableFrameForNumberOfRows:self.maximumNumberOfAutoCompleteRows]; [self.autoCompleteTableView setContentInset:self.autoCompleteContentInsets]; [self.autoCompleteTableView setScrollIndicatorInsets:self.autoCompleteScrollIndicatorInsets]; [self setInputAccessoryView:nil]; } - (void)setAutoCompleteTableViewHidden:(BOOL)autoCompleteTableViewHidden { [self.autoCompleteTableView setHidden:autoCompleteTableViewHidden]; } - (void)setAutoCompleteTableBackgroundColor:(UIColor *)autoCompleteTableBackgroundColor { [self.autoCompleteTableView setBackgroundColor:autoCompleteTableBackgroundColor]; _autoCompleteTableBackgroundColor = autoCompleteTableBackgroundColor; } - (void)setAutoCompleteTableBorderWidth:(CGFloat)autoCompleteTableBorderWidth { [self.autoCompleteTableView.layer setBorderWidth:autoCompleteTableBorderWidth]; _autoCompleteTableBorderWidth = autoCompleteTableBorderWidth; } - (void)setAutoCompleteTableBorderColor:(UIColor *)autoCompleteTableBorderColor { [self.autoCompleteTableView.layer setBorderColor:[autoCompleteTableBorderColor CGColor]]; _autoCompleteTableBorderColor = autoCompleteTableBorderColor; } - (void)setAutoCompleteContentInsets:(UIEdgeInsets)autoCompleteContentInsets { [self.autoCompleteTableView setContentInset:autoCompleteContentInsets]; _autoCompleteContentInsets = autoCompleteContentInsets; } - (void)setAutoCompleteScrollIndicatorInsets:(UIEdgeInsets)autoCompleteScrollIndicatorInsets { [self.autoCompleteTableView setScrollIndicatorInsets:autoCompleteScrollIndicatorInsets]; _autoCompleteScrollIndicatorInsets = autoCompleteScrollIndicatorInsets; } - (void)resetKeyboardAutoCompleteTableFrameForNumberOfRows:(NSInteger)numberOfRows { [self.autoCompleteTableView.layer setCornerRadius:0]; CGRect newAutoCompleteTableViewFrame = [self autoCompleteTableViewFrameForTextField:self forNumberOfRows:numberOfRows]; [self.autoCompleteTableView setFrame:newAutoCompleteTableViewFrame]; [self.autoCompleteTableView setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; [self.autoCompleteTableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO]; } - (void)resetDropDownAutoCompleteTableFrameForNumberOfRows:(NSInteger)numberOfRows { [self.autoCompleteTableView.layer setCornerRadius:self.autoCompleteTableCornerRadius]; CGRect newAutoCompleteTableViewFrame = [self autoCompleteTableViewFrameForTextField:self forNumberOfRows:numberOfRows]; [self.autoCompleteTableView setFrame:newAutoCompleteTableViewFrame]; [self.autoCompleteTableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO]; } - (void)registerAutoCompleteCellNib:(UINib *)nib forCellReuseIdentifier:(NSString *)reuseIdentifier { NSAssert(self.autoCompleteTableView, @"Must have an autoCompleteTableView to register cells to."); if(self.reuseIdentifier){ [self unregisterAutoCompleteCellForReuseIdentifier:self.reuseIdentifier]; } [self.autoCompleteTableView registerNib:nib forCellReuseIdentifier:reuseIdentifier]; [self setReuseIdentifier:reuseIdentifier]; } - (void)registerAutoCompleteCellClass:(Class)cellClass forCellReuseIdentifier:(NSString *)reuseIdentifier { NSAssert(self.autoCompleteTableView, @"Must have an autoCompleteTableView to register cells to."); if(self.reuseIdentifier){ [self unregisterAutoCompleteCellForReuseIdentifier:self.reuseIdentifier]; } BOOL classSettingSupported = NO; classSettingSupported = [self.autoCompleteTableView respondsToSelector:@selector(registerClass:forCellReuseIdentifier:)]; NSAssert(classSettingSupported, @"Unable to set class for cell for autocomplete table, in iOS 5.0 you can set a custom NIB for a reuse identifier to get similar functionality."); [self.autoCompleteTableView registerClass:cellClass forCellReuseIdentifier:reuseIdentifier]; [self setReuseIdentifier:reuseIdentifier]; } - (void)unregisterAutoCompleteCellForReuseIdentifier:(NSString *)reuseIdentifier { [self.autoCompleteTableView registerNib:nil forCellReuseIdentifier:reuseIdentifier]; } - (void)styleAutoCompleteTableForBorderStyle:(UITextBorderStyle)borderStyle { if([self.autoCompleteDelegate respondsToSelector:@selector(autoCompleteTextField:shouldStyleAutoCompleteTableView:forBorderStyle:)]){ if(![self.autoCompleteDelegate autoCompleteTextField:self shouldStyleAutoCompleteTableView:self.autoCompleteTableView forBorderStyle:borderStyle]){ return; } } switch (borderStyle) { case UITextBorderStyleRoundedRect: [self setRoundedRectStyleForAutoCompleteTableView]; break; case UITextBorderStyleBezel: case UITextBorderStyleLine: [self setLineStyleForAutoCompleteTableView]; break; case UITextBorderStyleNone: [self setNoneStyleForAutoCompleteTableView]; break; default: break; } } - (void)setRoundedRectStyleForAutoCompleteTableView { [self setAutoCompleteTableCornerRadius:8.0]; [self setAutoCompleteTableOriginOffset:CGSizeMake(0, -18)]; [self setAutoCompleteScrollIndicatorInsets:UIEdgeInsetsMake(18, 0, 0, 0)]; [self setAutoCompleteContentInsets:UIEdgeInsetsMake(18, 0, 0, 0)]; if(self.backgroundColor == [UIColor clearColor]){ [self setAutoCompleteTableBackgroundColor:[UIColor whiteColor]]; } else { [self setAutoCompleteTableBackgroundColor:self.backgroundColor]; } } - (void)setLineStyleForAutoCompleteTableView { [self setAutoCompleteTableCornerRadius:0.0]; [self setAutoCompleteTableOriginOffset:CGSizeZero]; [self setAutoCompleteScrollIndicatorInsets:UIEdgeInsetsZero]; [self setAutoCompleteContentInsets:UIEdgeInsetsZero]; [self setAutoCompleteTableBorderWidth:1.0]; [self setAutoCompleteTableBorderColor:[UIColor colorWithWhite:0.0 alpha:0.5]]; if(self.backgroundColor == [UIColor clearColor]){ [self setAutoCompleteTableBackgroundColor:[UIColor whiteColor]]; } else { [self setAutoCompleteTableBackgroundColor:self.backgroundColor]; } } - (void)setNoneStyleForAutoCompleteTableView { [self setAutoCompleteTableCornerRadius:8.0]; [self setAutoCompleteTableOriginOffset:CGSizeMake(0, 7)]; [self setAutoCompleteScrollIndicatorInsets:UIEdgeInsetsZero]; [self setAutoCompleteContentInsets:UIEdgeInsetsZero]; [self setAutoCompleteTableBorderWidth:1.0]; UIColor *lightBlueColor = [UIColor colorWithRed:181/255.0 green:204/255.0 blue:255/255.0 alpha:1.0]; [self setAutoCompleteTableBorderColor:lightBlueColor]; UIColor *blueTextColor = [UIColor colorWithRed:23/255.0 green:119/255.0 blue:206/255.0 alpha:1.0]; [self setAutoCompleteTableCellTextColor:blueTextColor]; if(self.backgroundColor == [UIColor clearColor]){ [self setAutoCompleteTableBackgroundColor:[UIColor whiteColor]]; } else { [self setAutoCompleteTableBackgroundColor:self.backgroundColor]; } } - (void)saveCurrentShadowProperties { [self setOriginalShadowColor:self.layer.shadowColor]; [self setOriginalShadowOffset:self.layer.shadowOffset]; [self setOriginalShadowOpacity:self.layer.shadowOpacity]; } - (void)restoreOriginalShadowProperties { [self.layer setShadowColor:self.originalShadowColor]; [self.layer setShadowOffset:self.originalShadowOffset]; [self.layer setShadowOpacity:self.originalShadowOpacity]; } - (void)reloadData { [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fetchAutoCompleteSuggestions) object:nil]; [self performSelector:@selector(fetchAutoCompleteSuggestions) withObject:nil afterDelay:self.autoCompleteFetchRequestDelay]; } #pragma mark - Getters - (BOOL)autoCompleteTableViewHidden { return self.autoCompleteTableView.hidden; } - (void)fetchAutoCompleteSuggestions { if(self.disableAutoCompleteTableUserInteractionWhileFetching){ [self.autoCompleteTableView setUserInteractionEnabled:NO]; } [self.autoCompleteFetchQueue cancelAllOperations]; MLPAutoCompleteFetchOperation *fetchOperation = [[MLPAutoCompleteFetchOperation alloc] initWithDelegate:self completionsDataSource:self.autoCompleteDataSource autoCompleteTextField:self]; [self.autoCompleteFetchQueue addOperation:fetchOperation]; } #pragma mark - Factory Methods - (UITableView *)newAutoCompleteTableViewForTextField:(MLPAutoCompleteTextField *)textField { CGRect dropDownTableFrame = [self autoCompleteTableViewFrameForTextField:textField]; UITableView *newTableView = [[UITableView alloc] initWithFrame:dropDownTableFrame style:UITableViewStylePlain]; [newTableView setDelegate:textField]; [newTableView setDataSource:textField]; [newTableView setScrollEnabled:YES]; [newTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; return newTableView; } - (CGRect)autoCompleteTableViewFrameForTextField:(MLPAutoCompleteTextField *)textField forNumberOfRows:(NSInteger)numberOfRows { #if BROKEN // TODO: Reimplement this code for people using table views. Right now it breaks // more normal use cases because of UILayoutContainerView CGRect newTableViewFrame = [self autoCompleteTableViewFrameForTextField:textField]; UIView *rootView = [textField.window.subviews objectAtIndex:0]; CGRect textFieldFrameInContainerView = [rootView convertRect:textField.bounds fromView:textField]; CGFloat textfieldTopInset = textField.autoCompleteTableView.contentInset.top; CGFloat converted_originY = textFieldFrameInContainerView.origin.y + textfieldTopInset; #else CGRect newTableViewFrame = [self autoCompleteTableViewFrameForTextField:textField]; CGFloat textfieldTopInset = textField.autoCompleteTableView.contentInset.top; #endif CGFloat height = [self autoCompleteTableHeightForTextField:textField withNumberOfRows:numberOfRows]; newTableViewFrame.size.height = height; #if BROKEN newTableViewFrame.origin.y = converted_originY; #endif if(!textField.autoCompleteTableAppearsAsKeyboardAccessory){ newTableViewFrame.size.height += textfieldTopInset; } return newTableViewFrame; } - (CGFloat)autoCompleteTableHeightForTextField:(MLPAutoCompleteTextField *)textField withNumberOfRows:(NSInteger)numberOfRows { CGFloat maximumHeightMultiplier = (textField.maximumNumberOfAutoCompleteRows - textField.partOfAutoCompleteRowHeightToCut); CGFloat heightMultiplier; if(numberOfRows >= textField.maximumNumberOfAutoCompleteRows){ heightMultiplier = maximumHeightMultiplier; } else { heightMultiplier = numberOfRows; } CGFloat height = textField.autoCompleteRowHeight * heightMultiplier; return height; } - (CGRect)autoCompleteTableViewFrameForTextField:(MLPAutoCompleteTextField *)textField { CGRect frame = CGRectZero; if (CGRectGetWidth(self.autoCompleteTableFrame) > 0){ frame = self.autoCompleteTableFrame; } else { frame = textField.frame; frame.origin.y += textField.frame.size.height; } frame.origin.x += textField.autoCompleteTableOriginOffset.width; frame.origin.y += textField.autoCompleteTableOriginOffset.height; frame.size.height += textField.autoCompleteTableSizeOffset.height; frame.size.width += textField.autoCompleteTableSizeOffset.width; frame = CGRectInset(frame, 1, 0); return frame; } - (NSAttributedString *)boldedString:(NSString *)string withRange:(NSRange)boldRange { UIFont *boldFont = [UIFont fontWithName:self.autoCompleteBoldFontName size:self.autoCompleteFontSize]; UIFont *regularFont = [UIFont fontWithName:self.autoCompleteRegularFontName size:self.autoCompleteFontSize]; NSDictionary *boldTextAttributes = @{NSFontAttributeName : boldFont}; NSDictionary *regularTextAttributes = @{NSFontAttributeName : regularFont}; NSDictionary *firstAttributes; NSDictionary *secondAttributes; if(self.reverseAutoCompleteSuggestionsBoldEffect){ firstAttributes = regularTextAttributes; secondAttributes = boldTextAttributes; } else { firstAttributes = boldTextAttributes; secondAttributes = regularTextAttributes; } NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:string attributes:firstAttributes]; [attributedText setAttributes:secondAttributes range:boldRange]; return attributedText; } @end #pragma mark - #pragma mark - MLPAutoCompleteFetchOperation @implementation MLPAutoCompleteFetchOperation{ dispatch_semaphore_t sentinelSemaphore; } - (void) cancel { if (sentinelSemaphore) dispatch_semaphore_signal(sentinelSemaphore); [super cancel]; } - (void)main { @autoreleasepool { if (self.isCancelled){ return; } if([self.dataSource respondsToSelector:@selector(autoCompleteTextField:possibleCompletionsForString:completionHandler:)]){ __weak MLPAutoCompleteFetchOperation *operation = self; sentinelSemaphore = dispatch_semaphore_create(0); [self.dataSource autoCompleteTextField:self.textField possibleCompletionsForString:self.incompleteString completionHandler:^(NSArray *suggestions){ [operation performSelector:@selector(didReceiveSuggestions:) withObject:suggestions]; dispatch_semaphore_signal(sentinelSemaphore); }]; dispatch_semaphore_wait(sentinelSemaphore, DISPATCH_TIME_FOREVER); if(self.isCancelled){ return; } } else if ([self.dataSource respondsToSelector:@selector(autoCompleteTextField:possibleCompletionsForString:)]){ NSArray *results = [self.dataSource autoCompleteTextField:self.textField possibleCompletionsForString:self.incompleteString]; if(!self.isCancelled){ [self didReceiveSuggestions:results]; } } else { NSAssert(0, @"An autocomplete datasource must implement either autoCompleteTextField:possibleCompletionsForString: or autoCompleteTextField:possibleCompletionsForString:completionHandler:"); } } } - (void)didReceiveSuggestions:(NSArray *)suggestions { if(suggestions == nil){ suggestions = [NSArray array]; } if(!self.isCancelled){ if(suggestions.count){ NSObject *firstObject = nil; firstObject = suggestions[0]; NSAssert([firstObject isKindOfClass:[NSString class]] || [firstObject conformsToProtocol:@protocol(MLPAutoCompletionObject)], @"MLPAutoCompleteTextField expects an array with objects that are either strings or conform to the MLPAutoCompletionObject protocol for possible completions."); } NSDictionary *resultsInfo = @{kFetchedTermsKey: suggestions, kFetchedStringKey : self.incompleteString}; [(NSObject *)self.delegate performSelectorOnMainThread:@selector(autoCompleteTermsDidFetch:) withObject:resultsInfo waitUntilDone:NO]; }; } - (id)initWithDelegate:(id)aDelegate completionsDataSource:(id)aDataSource autoCompleteTextField:(MLPAutoCompleteTextField *)aTextField { self = [super init]; if (self) { [self setDelegate:aDelegate]; [self setTextField:aTextField]; [self setDataSource:aDataSource]; [self setIncompleteString:aTextField.text]; if(!self.incompleteString){ self.incompleteString = @""; } } return self; } - (void)dealloc { [self setDelegate:nil]; [self setTextField:nil]; [self setDataSource:nil]; [self setIncompleteString:nil]; } @end #pragma mark - #pragma mark - MLPAutoCompleteSortOperation @implementation MLPAutoCompleteSortOperation - (void)main { @autoreleasepool { if (self.isCancelled){ return; } NSArray *results = [self sortedCompletionsForString:self.incompleteString withPossibleStrings:self.possibleCompletions]; if (self.isCancelled){ return; } if(!self.isCancelled){ [(NSObject *)self.delegate performSelectorOnMainThread:@selector(autoCompleteTermsDidSort:) withObject:results waitUntilDone:NO]; } } } - (id)initWithDelegate:(id)aDelegate incompleteString:(NSString *)string possibleCompletions:(NSArray *)possibleStrings { self = [super init]; if (self) { [self setDelegate:aDelegate]; [self setIncompleteString:string]; [self setPossibleCompletions:possibleStrings]; } return self; } - (NSArray *)sortedCompletionsForString:(NSString *)inputString withPossibleStrings:(NSArray *)possibleTerms { if([inputString isEqualToString:@""]){ return possibleTerms; } if(self.isCancelled){ return [NSArray array]; } NSMutableArray *editDistances = [NSMutableArray arrayWithCapacity:possibleTerms.count]; NSInteger maxEditDistance = self.delegate.maximumEditDistanceForAutoCompleteTerms; for(NSObject *originalObject in possibleTerms) { NSString *currentString; if([originalObject isKindOfClass:[NSString class]]){ currentString = (NSString *)originalObject; } else if ([originalObject conformsToProtocol:@protocol(MLPAutoCompletionObject)]){ currentString = [(id )originalObject autocompleteString]; } else { NSAssert(0, @"Autocompletion terms must either be strings or objects conforming to the MLPAutoCompleteObject protocol."); } if(self.isCancelled){ return [NSArray array]; } NSUInteger maximumRange = (inputString.length < currentString.length) ? inputString.length : currentString.length; float editDistanceOfCurrentString = [inputString asciiLevenshteinDistanceWithString:[currentString substringWithRange:NSMakeRange(0, maximumRange)]]; if(editDistanceOfCurrentString > maxEditDistance){ continue; } NSDictionary * stringsWithEditDistances = @{kSortInputStringKey : currentString , kSortObjectKey : originalObject, kSortEditDistancesKey : [NSNumber numberWithFloat:editDistanceOfCurrentString]}; [editDistances addObject:stringsWithEditDistances]; } if(self.isCancelled){ return [NSArray array]; } [editDistances sortUsingComparator:^(NSDictionary *string1Dictionary, NSDictionary *string2Dictionary){ return [string1Dictionary[kSortEditDistancesKey] compare:string2Dictionary[kSortEditDistancesKey]]; }]; NSMutableArray *prioritySuggestions = [NSMutableArray array]; NSMutableArray *otherSuggestions = [NSMutableArray array]; for(NSDictionary *stringsWithEditDistances in editDistances){ if(self.isCancelled){ return [NSArray array]; } NSObject *autoCompleteObject = stringsWithEditDistances[kSortObjectKey]; NSString *suggestedString = stringsWithEditDistances[kSortInputStringKey]; NSArray *suggestedStringComponents = [suggestedString componentsSeparatedByString:@" "]; BOOL suggestedStringDeservesPriority = NO; for(NSString *component in suggestedStringComponents){ NSRange occurrenceOfInputString = [[component lowercaseString] rangeOfString:[inputString lowercaseString]]; if (occurrenceOfInputString.length != 0 && occurrenceOfInputString.location == 0) { suggestedStringDeservesPriority = YES; [prioritySuggestions addObject:autoCompleteObject]; break; } if([inputString length] <= 1){ //if the input string is very short, don't check anymore components of the input string. break; } } if(!suggestedStringDeservesPriority){ [otherSuggestions addObject:autoCompleteObject]; } } NSMutableArray *results = [NSMutableArray array]; [results addObjectsFromArray:prioritySuggestions]; [results addObjectsFromArray:otherSuggestions]; return [NSArray arrayWithArray:results]; } - (void)dealloc { [self setDelegate:nil]; [self setIncompleteString:nil]; [self setPossibleCompletions:nil]; } @end ================================================ FILE: MLPAutoCompleteTextField/MLPAutoCompleteTextFieldDataSource.h ================================================ /* // MLPAutoCompleteTextFieldDataSource.h // // // Created by Eddy Borja on 12/29/12. // Copyright (c) 2013 Mainloop LLC. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import @class MLPAutoCompleteTextField; @protocol MLPAutoCompleteTextFieldDataSource @optional //One of these two methods must be implemented to fetch autocomplete terms. /* When you have the suggestions ready you must call the completionHandler block with an NSArray of strings or objects implementing the MLPAutoCompletionObject protocol that could be used as possible completions for the given string in textField. */ - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField possibleCompletionsForString:(NSString *)string completionHandler:(void(^)(NSArray *suggestions))handler; /* Like the above, this method should return an NSArray of strings or objects implementing the MLPAutoCompletionObject protocol that could be used as possible completions for the given string in textField. This method will be called asynchronously, so an immediate return is not necessary. */ - (NSArray *)autoCompleteTextField:(MLPAutoCompleteTextField *)textField possibleCompletionsForString:(NSString *)string; @end ================================================ FILE: MLPAutoCompleteTextField/MLPAutoCompleteTextFieldDelegate.h ================================================ /* // MLPAutoCompleteTextFieldDelegate.h // // // Created by Eddy Borja on 2/5/13. // Copyright (c) 2013 Mainloop LLC. All rights reserved. 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import #import "MLPAutoCompletionObject.h" @class MLPAutoCompleteTextField; @protocol MLPAutoCompleteTextFieldDelegate @optional - (BOOL)autoCompleteTextField:(MLPAutoCompleteTextField *)textField shouldStyleAutoCompleteTableView:(UITableView *)autoCompleteTableView forBorderStyle:(UITextBorderStyle)borderStyle; /*IndexPath corresponds to the order of strings within the autocomplete table, not the original data source.*/ - (BOOL)autoCompleteTextField:(MLPAutoCompleteTextField *)textField shouldConfigureCell:(UITableViewCell *)cell withAutoCompleteString:(NSString *)autocompleteString withAttributedString:(NSAttributedString *)boldedString forAutoCompleteObject:(id)autocompleteObject forRowAtIndexPath:(NSIndexPath *)indexPath; /*IndexPath corresponds to the order of strings within the autocomplete table, not the original data source. autoCompleteObject may be nil if the selectedString had no object associated with it. */ - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField didSelectAutoCompleteString:(NSString *)selectedString withAutoCompleteObject:(id)selectedObject forRowAtIndexPath:(NSIndexPath *)indexPath; - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField willShowAutoCompleteTableView:(UITableView *)autoCompleteTableView; - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField didShowAutoCompleteTableView:(UITableView *)autoCompleteTableView; - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField willHideAutoCompleteTableView:(UITableView *)autoCompleteTableView; - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField didHideAutoCompleteTableView:(UITableView *)autoCompleteTableView; - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField didChangeNumberOfSuggestions:(NSInteger)numberOfSuggestions; @end ================================================ FILE: MLPAutoCompleteTextField/MLPAutoCompletionObject.h ================================================ /* // MLPAutoCompletionObject.h // // // Created by Eddy Borja on 4/19/13. // Copyright (c) 2013 Mainloop LLC. All rights reserved. // 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import /* In some cases you may want strings in an autocomplete menu to be associated to some kind of model object (For example: a list of names may be associated with friend objects and you want to track which friend object a user selects based on what name they choose from the autocomplete list.) In order to pass your model objects straight to the MLPAutoCompleteTextFieldDataSource's autoCompleteTextField:possibleCompletionsForString: method, your model object must implement this protocol. */ @protocol MLPAutoCompletionObject @required /*Return the string that should be displayed in the autocomplete menu that represents this object. (For example: a person's name.)*/ - (NSString *)autocompleteString; @end ================================================ FILE: MLPAutoCompleteTextField/NSString+Levenshtein.h ================================================ // // NSString+Levenshtein.h // // Created by Mark Aufflick on 9/11/09. // Copyright 2009 pumptheory.com. All rights reserved. // // Based loosely on the NSString(Levenshtein) code by Rick Bourner // rick@bourner.com // /* Copyright (c) 2009, Mark Aufflick All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Mark Aufflick nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY MARK AUFFLICK ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MARK AUFFLICK BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import #import // used to indicate that stringB is nil #define LEV_INF_DISTANCE FLT_MAX @interface NSString(Levenshtein) - (float) asciiLevenshteinDistanceWithString: (NSString *)stringB; - (float) asciiLevenshteinDistanceWithString: (NSString *)stringB skippingCharacterSet: (NSCharacterSet *)charset; @end ================================================ FILE: MLPAutoCompleteTextField/NSString+Levenshtein.m ================================================ // // NSString+Levenshtein.m // // Created by Mark Aufflick on 9/11/09. // mark@aufflick.com // // Based somewhat on the NSString(Levenshtein) code by Rick Bourner // rick@bourner.com // which in turn implements a variation on the Wagner-Fischer algorithm // /* Copyright (c) 2009, Mark Aufflick All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Mark Aufflick nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY MARK AUFFLICK ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MARK AUFFLICK BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "NSString+Levenshtein.h" // private util function int smallestOf(int a, int b, int c); @implementation NSString (Levenshtein) - (float) asciiLevenshteinDistanceWithString: (NSString *)stringB { return [self asciiLevenshteinDistanceWithString:stringB skippingCharacterSet:nil]; } - (float) asciiLevenshteinDistanceWithString: (NSString *)stringB skippingCharacterSet: (NSCharacterSet *)charset { // try to convince caller that a nil object is *really* different from any string if (!stringB) return LEV_INF_DISTANCE; // strip chars from the requested charset (if any) NSString *stringA; if (charset) { stringA = [[self componentsSeparatedByCharactersInSet:charset] componentsJoinedByString:@""]; stringB = [[stringB componentsSeparatedByCharactersInSet:charset] componentsJoinedByString:@""]; } else { stringA = self; } // converting to ASCII to normalize characters with accents etc. // and also so we can use treat the string as an array of char * NSData *dataA = [stringA dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; NSData *dataB = [stringB dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; // not really cstrings, since not nul terminated const char *cstringA = [dataA bytes]; const char *cstringB = [dataB bytes]; // Calculate Levenshtein distance // Step 1 int k, i, j, cost, * d, distance; NSUInteger n = [dataA length]; NSUInteger m = [dataB length]; if( n++ != 0 && m++ != 0 ) { d = malloc( sizeof(int) * m * n ); // Step 2 for( k = 0; k < n; k++) d[k] = k; for( k = 0; k < m; k++) d[ k * n ] = k; // Step 3 and 4 for( i = 1; i < n; i++ ) for( j = 1; j < m; j++ ) { // Step 5 if( cstringA[i-1] == cstringB[j-1] ) cost = 0; else cost = 1; // Step 6 d[ j * n + i ] = smallestOf( d[ (j - 1) * n + i ] + 1, d[ j * n + i - 1 ] + 1, d[ (j - 1) * n + i -1 ] + cost ); } distance = d[ n * m - 1 ]; free( d ); return distance; } return 0.0; } // return the minimum of a, b and c int smallestOf(int a, int b, int c) { int min = a; if ( b < min ) min = b; if( c < min ) min = c; return min; } @end ================================================ FILE: RCTAutoComplete.android.js ================================================ /** * Stub of RCTAutoComplete for Android. * * @providesModule RCTAutoComplete * @flow */ 'use strict'; var warning = require('warning'); var RCTAutoComplete = { test: function() { warning("Not yet implemented for Android."); } }; module.exports = RCTAutoComplete; ================================================ FILE: RCTAutoComplete.h ================================================ #import #import #import "MLPAutoCompleteTextField/MLPAutoCompleteTextField.h" @interface RCTAutoComplete : RCTViewManager @end ================================================ FILE: RCTAutoComplete.ios.js ================================================ var React = require("react"); var PropTypes = require("prop-types"); var { requireNativeComponent } = require("react-native"); var NativeAutoComplete = requireNativeComponent("RCTAutoComplete", null); class RCTAutoComplete extends React.Component { constructor(props) { super(props); this.state = { mostRecentEventCount: 0 }; this._getText = this._getText.bind(this); this._onChange = this._onChange.bind(this); this._onFocus = this._onFocus.bind(this); this._onBlur = this._onBlur.bind(this); } _getText() { return typeof this.props.value === "string" ? this.props.value : this.props.defaultValue; } _onChange(event) { var text = event.nativeEvent.text; var eventCount = event.nativeEvent.eventCount; this.props.onChange && this.props.onChange(event); this.props.onChangeText && this.props.onChangeText(text); this.setState({ mostRecentEventCount: eventCount }, () => { // This is a controlled component, so make sure to force the native value // to match. Most usage shouldn't need this, but if it does this will be // more correct but might flicker a bit and/or cause the cursor to jump. if (text !== this.props.value && typeof this.props.value === "string") { this.refs.input && this.refs.input.setNativeProps({ text: this.props.value }); } }); event.nativeEvent.possibleCompletionsForString && this.props.onTyping && this.props.onTyping(event.nativeEvent.possibleCompletionsForString); event.nativeEvent.didSelectAutoCompleteString && this.props.onSelect && this.props.onSelect(event.nativeEvent.didSelectAutoCompleteString); } _onFocus(event) { if (this.props.onFocus) { this.props.onFocus(event); } } _onBlur(event) { if (this.props.onBlur) { this.props.onBlur(event); } } render() { var props = Object.assign({}, this.props); return ( true} text={this._getText()} mostRecentEventCount={this.state.mostRecentEventCount} /> ); } } RCTAutoComplete.PropTypes = { /** * If false, disables auto-correct. The default value is true. */ autoCorrect: PropTypes.bool, /** * The string that will be rendered before text input has been entered */ placeholder: PropTypes.string, /** * When the clear button should appear on the right side of the text view * @platform ios */ clearButtonMode: PropTypes.oneOf([ "never", "while-editing", "unless-editing", "always" ]), /** * If true, clears the text field automatically when editing begins * @platform ios */ clearTextOnFocus: PropTypes.bool, /** * Determines which keyboard to open, e.g.`numeric`. * * The following values work across platforms: * - default * - numeric * - email-address */ keyboardType: PropTypes.oneOf([ // Cross-platform "default", "numeric", "email-address", // iOS-only "ascii-capable", "numbers-and-punctuation", "url", "number-pad", "phone-pad", "name-phone-pad", "decimal-pad", "twitter", "web-search" ]), /** * Determines how the return key should look. * @platform ios */ returnKeyType: PropTypes.oneOf([ "default", "go", "google", "join", "next", "route", "search", "send", "yahoo", "done", "emergency-call" ]), /** * If true, the keyboard disables the return key when there is no text and * automatically enables it when there is text. The default value is false. * @platform ios */ enablesReturnKeyAutomatically: PropTypes.bool, /** * Can tell TextInput to automatically capitalize certain characters. * * - characters: all characters, * - words: first letter of each word * - sentences: first letter of each sentence (default) * - none: don't auto capitalize anything */ autoCapitalize: PropTypes.oneOf(["none", "sentences", "words", "characters"]), /** * Set the position of the cursor from where editing will begin. * @platorm android */ textAlign: PropTypes.oneOf(["start", "center", "end"]), cellComponent: PropTypes.string, suggestions: PropTypes.array, autoCompleteFetchRequestDelay: PropTypes.number, maximumNumberOfAutoCompleteRows: PropTypes.number, showTextFieldDropShadowWhenAutoCompleteTableIsOpen: PropTypes.bool, autoCompleteTableViewHidden: PropTypes.bool, autoCompleteTableBorderColor: PropTypes.string, autoCompleteTableBorderWidth: PropTypes.number, autoCompleteTableBackgroundColor: PropTypes.string, autoCompleteTableCornerRadius: PropTypes.number, autoCompleteTableTopOffset: PropTypes.number, autoCompleteTableLeftOffset: PropTypes.number, autoCompleteTableSizeOffset: PropTypes.number, autoCompleteRowHeight: PropTypes.number, autoCompleteFontSize: PropTypes.number, autoCompleteRegularFontName: PropTypes.string, autoCompleteBoldFontName: PropTypes.string, autoCompleteTableCellTextColor: PropTypes.string, autoCompleteTableCellBackgroundColor: PropTypes.string, applyBoldEffectToAutoCompleteSuggestions: PropTypes.bool, reverseAutoCompleteSuggestionsBoldEffect: PropTypes.bool, disableAutoCompleteTableUserInteractionWhileFetching: PropTypes.bool }; RCTAutoComplete.defaultProps = { autoCorrect: false, clearTextOnFocus: true, showTextFieldDropShadowWhenAutoCompleteTableIsOpen: true, reverseAutoCompleteSuggestionsBoldEffect: false, autoCompleteTableViewHidden: false, applyBoldEffectToAutoCompleteSuggestions: false, enablesReturnKeyAutomatically: false, disableAutoCompleteTableUserInteractionWhileFetching: false, placeholder: "Search a name", clearButtonMode: "while-editing", returnKeyType: "go", textAlign: "center", autoCompleteTableTopOffset: 10, autoCompleteTableLeftOffset: 20, autoCompleteTableSizeOffset: 40, autoCompleteTableBorderColor: "lightblue", autoCompleteTableBackgroundColor: "azure", autoCompleteTableCornerRadius: 8, autoCompleteTableBorderWidth: 1, autoCompleteFontSize: 18, autoCompleteRegularFontName: "Helvetica Neue", autoCompleteBoldFontName: "Helvetica Bold", autoCompleteTableCellTextColor: "dimgray", autoCompleteRowHeight: 40, autoCompleteFetchRequestDelay: 100, maximumNumberOfAutoCompleteRows: 6 }; module.exports = RCTAutoComplete; ================================================ FILE: RCTAutoComplete.m ================================================ #import #import "RCTAutoComplete.h" #import "RCTTableViewCell.h" #import #import "UIView+React.h" #import "AutoCompleteView.h" #import "DictionaryAutoCompleteObject.h" #import @implementation RCTAutoComplete RCT_EXPORT_MODULE() // From AutoCompleteView.m RCT_EXPORT_VIEW_PROPERTY(suggestions, NSArray) RCT_EXPORT_VIEW_PROPERTY(maximumNumberOfAutoCompleteRows, NSInteger) RCT_EXPORT_VIEW_PROPERTY(applyBoldEffectToAutoCompleteSuggestions, BOOL) RCT_EXPORT_VIEW_PROPERTY(reverseAutoCompleteSuggestionsBoldEffect, BOOL) RCT_EXPORT_VIEW_PROPERTY(showTextFieldDropShadowWhenAutoCompleteTableIsOpen, BOOL); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableViewHidden, BOOL); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableBorderColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableBorderWidth, CGFloat); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableBackgroundColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableCellBackgroundColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableCellTextColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(autoCompleteTableCornerRadius, CGFloat); RCT_EXPORT_VIEW_PROPERTY(autoCompleteRowHeight, CGFloat); RCT_EXPORT_VIEW_PROPERTY(autoCompleteFontSize, CGFloat); RCT_EXPORT_VIEW_PROPERTY(autoCompleteBoldFontName, NSString); RCT_EXPORT_VIEW_PROPERTY(autoCompleteRegularFontName, NSString); RCT_CUSTOM_VIEW_PROPERTY(autoCompleteTableTopOffset, NSInteger, AutoCompleteView) { CGSize size = view.autoCompleteTableOriginOffset; size.height = [RCTConvert NSInteger:json]; view.autoCompleteTableOriginOffset = size; } RCT_CUSTOM_VIEW_PROPERTY(autoCompleteTableLeftOffset, NSInteger, AutoCompleteView) { CGSize size = view.autoCompleteTableOriginOffset; size.width = [RCTConvert NSInteger:json]; view.autoCompleteTableOriginOffset = size; } RCT_CUSTOM_VIEW_PROPERTY(autoCompleteTableSizeOffset, NSInteger, AutoCompleteView) { view.autoCompleteTableSizeOffset = CGSizeMake([RCTConvert NSInteger:json], 0); } RCT_CUSTOM_VIEW_PROPERTY(cellComponent, NSString*, AutoCompleteView) { // Register RCTTableViewCellBridge (hosts cell React Component) [view registerAutoCompleteCellClass:[RCTTableViewCell class] forCellReuseIdentifier:@"RCTTableViewCell"]; // Set cell React Component [view setCellComponent:[RCTConvert NSString:json]]; } RCT_EXPORT_VIEW_PROPERTY(autoCompleteFetchRequestDelay, NSTimeInterval); // From RCTTextFieldManager.m RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) RCT_EXPORT_VIEW_PROPERTY(text, NSString) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(clearButtonMode, UITextFieldViewMode) RCT_REMAP_VIEW_PROPERTY(clearTextOnFocus, clearsOnBeginEditing, BOOL) RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL) RCT_EXPORT_VIEW_PROPERTY(keyboardType, UIKeyboardType) RCT_EXPORT_VIEW_PROPERTY(returnKeyType, UIReturnKeyType) RCT_EXPORT_VIEW_PROPERTY(enablesReturnKeyAutomatically, BOOL) RCT_REMAP_VIEW_PROPERTY(color, textColor, UIColor) RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType, UITextAutocapitalizationType) RCT_REMAP_VIEW_PROPERTY(textAlign, textAlignment, NSTextAlignment) RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, AutoCompleteView) { view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)]; } RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused AutoCompleteView) { view.font = [RCTFont updateFont:view.font withWeight:json]; // defaults to normal } RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused AutoCompleteView) { view.font = [RCTFont updateFont:view.font withStyle:json]; // defaults to normal } RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, AutoCompleteView) { view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName]; } RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger) - (UIView *) view { AutoCompleteView *searchTextField = [[AutoCompleteView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; searchTextField.autoCompleteDataSource = self; searchTextField.autoCompleteDelegate = self; searchTextField.showTextFieldDropShadowWhenAutoCompleteTableIsOpen = false; return searchTextField; } - (void)autoCompleteTextField:(AutoCompleteView *)textField possibleCompletionsForString:(NSString *)string completionHandler:(void (^)(NSArray *))handler { // If empty string, empty the completion list if([string isEqualToString:@""]) { handler([NSArray array]); } else { textField.handler = handler; NSDictionary *event = @{ @"possibleCompletionsForString": string, @"target": textField.reactTag }; [self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event]; } } - (BOOL)autoCompleteTextField:(MLPAutoCompleteTextField *)textField shouldConfigureCell:(UITableViewCell *)cell withAutoCompleteString:(NSString *)autocompleteString withAttributedString:(NSAttributedString *)boldedString forAutoCompleteObject:(id)autocompleteObject forRowAtIndexPath:(NSIndexPath *)indexPath; { // If no registered Cell Component if (((AutoCompleteView*)textField).cellComponent == nil) { return YES; } RCTTableViewCell *customCell = (RCTTableViewCell*) cell; RCTBridge *_bridge = self.bridge; // 🚀 https://github.com/aksonov/react-native-tableview/blob/8982116711cd74e819a73237c53307839fe071ce/RNTableView/RNTableView.m#L87 while ([_bridge respondsToSelector:NSSelectorFromString(@"parentBridge")] && [_bridge valueForKey:@"parentBridge"]) { _bridge = [_bridge valueForKey:@"parentBridge"]; } [customCell initWithBridge:_bridge reactComponent:[(AutoCompleteView*)textField cellComponent] json:[(DictionaryAutoCompleteObject*)autocompleteObject json] ]; return YES; } - (void)autoCompleteTextField:(MLPAutoCompleteTextField *)textField didSelectAutoCompleteString:(NSString *)selectedString withAutoCompleteObject:(id)selectedObject forRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *event = selectedObject ? @{ @"target": textField.reactTag, @"didSelectAutoCompleteString": [(DictionaryAutoCompleteObject*)selectedObject json] } : @{ @"target": textField.reactTag, @"didSelectAutoCompleteString": selectedString }; [self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event]; } @end ================================================ FILE: RCTAutoComplete.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 13BE3DEE1AC21097009241FE /* RCTAutoComplete.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAutoComplete.m */; }; 37005CB01E5720DB00FE0899 /* DictionaryAutoCompleteObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 37005CAF1E5720DB00FE0899 /* DictionaryAutoCompleteObject.m */; }; 3798A8D81D9B09D500348235 /* AutoCompleteView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3798A8D61D9B09D500348235 /* AutoCompleteView.m */; }; 3798A8F51D9B0B0900348235 /* MLPAutoCompleteTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 3798A8EF1D9B0B0900348235 /* MLPAutoCompleteTextField.m */; }; 3798A8F61D9B0B0900348235 /* NSString+Levenshtein.m in Sources */ = {isa = PBXBuildFile; fileRef = 3798A8F41D9B0B0900348235 /* NSString+Levenshtein.m */; }; 37DBC8B31E4496DE00317A45 /* RCTTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 37DBC8B21E4496DE00317A45 /* RCTTableViewCell.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 58B511D91A9E6C8500147676 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRCTAutoComplete.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAutoComplete.a; sourceTree = BUILT_PRODUCTS_DIR; }; 13BE3DEC1AC21097009241FE /* RCTAutoComplete.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAutoComplete.h; sourceTree = ""; }; 13BE3DED1AC21097009241FE /* RCTAutoComplete.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAutoComplete.m; sourceTree = ""; }; 37005CAE1E5720B500FE0899 /* DictionaryAutoCompleteObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DictionaryAutoCompleteObject.h; sourceTree = ""; }; 37005CAF1E5720DB00FE0899 /* DictionaryAutoCompleteObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DictionaryAutoCompleteObject.m; sourceTree = ""; }; 3798A8D51D9B09D500348235 /* AutoCompleteView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoCompleteView.h; sourceTree = ""; }; 3798A8D61D9B09D500348235 /* AutoCompleteView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AutoCompleteView.m; sourceTree = ""; }; 3798A8EE1D9B0B0900348235 /* MLPAutoCompleteTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MLPAutoCompleteTextField.h; path = MLPAutoCompleteTextField/MLPAutoCompleteTextField.h; sourceTree = ""; }; 3798A8EF1D9B0B0900348235 /* MLPAutoCompleteTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MLPAutoCompleteTextField.m; path = MLPAutoCompleteTextField/MLPAutoCompleteTextField.m; sourceTree = ""; }; 3798A8F01D9B0B0900348235 /* MLPAutoCompleteTextFieldDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MLPAutoCompleteTextFieldDataSource.h; path = MLPAutoCompleteTextField/MLPAutoCompleteTextFieldDataSource.h; sourceTree = ""; }; 3798A8F11D9B0B0900348235 /* MLPAutoCompleteTextFieldDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MLPAutoCompleteTextFieldDelegate.h; path = MLPAutoCompleteTextField/MLPAutoCompleteTextFieldDelegate.h; sourceTree = ""; }; 3798A8F21D9B0B0900348235 /* MLPAutoCompletionObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MLPAutoCompletionObject.h; path = MLPAutoCompleteTextField/MLPAutoCompletionObject.h; sourceTree = ""; }; 3798A8F31D9B0B0900348235 /* NSString+Levenshtein.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+Levenshtein.h"; path = "MLPAutoCompleteTextField/NSString+Levenshtein.h"; sourceTree = ""; }; 3798A8F41D9B0B0900348235 /* NSString+Levenshtein.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+Levenshtein.m"; path = "MLPAutoCompleteTextField/NSString+Levenshtein.m"; sourceTree = ""; }; 37DBC8B11E44965900317A45 /* RCTTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTableViewCell.h; sourceTree = ""; }; 37DBC8B21E4496DE00317A45 /* RCTTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTableViewCell.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 58B511D81A9E6C8500147676 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 134814211AA4EA7D00B7C361 /* Products */ = { isa = PBXGroup; children = ( 134814201AA4EA6300B7C361 /* libRCTAutoComplete.a */, ); name = Products; sourceTree = ""; }; 3798A8E41D9B0ACB00348235 /* MLPAutoCompleteTextField */ = { isa = PBXGroup; children = ( 3798A8EE1D9B0B0900348235 /* MLPAutoCompleteTextField.h */, 3798A8EF1D9B0B0900348235 /* MLPAutoCompleteTextField.m */, 3798A8F01D9B0B0900348235 /* MLPAutoCompleteTextFieldDataSource.h */, 3798A8F11D9B0B0900348235 /* MLPAutoCompleteTextFieldDelegate.h */, 3798A8F21D9B0B0900348235 /* MLPAutoCompletionObject.h */, 3798A8F31D9B0B0900348235 /* NSString+Levenshtein.h */, 3798A8F41D9B0B0900348235 /* NSString+Levenshtein.m */, ); name = MLPAutoCompleteTextField; sourceTree = ""; }; 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( 37005CAF1E5720DB00FE0899 /* DictionaryAutoCompleteObject.m */, 37005CAE1E5720B500FE0899 /* DictionaryAutoCompleteObject.h */, 37DBC8B21E4496DE00317A45 /* RCTTableViewCell.m */, 37DBC8B11E44965900317A45 /* RCTTableViewCell.h */, 3798A8E41D9B0ACB00348235 /* MLPAutoCompleteTextField */, 3798A8D51D9B09D500348235 /* AutoCompleteView.h */, 3798A8D61D9B09D500348235 /* AutoCompleteView.m */, 13BE3DEC1AC21097009241FE /* RCTAutoComplete.h */, 13BE3DED1AC21097009241FE /* RCTAutoComplete.m */, 134814211AA4EA7D00B7C361 /* Products */, ); sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 58B511DA1A9E6C8500147676 /* RCTAutoComplete */ = { isa = PBXNativeTarget; buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAutoComplete" */; buildPhases = ( 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, 58B511D91A9E6C8500147676 /* CopyFiles */, ); buildRules = ( ); dependencies = ( ); name = RCTAutoComplete; productName = RCTDataManager; productReference = 134814201AA4EA6300B7C361 /* libRCTAutoComplete.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 58B511D31A9E6C8500147676 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0610; ORGANIZATIONNAME = Facebook; TargetAttributes = { 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; }; }; }; buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAutoComplete" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( en, ); mainGroup = 58B511D21A9E6C8500147676; productRefGroup = 58B511D21A9E6C8500147676; projectDirPath = ""; projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RCTAutoComplete */, ); }; /* End PBXProject section */ /* Begin PBXSourcesBuildPhase section */ 58B511D71A9E6C8500147676 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 3798A8F51D9B0B0900348235 /* MLPAutoCompleteTextField.m in Sources */, 37005CB01E5720DB00FE0899 /* DictionaryAutoCompleteObject.m in Sources */, 37DBC8B31E4496DE00317A45 /* RCTTableViewCell.m in Sources */, 3798A8F61D9B0B0900348235 /* NSString+Levenshtein.m in Sources */, 3798A8D81D9B09D500348235 /* AutoCompleteView.m in Sources */, 13BE3DEE1AC21097009241FE /* RCTAutoComplete.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 58B511ED1A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; }; 58B511EE1A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; }; name = Release; }; 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../React/**", "$(SRCROOT)/../../node_modules/react-native/React/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTAutoComplete; SKIP_INSTALL = YES; }; name = Debug; }; 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, "$(SRCROOT)/../../React/**", "$(SRCROOT)/../../node_modules/react-native/React/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = RCTAutoComplete; SKIP_INSTALL = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAutoComplete" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511ED1A9E6C8500147676 /* Debug */, 58B511EE1A9E6C8500147676 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAutoComplete" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511F01A9E6C8500147676 /* Debug */, 58B511F11A9E6C8500147676 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 58B511D31A9E6C8500147676 /* Project object */; } ================================================ FILE: RCTTableViewCell.h ================================================ #import #import @interface RCTTableViewCell : UITableViewCell @property (strong, nonatomic) RCTRootView *view; -(void)initWithBridge:(RCTBridge*)bridge reactComponent:(NSString*)reactComponent json:(NSDictionary*)json; @end ================================================ FILE: RCTTableViewCell.m ================================================ #import #import "RCTTableViewCell.h" @implementation RCTTableViewCell @synthesize view; - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { return [super initWithStyle:style reuseIdentifier:reuseIdentifier]; } -(void)initWithBridge:(RCTBridge*)bridge reactComponent:(NSString*)reactComponent json:(NSDictionary*)json { if (view == nil) { view = [[RCTRootView alloc] initWithBridge:bridge moduleName:reactComponent initialProperties:@{@"data": json}]; [self.contentView addSubview:view]; view.frame = self.contentView.frame; } else { view.appProperties = @{@"data": json}; } } @end ================================================ FILE: README.md ================================================ # react-native-autocomplete [MLPAutoCompleteTextField](https://github.com/EddyBorja/MLPAutoCompleteTextField) (iOS only) wrapper for React Native, supports React Native custom cells 🎨. ![Demo gif](https://raw.githubusercontent.com/nulrich/RCTAutoComplete/master/demo.gif) ## Installation * `$ npm install react-native-autocomplete` * Right click on Libraries, select **Add files to "…"** and select `node_modules/react-native-autocomplete/RCTAutoComplete.xcodeproj` * Select your project and under **Build Phases** -> **Link Binary With Libraries**, press the + and select `libRCTAutoComplete.a`. [Facebook documentation](https://facebook.github.io/react-native/docs/linking-libraries.html#content) ## Usage For example download [Country list](https://gist.githubusercontent.com/Keeguon/2310008/raw/865a58f59b9db2157413e7d3d949914dbf5a237d/countries.json) ```js import React, { Component } from "react"; import { AppRegistry, StyleSheet, Text, View, AlertIOS } from "react-native"; import AutoComplete from "react-native-autocomplete"; import Countries from "./countries.json"; const styles = StyleSheet.create({ autocomplete: { alignSelf: "stretch", height: 50, margin: 10, marginTop: 50, backgroundColor: "#FFF", borderColor: "lightblue", borderWidth: 1 }, container: { flex: 1, backgroundColor: "#F5FCFF" } }); class RCTAutoCompleteApp extends Component { state = { data: [] }; constructor(props) { super(props); this.onTyping = this.onTyping.bind(this); } onTyping(text) { const countries = Countries.filter(country => country.name.toLowerCase().startsWith(text.toLowerCase()) ).map(country => country.name); this.setState({ data: countries }); } onSelect(value) { AlertIOS.alert("You choosed", value); } render() { return ( ); } } AppRegistry.registerComponent("RCTAutoCompleteApp", () => RCTAutoCompleteApp); ``` # Custom Cell You can use a React Native component to render the cells. ```js import React, { Component } from "react"; import { AppRegistry, StyleSheet, Text, View, Image, AlertIOS } from "react-native"; import AutoComplete from "react-native-autocomplete"; import Countries from "./countries.json"; const flag = code => `https://raw.githubusercontent.com/hjnilsson/country-flags/master/png250px/${ code }.png`; const styles = StyleSheet.create({ autocomplete: { alignSelf: "stretch", height: 50, margin: 10, marginTop: 50, backgroundColor: "#FFF", borderColor: "lightblue", borderWidth: 1 }, cell: { flex: 1, borderWidth: 1, borderColor: "lightblue", flexDirection: "row", justifyContent: "center", alignItems: "center" }, cellText: { flex: 1, marginLeft: 10 }, image: { width: 20, height: 20, marginLeft: 10 }, container: { flex: 1, backgroundColor: "#F5FCFF" } }); const CustomCell = ({ data }) => ( {data.country} ); class RCTAutoCompleteApp extends Component { state = { data: [] }; constructor(props) { super(props); this.onTyping = this.onTyping.bind(this); } onTyping(text) { const countries = Countries.filter(country => country.name.toLowerCase().startsWith(text.toLowerCase()) ).map(country => { return { country: country.name, code: country.code.toLowerCase() }; }); this.setState({ data: countries }); } onSelect(json) { AlertIOS.alert("You choosed", json.country); } render() { return ( ); } } AppRegistry.registerComponent("CustomCell", () => CustomCell); AppRegistry.registerComponent("RCTAutoCompleteApp", () => RCTAutoCompleteApp); ``` ## Events | event | Info | | -------- | ----------------------------------------------------------------------------------------- | | onTyping | Text is entered. The callback can be delayed with option `autoCompleteFetchRequestDelay`. | | onSelect | A cell in the suggestions list is selected. | | onFocus | Text input get focus. | | onBlur | Text input lost focus. | > > Other events from Text Input are avalaible. ## Global options | option | type | Info | | -------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cellComponent | string | Name of a React Native component used to render cells. If `null`, use the default rendering. | | suggestions | array | If using default cell rendering specify an Array of string, otherwise any object. | | autoCompleteFetchRequestDelay | number | Delay in milliseconds before retrieving suggestions. | | maximumNumberOfAutoCompleteRows | number | Number of suggestions displayed. | | showTextFieldDropShadowWhenAutoCompleteTableIsOpen | bool | Display a drop shadow around the text field. | | autoCompleteTableViewHidden | bool | If true, the suggestions list will be hidden. | | autoCompleteTableBorderColor | color | Set suggestions list border color. | | autoCompleteTableBorderWidth | number | Set suggestions list border color. | | autoCompleteTableBackgroundColor | color | Set suggestions list border size. | | autoCompleteTableCornerRadius | number | Set suggestions list background color. | | autoCompleteTableTopOffset | number | Set the distance between the text field and the suggestions list. | | autoCompleteTableLeftOffset | number | Set the left offset between the container and the suggestions list. | | autoCompleteTableSizeOffset | number | Set the offset of the suggestions list size. Combined with autoCompleteTableLeftOffset, you can reduce the width of the suggestions list and to center it. Exemple: autoCompleteTableLeftOffset=20 autoCompleteTableSizeOffset=40 | | autoCompleteRowHeight | number | Height of cells in the suggestions list. | ## Default cell rendering options | option | type | Info | | ---------------------------------------- | ------ | ------------------------------------------------- | | autoCompleteFontSize | number | Font Size used to display text. | | autoCompleteRegularFontName | string | Font used to display text. | | autoCompleteBoldFontName | string | Font used to display suggestion text. | | autoCompleteTableCellTextColor | color | Text Color used to display text. | | autoCompleteTableCellBackgroundColor | color | Background color of cells. | | applyBoldEffectToAutoCompleteSuggestions | bool | If false, disable bold effect on suggestion text. | | reverseAutoCompleteSuggestionsBoldEffect | bool | Reverse the bold effect. | ## License MIT © Nicolas Ulrich 2017 ================================================ FILE: package.json ================================================ { "name": "react-native-autocomplete", "author": "Nicolas Ulrich (https://github.com/nulrich)", "description": "React Native Component for MLPAutoCompleteTextField", "version": "0.4.0", "main": "RCTAutoComplete.ios.js", "repository": { "type": "git", "url": "https://github.com/nulrich/RCTAutoComplete.git" }, "license": "MIT", "keywords": ["react-component", "react-native", "ios", "autocomplete"], "peerDependencies": { "react-native": ">=0.40.0" } } ================================================ FILE: react-native-autocomplete.podspec ================================================ Pod::Spec.new do |s| s.name = "react-native-autocomplete" s.version = "0.4.0" s.summary = "React Native Component for MLPAutoCompleteTextField" s.requires_arc = true s.author = { 'Nicolas Ulrich' => 'github@ulrich.co' } s.license = 'MIT' s.homepage = 'https://github.com/nulrich/RCTAutoComplete' s.source = { :git => "https://github.com/nulrich/RCTAutoComplete.git" } s.platform = :ios, "7.0" s.dependency 'React' s.subspec 'MLPAutoCompleteTextField' do |ss| ss.source_files = "MLPAutoCompleteTextField/*.{h,m}" end s.subspec 'RCTAutoComplete' do |ss| ss.dependency 'react-native-autocomplete/MLPAutoCompleteTextField' ss.source_files = "*.{h,m}" ss.preserve_paths = "*.js" end end