Ios TableviewCells未按预期插入

Ios TableviewCells未按预期插入,ios,objective-c,uitableview,Ios,Objective C,Uitableview,我有一个可重用的TableViewCell,每当用户在教科书中输入内容并单击“发送”按钮时,它就会简单地回显到tableview。我的问题是,这些单元格在正确插入单元格的几次和不正确插入单元格的几次中的行为都不符合预期 预期行为:当用户在文本框中完成某些文本输入后单击“发送”按钮时,相同的值应打印两次(如发送和接收相同的文本) 当前行为:有时它确实给我预期的行为,但有时两个细胞在同一侧 例: 有时,当我们滚动时,单元格会从发送方到接收方改变其位置(在代码中可以看到奇数单元格和偶数单元格),反之亦

我有一个可重用的TableViewCell,每当用户在教科书中输入内容并单击“发送”按钮时,它就会简单地回显到tableview。我的问题是,这些单元格在正确插入单元格的几次和不正确插入单元格的几次中的行为都不符合预期

预期行为:当用户在文本框中完成某些文本输入后单击“发送”按钮时,相同的值应打印两次(如发送和接收相同的文本)

当前行为:有时它确实给我预期的行为,但有时两个细胞在同一侧

例:

有时,当我们滚动时,单元格会从发送方到接收方改变其位置(在代码中可以看到奇数单元格和偶数单元格),反之亦然。

我的代码

//FirstTableViewController.h


#import <UIKit/UIKit.h>
@class SecondViewController;
@interface FirstTableViewController : UITableViewController
@property (strong, nonatomic) IBOutlet UITableView *messageView;
@property (nonatomic,readwrite) NSInteger counter;
@property (nonatomic,readwrite) NSMutableArray *userInput;
@property (nonatomic,readwrite) NSMutableDictionary *heightAtIndexPath;
@property (nonatomic, assign) BOOL shouldScrollToLastRow;
+ (id)sharedInstance;
@end
@interface ChatMessageCellTableViewCell : UITableViewCell
@property (nonatomic, retain) UILabel *formLabel;
@property (nonatomic, retain) UIView *bubbleBackView;
@end

这基本上是一个聊天视图,我实际上试图实现一切都是好的,除了这种不正常的行为

我希望任何人都能花点时间纠正我的错误


更新:任何在objective-C中寻找基本聊天视图的人都可以使用上面的代码作为参考,使用上面的代码并在接受的答案中更正所提到的内容。

这是一个典型的单元重用问题。在iOS中,所有集合(UITableView/UICollectionView)都会重用单元格,并且单元格
initWithStyle
仅在单元格初始化后才会被调用。一旦tableView中有足够的单元格,它将重用该单元格,因此不会一直调用
initWithStyle
。因此,您的几个细胞(最好是初始细胞)似乎没有问题。由于您在
init
中正确设置了约束,并且对于未正确显示的其他单元格,从未调用
init
,因此您的约束从未更新。因此显示出错误的泡沫

解决方案是什么?:

1。使用prepareforeuse 当每个单元被重用时,iOS会调用该单元上的
prepareforeuse
,为开发人员提供最后一次清理的机会

-(void) prepareForReuse {
    [super prepareForReuse];
    [self.formLabel setText: nil];
    //set default background color or change bubble view
    // do whatever clean up you wanna do here
}
2。修改单元格方法,确保每次显示单元格时更新约束,而不仅仅是在init中

假设您添加了一个名为:

 -(void)configureView:(BOOL) isRecieved {
    isReceived = isRecieved;
    if(isReceived){
        [leadingConstraint setActive:YES];
        [trailingConstraint setActive:NO];
    }
    else{
        [leadingConstraint setActive:NO];
        [trailingConstraint setActive:YES];
    }
    //[self layoutIfNeeded]; might be needed here
    [self loaded];

}
在init中,删除基于isRecieved值设置约束的代码

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    [self setBackgroundColor:[UIColor clearColor]];
    self.formLabel = [UILabel new];
    self.bubbleBackView = [UIView new];

    //[self.bubbleBackView setBackgroundColor:[UIColor yellowColor]];
    [self.bubbleBackView.layer setCornerRadius:12];
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if(self){
        [[self contentView] addSubview:self.bubbleBackView
         ];
        [self loaded];
        [self.bubbleBackView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [[self contentView] addSubview:self.formLabel];

        [self.formLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
        if (@available(iOS 9.0, *)) {
            [self.formLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:32].active=YES;
            [self.formLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-32].active=YES;
            [self.formLabel.widthAnchor constraintLessThanOrEqualToConstant:250].active=YES;

            [self.bubbleBackView.topAnchor constraintEqualToAnchor:_formLabel.topAnchor constant:-16].active=YES;
            [self.bubbleBackView.bottomAnchor constraintEqualToAnchor:_formLabel.bottomAnchor constant:16].active=YES;
            [self.bubbleBackView.trailingAnchor constraintEqualToAnchor:_formLabel.trailingAnchor constant:16].active=YES;
            [self.bubbleBackView.leadingAnchor constraintEqualToAnchor:_formLabel.leadingAnchor constant:-16].active=YES;
            leadingConstraint= [self.formLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:32];
            trailingConstraint = [self.formLabel.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-32];

        } else {
            // Fallback on earlier versions
        }
        [self.formLabel setLineBreakMode:NSLineBreakByWordWrapping];
        [self.formLabel setNumberOfLines:0];
        [self.formLabel sizeToFit];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-40-[bodyLabel]-40-|" options:0
                                                                                 metrics:nil
                                                                                   views:@{ @"bodyLabel":self.formLabel}]];

    }
    return self;
}
最后在
cellforrowatinexpath
call
configureView
中,接收到

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *cellIdentifier = (indexPath.row % 2 == 0 ? @"EvenCell" : @"OddCell"); //just to differentiate the sending and receiving cell.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    ChatMessageCellTableViewCell *messageCell = (ChatMessageCellTableViewCell*) cell;
    if (messageCell == nil) {
        messageCell = [[ChatMessageCellTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
            reuseIdentifier:cellIdentifier];
    }
    if(indexPath.row % 2 == 0) // simple logic to differentiate and apply my constraints to the sending and receiving cells.
    {
        isReceived =TRUE;
    }
    else{
        isReceived = FALSE;
    }
    [messageCell configureView: isReceived];
    [[messageCell formLabel]setText:classA.userInput[indexPath.row]];
    [messageCell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[self tableView] setEstimatedRowHeight:50.0];
    [self.tableView setRowHeight:UITableViewAutomaticDimension];
    return messageCell;
}

希望能有所帮助

这是一个典型的单元重用问题。在iOS中,所有集合(UITableView/UICollectionView)都会重用单元格,并且单元格
initWithStyle
仅在单元格初始化后才会被调用。一旦tableView中有足够的单元格,它将重用该单元格,因此不会一直调用
initWithStyle
。因此,您的几个细胞(最好是初始细胞)似乎没有问题。由于您在
init
中正确设置了约束,并且对于未正确显示的其他单元格,从未调用
init
,因此您的约束从未更新。因此显示出错误的泡沫

解决方案是什么?:

1。使用prepareforeuse 当每个单元被重用时,iOS会调用该单元上的
prepareforeuse
,为开发人员提供最后一次清理的机会

-(void) prepareForReuse {
    [super prepareForReuse];
    [self.formLabel setText: nil];
    //set default background color or change bubble view
    // do whatever clean up you wanna do here
}
2。修改单元格方法,确保每次显示单元格时更新约束,而不仅仅是在init中

假设您添加了一个名为:

 -(void)configureView:(BOOL) isRecieved {
    isReceived = isRecieved;
    if(isReceived){
        [leadingConstraint setActive:YES];
        [trailingConstraint setActive:NO];
    }
    else{
        [leadingConstraint setActive:NO];
        [trailingConstraint setActive:YES];
    }
    //[self layoutIfNeeded]; might be needed here
    [self loaded];

}
在init中,删除基于isRecieved值设置约束的代码

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    [self setBackgroundColor:[UIColor clearColor]];
    self.formLabel = [UILabel new];
    self.bubbleBackView = [UIView new];

    //[self.bubbleBackView setBackgroundColor:[UIColor yellowColor]];
    [self.bubbleBackView.layer setCornerRadius:12];
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if(self){
        [[self contentView] addSubview:self.bubbleBackView
         ];
        [self loaded];
        [self.bubbleBackView setTranslatesAutoresizingMaskIntoConstraints:NO];
        [[self contentView] addSubview:self.formLabel];

        [self.formLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
        if (@available(iOS 9.0, *)) {
            [self.formLabel.topAnchor constraintEqualToAnchor:self.topAnchor constant:32].active=YES;
            [self.formLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-32].active=YES;
            [self.formLabel.widthAnchor constraintLessThanOrEqualToConstant:250].active=YES;

            [self.bubbleBackView.topAnchor constraintEqualToAnchor:_formLabel.topAnchor constant:-16].active=YES;
            [self.bubbleBackView.bottomAnchor constraintEqualToAnchor:_formLabel.bottomAnchor constant:16].active=YES;
            [self.bubbleBackView.trailingAnchor constraintEqualToAnchor:_formLabel.trailingAnchor constant:16].active=YES;
            [self.bubbleBackView.leadingAnchor constraintEqualToAnchor:_formLabel.leadingAnchor constant:-16].active=YES;
            leadingConstraint= [self.formLabel.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:32];
            trailingConstraint = [self.formLabel.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-32];

        } else {
            // Fallback on earlier versions
        }
        [self.formLabel setLineBreakMode:NSLineBreakByWordWrapping];
        [self.formLabel setNumberOfLines:0];
        [self.formLabel sizeToFit];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-40-[bodyLabel]-40-|" options:0
                                                                                 metrics:nil
                                                                                   views:@{ @"bodyLabel":self.formLabel}]];

    }
    return self;
}
最后在
cellforrowatinexpath
call
configureView
中,接收到

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSString *cellIdentifier = (indexPath.row % 2 == 0 ? @"EvenCell" : @"OddCell"); //just to differentiate the sending and receiving cell.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    ChatMessageCellTableViewCell *messageCell = (ChatMessageCellTableViewCell*) cell;
    if (messageCell == nil) {
        messageCell = [[ChatMessageCellTableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
            reuseIdentifier:cellIdentifier];
    }
    if(indexPath.row % 2 == 0) // simple logic to differentiate and apply my constraints to the sending and receiving cells.
    {
        isReceived =TRUE;
    }
    else{
        isReceived = FALSE;
    }
    [messageCell configureView: isReceived];
    [[messageCell formLabel]setText:classA.userInput[indexPath.row]];
    [messageCell setSelectionStyle:UITableViewCellSelectionStyleNone];
    [[self tableView] setEstimatedRowHeight:50.0];
    [self.tableView setRowHeight:UITableViewAutomaticDimension];
    return messageCell;
}
希望能有帮助