如何过滤UICollectionView并保持键盘?

我已经将UISearchBar添加到UICollectionView并在委托searchBar:textDidChange:筛选我的模型,并调用[collectionView reloadData]reloadData (以及reloadSection等)想要从search栏的文本字段中取出firstResponder,从而解散键盘。

我正在尝试构build一个“实时更新”filter,因此在input每个字符后都会让键盘消失。

有任何想法吗?

在searchBar委托函数中,我使用了performBatchUpdates,首先重载collectionView然后调用[self.searchBar becomeFirstResponder]来显示键盘

 - (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]; }]; } 

我最近遇到同样的问题,需要一段时间才能修复。 问题是,当文本字段嵌套在ReusableCollectionView下面不起作用。

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

此外,对我来说,它在模拟器上工作正常,但没有在设备上工作。

将视图控制器设置为文本字段委托并执行

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

没有工作,因为结果集合视图没有刷新。 我的猜测是 – 在重新加载其视图集合之前,尝试从所有嵌套控件中移除焦点,但它不能 – 我们从文本字段委托方法返回NO。

所以对我来说,解决scheme是让系统从文本字段中移除焦点,然后在重新加载后重新获得焦点。 问题是什么时候这样做。

首先,我试图在这个方面做

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

但是当收集中没有项目时(这是过滤期间的正常情况),这个方法没有被调用。

最终我以下面的方式解决了这个问题。 我创build了UICollectionReusableView子类,实现了两个方法:-prepareForReuse和-drawRect :. 第一个包含-setNeedsDesplay调用,该调用在下一个绘图周期中调度drawRect调用。 如果相应的标志被设置为YES,第二个通过调用[self.textField becomeFirstResponder]来恢复焦点。 所以这个想法是“稍后”调用becomeFirstResponder,但不能太晚,否则会发生键盘奇怪的“跳跃”。

我的自定义可重用集合视图如下所示:

 @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]; } 

然后在我的视图控制器:

 - (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]; } 

可能不是最优雅的解决scheme,但它的工作原理。 🙂

如果您已经将UISearchBar作为头添加到UICollectionViewreloadData调用将通过删除并重新添加UISearchBar来“刷新”标头,导致其丢失firstResponder

你可以用另一种方式添加你的UISearchBar ,而不是使用头,或者覆盖reloadData ,检查search栏是否是firstResponder,如果是,在调用super ,执行:

 // If we've lost first-responder, restore it. if ((wasFirstResponder) && (![self.searchBar isFirstResponder])) { [self.searchBar becomeFirstResponder]; } 

解决了它:

  UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 726.0f, 44.0f)]; [_collectionView addSubview:searchBar]; searchBar.delegate = self; [(UICollectionViewFlowLayout *)_collectionView.collectionViewLayout setSectionInset:UIEdgeInsetsMake(64, 20, 20, 20)]; - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [_collectionView reloadData]; [searchBar becomeFirstResponder]; } 

我刚刚创build了一个小testing应用程序。 以下代码在search栏中input字符时不会隐藏键盘。 这就是说,[UITableView reloadData]不会退出响应者。

 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return arc4random() % 10; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; return cell; } - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText { [self.collectionView reloadData]; } 

你确定,你不是在某个地方辞职吗?

我想你是在-resignFirstResponder中调用-resignFirstResponder方法,这会导致每次input值被解散

你在数据源方法的正确path和重新加载数据。使用一个数组来存储所有值另一个数组加载collectionview和search栏filter上input的每个字母,并加载数据源数组,然后重新加载

那么我认为最好保持键盘。这是正确的UI风格后,你已经完成input文本search,你可以解雇它手动。我认为这是更好的select,实时更新filter。

如果您正在使用一个button进行search,那么您可以在其中包含键盘隐藏选项,但使用此类选项时无法实现实时更新

以下是我如何pipe理它。 我在我的collections视图中将我的UISearchBar作为标题补充视图。 在search栏代表文本didchange我设置了一个标志,search是活跃(或不),并重新加载collectionview

 - (void)searchBar:(UISearchBar *)_searchBar textDidChange:(NSString *)searchText { if([searchText isEqualToString:@""]) { activeSearch = FALSE; } else { activeSearch = TRUE; } [self filterContentForSearchText:searchText scope:nil]; [self.collectionView reloadData]; } 

然后在cellForItemAtIndexPath我检查键盘是第一响应和search是活跃的,如果没有,我做它第一响应者:

  - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { ... if(activeSearch && ![searchBar isFirstResponder]) { [searchBar becomeFirstResponder]; } } 

我刚刚添加

  [searchBar becomeFirstResponder]; 

打完电话后

  [collectionView reloadData]; 

答案是不重新加载具有文本字段的部分。 我通过将所有项目放入单个search来解决此问题,因此可以重新加载该单个部分。

我正在修改的现有屏幕显示右侧的索引,并且用字母导航到每个部分,这意味着有很多部分,这使得难以知道如何重新加载每个部分而不会搞乱内部状态。 为了在文本字段成为第一响应者时使其工作,收集视图的组织变为放置所有项目到单个部分,当运行search时重新加载。 现在顶部没有重新加载,所以它不会失去焦点。

这种方式不需要像这个问题列出的其他答案那样不确定的行为。

https://github.com/brennanMKE/PullDownSearch