UICollectionView: Activate, scroll, and assign the first responder to a remote cell

I have a text box inside a UICollectionViewCell that can receive the status of the first responder. The cell is not currently displayed on the screen, and I want to scroll to the cell based on the button click with the UISegmentedControl . There are two segments for this control ... and the hit in the second segment should scroll to the first cell in the 2nd part of the UICollectionView . After that, the cell should be selected programmatically, and then the text field inside this cell should receive the status of the first responder and call the keyboard.

What happens now (inside my action method from changing the value from a segmented control) is that the call -[UICollectionView selectItemAtIndexPath:animated:scrollPosition:] does not scroll at all (and I use UICollectionViewScrollPositionTop ; it may also be " …None "). If I print the list manually, the cell will actually be selected (in this state there will be a darker background color), but the text field, of course, does not have the status of the first responder.

To fix the scroll problem, I was able to determine the position of the cell in the list and scroll to offset the contents of the cell ( scrollRectToVisible was also used scrollRectToVisible ). Then I will manually select it (and also tell the delegate to start the corresponding method, where the text field of the cell receives the status of the first responder).

 - (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 that is visible in -[collectionView:didSelectItemAtIndexPath:] is nil because it is not in the visible cell of the collection's collection view when the method starts.

What is the best way to solve this problem? I tried to throw my scroll codes inside the [UIView animateWithDuration:animations:completion:] block, and was assigned the first to the responder after completion, but I manually neglected the collection view in this way to load any of the cells that should be scrolled by the past. Any ideas?


Update : many thanks to @Esker, who suggested that I simply do a “focus selection” after the delay using Grand Central Dispatch. My decision turned out to be 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]; }); } 
+4
source share
1 answer

I had a similar task with UITableView : scrolling to a cell that was not yet visible, and assigning the first responder to a UITextField in the target cell after it was visible. Here is a simplified description of how I do this. I guess this approach might work with UICollectionView , but I don't have much experience browsing collections.

  • If the desired cell / text field is currently displayed, immediately send it to becomeFirstResponder and scroll to the cell if you want.
  • Otherwise, set a property in your view controller or similar class that indicates that the text field needs focus and needs focus.
  • Display a table / collection view for scrolling to the desired pointer path.
  • In collectionView:cellForItemAtIndexPath: you can try checking this property to see if a text field is required in the given indexPath , and if so, send it to becomeFirstResponder immediately, but I found that this will not work if the cell scrolls in the view, apparently because at the moment when you are setting up a new cell, it is not already in the hierarchy of views. So I added a check, if becomeFirstResponder returns NO at this moment, I will try again after the delay:
 dispatch_after(someDelay, dispatch_get_main_queue(), ^(void){ [self getFocus:textField]; }); 

The getFocus method getFocus send becomeFirstResponder to the text field and clear this property, which keeps track of which text field needs focus.

My actual implementation is somewhat specialized for the presentation model associated with my table view, and is encapsulated in several classes and uses some KVO, but I wanted to avoid this and focus on the minimum necessary logic in the description above.

+1
source

Source: https://habr.com/ru/post/1489363/


All Articles