Ios 文本更改时,自动布局动态高度表视图单元格呈现错误

Ios 文本更改时,自动布局动态高度表视图单元格呈现错误,ios,objective-c,uitableview,autolayout,Ios,Objective C,Uitableview,Autolayout,我有一个自定义的UITableViewCell,它使用Autolayout和iOS8动态高度,包含两个不同大小的标签。上标签可以是1到2行之间的任意位置,而下标签始终只有1行 在:cellforrowatinexpath:中,我将上部标签的文本设置为“Loading..”,并调用一个方法从服务器(或缓存)(可以是任意长度)检索实际文本。第二个标签设置为其最终文本 ... cell.title.text = @"Loading..."; cell.subtitle.text = source.na

我有一个自定义的
UITableViewCell
,它使用
Autolayout
iOS8动态高度
,包含两个不同大小的标签。上标签可以是1到2行之间的任意位置,而下标签始终只有1行

:cellforrowatinexpath:
中,我将上部标签的文本设置为“Loading..”,并调用一个方法从服务器(或缓存)(可以是任意长度)检索实际文本。第二个标签设置为其最终文本

...
cell.title.text = @"Loading...";
cell.subtitle.text = source.name;

__weak NewsTableViewCell *weakCell = cell;
[[NewsManager sharedManager] getNewsForSource:source withBlock:^(NewsObject *object, NSError *error) {
    NewsTableViewCell *strongCell = weakCell;

    strongCell.title.text = object.articleHeadline;
}];
...
当该方法返回并将文本从“加载…”更改为实际文本时,会发生以下三种情况之一:

  • 一切都正确地呈现了
  • 删除第二个标签,并将第一个标签固定到一行
  • 第一个标签在半宽处被切掉(这通常发生在将手机侧向转动并再次回到纵向后)
  • 我在
    视图中调用
    [self.tableView reloadData]
    将显示:
    ,因此,如果某些内容呈现错误,只需打开另一个视图并返回该视图即可解决问题。把手机转到另一边,然后又回到原来的样子也是如此

    下面的图片解释了发生的情况(注意:两个单元格应与第二个单元格相似):

    以下是我如何使用
    自动布局在
    故事板中设置
    UITableViewCell

    我没有改变任何属性(如拥抱和压缩优先级)

    谁能给我指点正确的方向/让我知道我做错了什么

    编辑1:这是一张照片,显示了不同的
    ui标签
    框架
    (1)正确的布局(2)错误的布局(3)单元格等待最终文本时的布局(导致出现错误的布局)

    编辑2:从块中调用
    [strongCell setNeedsLayout]
    [strongCell layoutifneed]
    并不能解决我的问题,但会导致控制台中出现此
    自动布局
    错误。你能帮我调试一下吗

    Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you 
    don't want. Try this: (1) look at each constraint and try to figure out which 
    you don't expect; (2) find the code that added the unwanted constraint or 
    constraints and fix it. (Note: If you're seeing 
    NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the 
    documentation for the UIView property 
    translatesAutoresizingMaskIntoConstraints) 
    
    (
    "<NSLayoutConstraint:0x1700927a0 UILabel:0x127d39c20'Horrifying moment lioness...'.top == UITableViewCellContentView:0x127d2e510.topMargin>",
    "<NSLayoutConstraint:0x170092930 V:[UILabel:0x127d39c20'Horrifying moment lioness...']-(8)-[UILabel:0x127d39dd0'Daily Mail Online']>",
    "<NSLayoutConstraint:0x170092a70 UILabel:0x127d39dd0'Daily Mail Online'.bottom == UITableViewCellContentView:0x127d2e510.bottomMargin>",
    "<NSLayoutConstraint:0x170092c00 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x127d2e510(0)]>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x170092930 V:[UILabel:0x127d39c20'Horrifying moment lioness...']-(8)-[UILabel:0x127d39dd0'Daily Mail Online']>
    
    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
    
    无法同时满足约束。
    可能以下列表中至少有一个约束是您需要的约束
    我不想要。尝试以下方法:(1)查看每个约束,并尝试找出哪一个约束
    你不期待;(2) 查找添加不需要的约束的代码或
    约束并修复它。(注意:如果您看到
    如果您不了解NSAutoresizingMaskLayoutConstraints,请参阅
    UIView属性的文档
    翻译自动调整大小(从皮肤到肌肉)
    (
    "",
    "",
    "",
    ""
    )
    将尝试通过打破约束进行恢复
    在UIViewAlertForUnsatifiableConstraints处创建一个符号断点,以便在调试器中捕获该断点。
    中列出的UIView上UIConstraintBasedLayoutDebugging类别中的方法也可能会有所帮助。
    
    如果使用多行UILabels,请不要忘记需要正确设置preferredMaxLayoutWidth

    尝试设置上部标签的
    首选MaxLayoutWidth

    cell.title.preferredMaxLayoutWidth  = cell.title.bounds.size.width;
    

    编辑:

    __weak NewsTableViewCell *weakCell = cell;
    [[NewsManager sharedManager] getNewsForSource:source withBlock:^(NewsObject *object, NSError *error) {
        NewsTableViewCell *strongCell = weakCell;
    
        strongCell.title.text = object.articleHeadline;
    
        [strongCell setNeedsLayout];
        [strongCell layoutIfNeeded];
    }];
    

    再次编辑:

    __weak NewsTableViewCell *weakCell = cell;
    [[NewsManager sharedManager] getNewsForSource:source withBlock:^(NewsObject *object, NSError *error) {
        NewsTableViewCell *strongCell = weakCell;
    
        strongCell.title.text = object.articleHeadline;
    
        [strongCell setNeedsLayout];
        [strongCell layoutIfNeeded];
    }];
    
    我强烈建议您在
    viewDidLoad
    中检索数据。表视图重复使用单元格。当一个单元格滚动到屏幕外时,它将被添加到队列中,并将被重新用于屏幕上滚动的下一个单元格

    如果您在
    cellForRow
    中请求数据,则必须按如下方式检查单元格:

    NSInteger row = indexPath.row;
    [[NewsManager sharedManager] getNewsForSource:source withBlock:^(NewsObject *object, NSError *error) {
    
        // Update the datasource, e.g. caching the news in getNewsForSource:withBlock:
    
        UITableViewCell *dangerousCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
        if (dangerousCell != nil) {
            NSString *articleHeadline = object.articleHeadline;
            if ([dangerousCell.title.text isEqualToString:articleHeadline]) {
                return;
            }
            textLabel.text = articleHeadline;
            [tableView reloadData];
        }
    }];
    

    为了重新布置单元,需要使用
    重新加载
    方法
    重新加载
    单元

    你可以通过这种方式实现它

    首先,创建一个可变字典属性来存储单元格的文本

    @property (nonatomic, strong) NSMutableDictionary *textDictionary;
    
    然后在某个地方初始化它

    然后在第行的单元格中

    更新了以下评论

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        ...
    
        cell.subtitle.text = source.name;
    
        // get the text from the dictionary
        NSString *titleText = self.textDictionary[source];
    
        if (titleText) {
            // second time through the text exists so just use it.
            cell.title.text = titleText;
        } else {
            // first time through the text is nil so set it to default and download it.
            cell.title.text = @"Loading...";
    
            // this just stops the download starting again before it has completed
            self.textDictionary[source] = @"Loading...";
        }
    
        // recheck the text and reload if it has changed
        [[NewsManager sharedManager] getNewsForSource:source withBlock:^(NewsObject *object, NSError *error) {
            NSString *oldText = self.textDictionary[source];
            NSString *newText = object.articleHeadline;
    
            // reload if the text has changed
            if (![oldText isEqualToString:newText]) {
                // store the text in the dictionary
                self.textDictionary[source] = newText;
    
                // reload the indexPath (will only trigger if the cell is visible)
                [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            }
        }];
    
        ....
    }
    
    第一次通过它触发字符串的异步获取。完成后,将其存储在本地,然后重新加载行


    第二次使用时,只需获取本地存储的字符串并使用该字符串,而无需再次下载。

    我尝试在传递的块和
    cellforrowatinexpath:
    中添加该行,但这似乎并没有使它变得更好。实际上,现在它似乎总是会导致第二个标签消失,而第一个标签仅为一行,不管文本长度如何,直到您再次调用
    重新加载数据
    。在更改文本时,我尝试调用
    [strongCell layoutSubview]
    [strongCell layoutNeeded]
    ,但这也不起作用。这一切真的不应该自动完成吗?我的意思是,我认为我已经正确设置了约束条件,所以我不知道为什么textlabel不能适应内容,为什么第二个textlabel会消失,为什么单元格不能增长以容纳较大的文本在主线程中调用
    getNewsForSource
    getNewsForSource:
    检查对象是否在缓存中。如果它在缓存中,则从
    主线程调用
    块()
    ,如果不是,则从等待网络连接的
    后台线程调用。那有关系吗?我知道你不能从背景线程对布局进行更改,但是更改文本是有效的,所以我不明白为什么布局不能正确渲染。也看到了原来的帖子,我添加了另一张图片。我认为当请求完成时,你必须更新单元格的高度。当文本加载时,你的处理方式不正确。您当前正在做的是更改文本,并假设布局将为您固定。当您获得新文本时,需要重新加载该单元格。这将重新布局cell.Los,将下载的文本存储在数组中,以便在cell for row中,您可以检查数组并将标题设置为“加载…”(如果不可用),或将数组中的文本设置为“加载…”(如果不可用)