重新加载行包含带有 rightView 的 UITextField 会导致无限循环和内存溢出

Posted

技术标签:

【中文标题】重新加载行包含带有 rightView 的 UITextField 会导致无限循环和内存溢出【英文标题】:Reloading row contains UITextField with a rightView causes infinite loop and memory overflow 【发布时间】:2016-08-08 10:15:48 【问题描述】:

我正面临一个奇怪的错误,即使用自定义 rightView 重新加载包含 UITextFieldUITableViewCell

我创建了一个非常简单的示例来演示该错误:

我的自定义单元格带有UITextField

@implementation TableViewCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) 
        _textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
        _textField.text = @"This is a text field";
        [self.contentView addSubview:_textField];
    

    return self;


@end

我的视图控制器带有UITableView:

@interface ViewController () <UITableViewDataSource>

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic) UIView *rightView;

@end

@implementation ViewController

- (void)viewDidLoad 
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // Press to reload the row with rightView
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadRow)];

    // Create rightView and store to a property
    _rightView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
    _rightView.backgroundColor = [UIColor greenColor];

    [_tableView registerClass:[TableViewCell class] forCellReuseIdentifier:@"Cell"];
    _tableView.dataSource = self;


- (void)reloadRow 
    [_tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:0
                                                            inSection:0]]
                      withRowAnimation:(UITableViewRowAnimationAutomatic)];


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
    return 1;


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    cell.textField.rightView = _rightView;
    cell.textField.rightViewMode = UITextFieldViewModeAlways;

    return cell;


@end

就是这样。然后我按下重新加载按钮。无限循环开始,应用程序挂起。几秒钟后,应用程序崩溃,因为它使用了太多内存:

Terminated due to memory error.

但是,如果我不将rightView 存储在属性中,并且每次tableView 使单元格出列时都创建新视图,那么就不会有问题。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    cell.textField.rightView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
    cell.textField.rightView.backgroundColor = [UIColor greenColor];
    cell.textField.rightViewMode = UITextFieldViewModeAlways;

    return cell;

但我的rightView(在实际应用中)不是一个简单的视图,所以我不想在每次出现该行时都重新创建它。

有人知道为什么会这样吗?如果我不存储rightView,为什么它没有发生?感谢阅读。

【问题讨论】:

为什么不像文本字段那样在 TableViewCell 中创建正确的视图? 我有一个UITableViewCell 的子类可以在应用程序的每个地方使用,我只在一个视图控制器中使用rightView,所以我不想在单元格中添加它子类。 正确的方法是使用不同的 UITableViewCell 类,而不仅仅是一个遍及整个应用程序 :-) 【参考方案1】:

您尝试将一个视图添加到多个superview。你不能那样做。您的@property (nonatomic) UIView *rightView 只能有一个superView。尝试从子视图中删除它cell.textField.subviews

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    for (UIView *subview in cell.textField.subviews) 
        if ([subview isEqual:_rightView]) 
           [subview removeFromSuperView];
        
    
    cell.textField.rightView = _rightView;
    cell.textField.rightViewMode = UITextFieldViewModeAlways;

    return cell;

或者制作一个返回rightView实例的fabric方法

【讨论】:

我试过你说的,但问题仍然存在。是的,我做了一个方法来创建一个新的rightView 每次单元格需要它时,这解决了问题,但正如我所说,我的自定义视图不是一个简单的视图,所以我不想多次重新创建它,因为它会使 tableView 在滚动时闪烁。 我的答案有点改变了,尝试从子视图中删除差异方式

以上是关于重新加载行包含带有 rightView 的 UITextField 会导致无限循环和内存溢出的主要内容,如果未能解决你的问题,请参考以下文章

快速删除元素字典并重新加载表格视图

iOS:重新加载 UITableView 后滑动显示数据

如何在 Xamarin IOS 自定义渲染器中单击 UITextField Rightview 打开 UIPickerView

在按钮点击时为选定的行设置动画

backone-保存到带有嵌套视图的数据库

来自 rightView 的 UITextField 参考