简体   繁体   English

UICollectionView:激活,滚动到第一响应者并将其分配给远程单元

[英]UICollectionView: activating, scrolling to, and assigning first responder to distant cell

I've got a text field inside of a UICollectionViewCell that can receive first-responder status. 我在UICollectionViewCell中有一个文本字段,可以接收第一响应者状态。 The cell currently isn't visible on-screen, and I want to scroll to the cell based off of a button hit from a UISegmentedControl . 当前在屏幕UISegmentedControl不到单元格,我想根据UISegmentedControl中的按钮滚动到单元UISegmentedControl There's two segments to this control… and a hit to the second segment should scroll to the first cell in the 2nd section of the UICollectionView . 这个控件有两个段......对第二个段的命中应该滚动到UICollectionView的第二个部分中的第一个单元格。 After this happens, the cell should get selected programatically, and then the text field inside of that cell is supposed to get first responder status and bring up the keyboard. 发生这种情况后,应该以编程方式选择单元格,然后该单元格内的文本字段应该获得第一响应者状态并调出键盘。

What's happening now (inside my action method from a value change from the segmented control) is that a call to -[UICollectionView selectItemAtIndexPath:animated:scrollPosition:] isn't scrolling to it at all (and I'm using UICollectionViewScrollPositionTop ; may as well be " …None "). 现在发生的事情(在我的动作方法中,来自分段控件的值更改)是调用-[UICollectionView selectItemAtIndexPath:animated:scrollPosition:]根本没有滚动到它(我正在使用UICollectionViewScrollPositionTop ;可能是好吧“ …None ”)。 If I thumb down the list manually, the cell is indeed selected (it gets a darker background color in that state), but the text field certainly doesn't have first responder status. 如果我手动向下滑动列表,则确实选择了单元格(在该状态下它会获得较暗的背景颜色),但文本字段肯定没有第一响应者状态。

To fix the scroll problem, I've been able to ascertain the position of the cell in the list, and scroll to the cell's content offset (I've also used scrollRectToVisible here). 为了解决滚动问题,我已经能够确定列表中单元格的位置,并滚动到单元格的内容偏移量(我在这里也使用了scrollRectToVisible )。 Then I manually select it (as well as telling the delegate to fire its appropriate method as well, where the cell's text field gains first responder status). 然后我手动选择它(以及告诉委托也启动它的适当方法,单元格的文本字段获得第一响应者状态)。

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

The problem here is that the cell as it's seen in -[collectionView:didSelectItemAtIndexPath:] is nil, because it's not in the visible cell set of the collection view when the method gets fired. 这里的问题是,在-[collectionView:didSelectItemAtIndexPath:]中看到的单元格是nil,因为当方法被触发时,它不在集合视图的可见单元格集中。

What's the best way to solve this? 解决这个问题的最佳方法是什么? I've tried tossing my scrolling code inside of a [UIView animateWithDuration:animations:completion:] block, and assigned first responder upon completion there, but manually animating the collection view in this manner neglects to load any of the cells that should be scrolled past. 我已经尝试在[UIView animateWithDuration:animations:completion:]块中抛出我的滚动代码,并在完成后分配第一个响应者,但是以这种方式手动动画集合视图忽略了加载应该滚动的任何单元格过去。 Any ideas? 有任何想法吗?


Update : many thanks to @Esker, who suggested I simply perform the "focus selection" action after a delay using Grand Central Dispatch. 更新 :非常感谢@Esker,他建议我在使用Grand Central Dispatch延迟后执行“焦点选择”操作。 My solution ended up looking like this. 我的解决方案最终看起来像这样。

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

I had a similar challenge with a UITableView : scrolling to a cell that was not yet visible, and assigning first responder to a UITextField within the target cell once it was visible. 我在UITableView遇到了类似的挑战:滚动到一个尚未可见的单元格,并在目标单元格中​​可见时将第一个响应者分配给目标单元格中​​的UITextField Here's a simplified description of how I handle this. 这是我如何处理这个的简化描述。 I imagine this approach could work with a UICollectionView , but I don't have much experience with collection views. 我想这种方法可以使用UICollectionView ,但我对集合视图没有太多经验。

  1. If the desired cell/text field is currently visible, immediately send it becomeFirstResponder , and scroll to the cell if desired. 如果当前可见所需的单元格/文本字段,请立即将其发送到becomeFirstResponder ,并根据需要滚动到该单元格。
  2. Otherwise, set a property in your view controller or a similar class that indicates that a text field needs focus, and which one needs focus 否则,在视图控制器或类似的类中设置一个属性,指示文本字段需要焦点,哪个需要焦点
  3. Tell the table view/collection view to scroll to the desired index path 告诉表视图/集合视图以滚动到所需的索引路径
  4. In collectionView:cellForItemAtIndexPath: , you could try to check that property to see if a text field at the given indexPath needs to get focus, and if so, send it becomeFirstResponder immediately, but I found this won't work if the cell is scrolling into view, presumably because at this point, when you're configuring the new cell, it's not yet actually in the view hierarchy. collectionView:cellForItemAtIndexPath: ,您可以尝试检查该属性以查看给定indexPath处的文本字段indexPath需要获得焦点,如果是,请立即将其发送到becomeFirstResponder ,但我发现如果单元格正在滚动则不起作用进入视图,大概是因为此时,当您配置新单元格时,它实际上还不在视图层次结构中。 So I added a check, if becomeFirstResponder returns NO at this point, I try again after a delay: 所以我添加了一个检查,如果becomeFirstResponder返回NO ,我会在延迟后再试一次:
dispatch_after(someDelay, dispatch_get_main_queue(), ^(void){
    [self getFocus:textField];
});

The getFocus method will both send becomeFirstResponder to the text field and clear that property that tracks which text field needs focus. getFocus方法都会将becomeFirstResponder发送到文本字段,并清除跟踪哪个文本字段需要关注的属性。

My actual implementation is somewhat specialized for the view model associated with my table view, and encapsulated in a couple of classes and using some KVO, but I wanted to avoid that and focus on the minimum required logic in the description above. 我的实际实现有点专门用于与我的表视图关联的视图模型,并封装在几个类中并使用一些KVO,但我想避免这种情况,并专注于上面描述中所需的最小逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM