UICollectionView:激活、滚动到并将第一响应者分配到远处的单元格

Posted

技术标签:

【中文标题】UICollectionView:激活、滚动到并将第一响应者分配到远处的单元格【英文标题】:UICollectionView: activating, scrolling to, and assigning first responder to distant cell 【发布时间】:2013-07-02 21:01:13 【问题描述】:

我在UICollectionViewCell 中有一个文本字段,可以接收第一响应者状态。该单元格当前在屏幕上不可见,我想根据UISegmentedControl 的按钮滚动到该单元格。这个控件有两个部分……点击第二个部分应该滚动到UICollectionView第二部分的第一个单元格。发生这种情况后,应以编程方式选择单元格,然后该单元格内的文本字段应获得第一响应者状态并调出键盘。

现在发生的事情(在我的操作方法中,来自分段控件的值更改)是对-[UICollectionView selectItemAtIndexPath:animated:scrollPosition:] 的调用根本没有滚动到它(我正在使用UICollectionViewScrollPositionTop;也可能是“…None”)。如果我手动向下滚动列表,则确实选择了单元格(在该状态下它的背景颜色较深),但文本字段肯定没有第一响应者状态。

为了解决滚动问题,我已经能够确定单元格在列表中的位置,并滚动到单元格的内容偏移量(我在这里也使用了scrollRectToVisible)。然后我手动选择它(并告诉委托也触发其适当的方法,单元格的文本字段获得第一响应者状态)。

- (void)directionSegmentedControlChanged:(UISegmentedControl *)sender 
    NSIndexPath *path = [NSIndexPath indexPathForItem:0 inSection:sender.selectedSegmentIndex];
    UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:path];
    [self.collectionView setContentOffset:attributes.frame.origin animated:YES];
    [self.collectionView selectItemAtIndexPath:path animated:NO scrollPosition:UICollectionViewScrollPositionNone];
    [self.collectionView.delegate collectionView:self.collectionView didSelectItemAtIndexPath:path];


- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 
    BDKCollectionViewCell *cell = (BDKCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    [cell.textField becomeFirstResponder];

这里的问题是它在-[collectionView:didSelectItemAtIndexPath:] 中看到的单元格是 nil,因为当方法被触发时它不在集合视图的可见单元格集中。

解决这个问题的最佳方法是什么?我尝试将滚动代码扔到[UIView animateWithDuration:animations:completion:] 块内,并在完成后分配第一响应者,但是以这种方式手动为集合视图设置动画会忽略加载任何应该滚动过去的单元格。有什么想法吗?


更新:非常感谢@Esker,他建议我在使用 Grand Central Dispatch 延迟后执行“焦点选择”操作。我的解决方案最终看起来像这样。

- (void)directionSegmentedControlChanged:(UISegmentedControl *)sender 
    NSIndexPath *path = [NSIndexPath indexPathForItem:0  inSection:sender.selectedSegmentIndex];

    UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:path];
    [self.collectionView setContentOffset:attributes.frame.origin animated:YES];

    dispatch_time_t startAfter = dispatch_time(DISPATCH_TIME_NOW, 0.28 * NSEC_PER_SEC);
    dispatch_after(startAfter, dispatch_get_main_queue(), ^
        [self.collectionView selectItemAtIndexPath:path animated:NO scrollPosition:UICollectionViewScrollPositionNone];
        [self collectionView:self.collectionView didSelectItemAtIndexPath:path];
    );

【问题讨论】:

像魅力一样工作!谢谢! 【参考方案1】:

UITableView 遇到了类似的挑战:滚动到一个尚不可见的单元格,并在目标单元格可见后将第一响应者分配给目标单元格中​​的 UITextField。这是我如何处理此问题的简化描述。我想这种方法可以与UICollectionView 一起使用,但我对集合视图没有太多经验。

    如果所需的单元格/文本字段当前可见,请立即发送becomeFirstResponder,并根据需要滚动到该单元格。 否则,请在视图控制器或类似的类中设置一个属性来指示文本字段需要焦点,以及哪个需要焦点 告诉表视图/集合视图滚动到所需的索引路径 在collectionView:cellForItemAtIndexPath: 中,您可以尝试检查该属性以查看给定indexPath 处的文本字段是否需要获得焦点,如果是,请立即发送becomeFirstResponder,但我发现这不会如果单元格滚动到视图中,则工作,大概是因为此时,当您配置新单元格时,它实际上还没有在视图层次结构中。所以我加了一个检查,如果此时becomeFirstResponder返回NO,我延迟后再试一次:
dispatch_after(someDelay, dispatch_get_main_queue(), ^(void)
    [self getFocus:textField];
);

getFocus 方法将becomeFirstResponder 发送到文本字段并清除跟踪哪个文本字段需要焦点的属性。

我的实际实现在某种程度上专门针对与我的表视图关联的视图模型,并封装在几个类中并使用了一些 KVO,但我想避免这种情况并专注于上述描述中所需的最低逻辑。

【讨论】:

感谢您抽出宝贵时间回复!你的代码 sn-p 帮助了——因为我最后的手段是在做一个performSelector:afterDelay:,因为我是我最后的手段,你的块更干净。你得到答案奖励,我会用我的解决方案编辑我的问题。 非常好,很高兴这有效。我觉得必须有一个更优雅的解决方案,但我还没有想出一个。 同意...我一直在考虑向 Apple 提交错误报告。这将帮助我制定一个合适的……呃,命题。

以上是关于UICollectionView:激活、滚动到并将第一响应者分配到远处的单元格的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 滚动单列

在 UICollectionView 中滚动时 Admob Native Expess Ads 块

如何检测滚动到水平 UICollectionView 的末尾

UICollectionView 没有滚动到位置

滚动到特定索引 UICollectionView

UICollectionView 没有滚动到 indexPath