Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to filter UICollectionView and keep keyboard up?

Tags:

I've added a UISearchBar to a UICollectionView and in the delegate searchBar:textDidChange: filter my model and call [collectionView reloadData]. reloadData (as well as reloadSection, etc) wants to take away firstResponder from the searchbar's textfield, thus dismissing the keyboard.

I am trying to build a "live updating" filter and so it's annoying to have the keyboard go away after each character typed.

Any ideas?

like image 903
Mike Katz Avatar asked Mar 06 '13 18:03

Mike Katz


2 Answers

In searchBar delegate function , I use performBatchUpdates, first,reload collectionView then call [self.searchBar becomeFirstResponder] to display keyboard

- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar{     [self setEditing:NO animated:YES];     [searchBar setShowsCancelButton:YES animated:YES];          [self.collectionView performBatchUpdates:^{             [self.collectionView reloadData];         } completion:^(BOOL finished) {             [self.searchBar becomeFirstResponder];         }]; } 
like image 94
Tran Trung Hieu Avatar answered Sep 20 '22 12:09

Tran Trung Hieu


I run into the same problem recently and it took a while to get it fixed. The problem is that when text field is nested in ReusableCollectionView the following doesn't work.

[self.collectionView reloadData]; [self.textField becomeFirstResponder]; 

Furthermore, for me it did work fine on simulator but didn't work on the device.

Setting view controller as text field delegate and implementing

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {     return NO; } 

didn't work because as the result collection view did not refresh. My guess is - before reloading its views collection tries to remove focus from all nested controls but it can't - we return NO from text field delegate method.

So the solution for me was to let the system remove focus from text field but then get it back after reload. The question was when actually to do that.

First, I've tried to do it in the

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 

but when there was no items in collection (which is normal case during filtering) this method didn’t get called.

Eventually I solved this in the following way. I created UICollectionReusableView subclass implementing two methods: -prepareForReuse and -drawRect:. The first one contains -setNeedsDesplay call which schedules drawRect call on next drawing cycle. The second one restores focus by calling [self.textField becomeFirstResponder] if corresponding flag is set to YES. So the idea was to call becomeFirstResponder "later" but not too late, otherwise weird "jumping" of keyboard happens.

My custom reusable collection view looks like this:

@interface MyCollectionReusableView : UICollectionReusableView  @property (nonatomic, assign) BOOL restoreFocus; @property (nonatomic, strong) UITextField *textField;  @end   @implementation MyCollectionReusableView @synthesize restoreFocus; @synthesize textField;  - (void)setTextField:(UITextField *)value {     textField = value;     [self addSubview:textField]; }  - (void)drawRect:(CGRect)rect {     [super drawRect:rect];     if (self.restoreFocus) {         [self.textField becomeFirstResponder];     } }  - (void)prepareForReuse {     [self setNeedsDisplay]; } 

Then in my View Controller:

- (void)viewDidLoad {     [super viewDidLoad];      [self.collectionView registerClass:[MyCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:HeaderReuseIdentifier];      [self.textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; }  - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {     if (UICollectionElementKindSectionHeader == kind) {         MyCollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader                                                                            withReuseIdentifier:HeaderReuseIdentifier                                                                                 forIndexPath:indexPath];          //add textField to the reusable view         if (nil == view.textField) {             view.textField = self.textField;         }         //set restore focus flag to the reusable view         view.restoreFocus = self.restoreFocus;         self.restoreFocus = NO;         return view;     }     return nil; }  - (void)textFieldDidChange:(UITextField *)textField {     self.restoreFocus = YES;     [self.collectionView reloadData]; }  

Probably not the most elegant solution, but it works. :)

like image 39
Vlad Lytvynenko Avatar answered Sep 20 '22 12:09

Vlad Lytvynenko