UITableView reloadData automatically calls resignFirstResponder

五迷三道 提交于 2019-11-27 12:18:16

This reads like expected behavior - the picker belongs to a particular cell, that cell gets reloaded and is not the first responder any more. I guess one had to select a specific element anyway for the picker to appear, i.e. to make it first responder.

So you either need to make it become first responder again after reloading, or update the specific cell directly.

adding:

[yourSearchBar becomeFirstResponder];

after your:

[_tableView reloadData];

did the trick

zutroi

I met the same problem, none of the answers above worked perfectly (I see the keyboard bouncing up and down, etc.).
Following this SO post I fixed the issue by calling

[tableView beginUpdates];
[tableView endUpdates]; 

this worked for me, table rows get updates and even expand/shrink (if you are changing rows height dynamically) with a nice animation, all without resigning first responder or even starting keyboard dismiss.
This will not scroll your table view to fit any expanded row, so I put the snippet above in dedicated method, f.e.:

- (void)tableView:(UITableView *)tableView reloadRowWhileShowingKeyboard:(NSIndexPath *)indexPath 
{  
    [tableView beginUpdates];
    [tableView endUpdates]; 

    [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

I solved this by subclassing UITextView, overriding -(BOOL)resignFirstResponder and by adding a BOOL canResign. this variable is set before reloading the data and unset a short time after.

You can follow this approach, not the best, but it works:

// pass the responder to a temporary (hidden) textField
[_tmpTextField becomeFirstResponder];

// reload data
[_tableView reloadData];

// reloadData is definitely async... 
// so pass the responder back in a timed op
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [_textField becomeFirstResponder];
});

I put my UISearchBar in its own section in a UITableView. When firing off the search, I made sure to only refresh the sections which do not contain the search bar.

- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
customTextField.canResign = NO;
[self.tableView reloadData];
customTextField.canResign = YES;

Custom text field is derived from UITextField.

.h

@interface CustomTextField : UITextField
@property (nonatomic,assign) BOOL canResign;
@end

.m

- (BOOL)canResignFirstResponder
{
    return self.canResign;
}

Make sure that your custom text field is not recreated on table view reloading.

If you are facing this issue with a search bar, the following did it for me in iOS 6:

  • Instantiate a UISearchBar and add it as a subview to your UITableView at the top.
  • Create a dummy first cell in your UITableView so that the search bar only blocks this dummy cell and not your actual cell with data.

As mentioned by @Eiko, this works for me!

Update the cell in UIPickerViewDelegate's pickerView:didSelectRow:inComponent: method:

- (void) pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
  TableViewCell *cell = (TableViewCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:pickerView.tag inSection:0]];

  /*
  Update your cell here. 
  */

  // Reload TableViewCell will resign the PickerView, so we need to focus it back.  
  [self.tableView reloadData];
  NSIndexPath* indexPath = [self.tableView indexPathForCell:cell];
  NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
  [self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationNone];
  [cell.textField becomeFirstResponder];
}

Swift solution:

we can override default canResignFirstResponder by subclassing UITextfiled

class CustomField: UITextField{
    var canResign:Bool = false
    override var canResignFirstResponder: Bool{
        return canResign
    }  
}

all you need to set canResign variable before and after reload statement.

cell.offerInputTextField.canResign = false
tableView.reloadData()
cell.offerInputTextField.canResign = true

don't forget to assign the custom class text field as CustomField.

Track which cell's keyboard is active and then get that particular cell by cellForRowAtIndexPath and make textView firstResponder

self.tableView.reloadData()
if let indexPath = self.activeIndexPath{
   if let cell = createFormTableView.cellForRow(at: indexPath) as? TextViewTableViewCell {
        cell.txtViewInput.becomeFirstResponder()
   }
}

I use beginUpdate and endUpdate After end update, get the cell contains the textfield already has focus then make it first responder

    self.tableView.beginUpdates()
    self.tableView.reloadRows(at: [indexPath], with: .automatic)
    self.tableView.endUpdates()
    let newCell = self.tableView.cellForRow(at: indexPath)
    newCell.textField.becomeFirstResponder()

You can solve this issue by temporarily transferring the first responder status to other object. Usually you transfer the control of input view to your ViewController. Since your UIViewController also inherits from UIResponder, you can do something like this:

on didSelect { ....

[yourViewController becomeFirstRespoder];

[_tableView reloadData];

[yourInputField becomeFirstResponder];

.... }

Thus, once the table is reloaded, you can transfer firstResponder status back to your label/field. By default, the canBecomeFirstResponder is set to NO. So you might need to override the same in your ViewController. Also, you might need to make the inputView for your view controller the same as your UIPicker, otherwise it might just dismiss your picker and display a keyboard.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!