美文网首页ios2017专题:临时区
iOS 10.3 Label高度计算问题 (UITableVie

iOS 10.3 Label高度计算问题 (UITableVie

作者: 陈雨尘 | 来源:发表于2017-04-07 17:06 被阅读1472次

    1.前言

    今天有用户反馈说10.3的系统,有些文字显示不全,影响正式用户我哪里敢怠慢。急速的更新手机系统进行测试,发现真的是有问题,而且这个问题是UITableView+FDTemplateLayoutCell 引起的,计算的高度不准确引起的。

    2.问题发现

    经过测试发现问题出现在xib或者nib创建的cell 拉约束之后再用UITableView+FDTemplateLayoutCell 计算高度就会出现问题,用Masonry 配合UITableView+FDTemplateLayoutCell 使用没问题(至少我的是没有问题,当然如果你的有问题也可以看下我列举的解决办法,相信有适合你的)

    3.解决问题

    经过Gogle 发现目前为止很少有人提到这个问题(难道大家都没发现还是大家的都没问题,当然也有可能没用UITableView+FDTemplateLayoutCell 的),在UITableView+FDTemplateLayoutCell issues 和 Masonry issues 里面有很多提到这个问题的。究其原因好像iOS 10.3 会加一个宽一个高约束(Looks like iOS 10.3 has two additional constraints there for width/height),对Autolayout的约束有新的计算方式。

    4.列举下解决问题的方法

    1.设置 label的 preferredMaxLayoutWidth

    这个方法亲测是可以的,但是有个问题nib 创建的cell 很多都不知道这个值到底是多少,就是知道也不能一个cell一个cell 的设置吧!至少我是不愿意 ,天啊几十种cell啊!当然有些人可能会图省事,随意设置一个吧,label.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 80 这样你测试的是会发现确实换行了,但是真的行吗?label 的换行是要根据这个 preferredMaxLayoutWidth 类似计算文字高度的方法

    - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullable NSDictionary<NSString *, id> *)attributes context:(nullable NSStringDrawingContext *)context NS_AVAILABLE(10_11, 7_0)
    

    就像这个方法中size size的宽就是要设置label能够显示的宽,如果给的不对当然计算出的高度也会不对,如果给的小了计算的高度就高,给的大了计算的高度就低

    1. 加 [cell layoutIfNeeded]

    因为有时候我发现第一次label显示的是没问题的,但是刷新一下就不行了,所以我想到在刷新重新算高度之前刷新下约束,这样就可以知道label的最大宽度限制了,当然我也不想在所以的cell 里面处理 所以在UITableView+FDTemplateLayoutCell 里面做了些处理

    - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id))configuration
      {
       if (!identifier) {
        return 0;
       }
    
    // Fetch a cached template cell for `identifier`.
    UITableViewCell *cell = [self fd_templateCellForReuseIdentifier:identifier];
    
    // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen).
    [cell prepareForReuse];
    
    // Customize and provide content for our template cell.
    if (configuration) {
        configuration(cell);
    }
    
    CGFloat contentViewWidth = CGRectGetWidth(self.frame);
    
    // If a cell has accessory view or system accessory type, its content view's width is smaller
    // than cell's by some fixed values.
        if (cell.accessoryView) {
        contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
    } else {
        static CGFloat systemAccessoryWidths[] = {
            [UITableViewCellAccessoryNone] = 0,
            [UITableViewCellAccessoryDisclosureIndicator] = 34,
            [UITableViewCellAccessoryDetailDisclosureButton] = 68,
            [UITableViewCellAccessoryCheckmark] = 40,
            [UITableViewCellAccessoryDetailButton] = 48
        };
        contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
    }
    
    CGSize fittingSize = CGSizeZero;
    // If auto layout enabled, cell's contentView must have some constraints.
    BOOL autoLayoutEnabled = cell.contentView.constraints.count > 0 && !cell.fd_enforceFrameLayout;
    
       if (autoLayoutEnabled) {
        // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
        // of growing horizontally, in a flow-layout manner.
        if (IOS_VERSION > 10.2) {
            [cell layoutIfNeeded];
        }
        NSLayoutConstraint *tempWidthConstraint =
        [NSLayoutConstraint constraintWithItem:cell.contentView
                                     attribute:NSLayoutAttributeWidth
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:1.0
                                      constant:contentViewWidth];
        [cell.contentView addConstraint:tempWidthConstraint];
        // Auto layout engine does its math
        fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        [cell.contentView removeConstraint:tempWidthConstraint];
        
    } else {
        
        // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
        // This is the same method used in iOS8 self-sizing cell's implementation.
        // Note: fitting height should not include separator view.
        SEL selector = @selector(sizeThatFits:);
        BOOL inherited = ![cell isMemberOfClass:UITableViewCell.class];
        BOOL overrided = [cell.class instanceMethodForSelector:selector] != [UITableViewCell instanceMethodForSelector:selector];
        if (inherited && !overrided) {
            NSAssert(NO, @"Customized cell must override '-sizeThatFits:' method if not using auto layout.");
        }
        fittingSize = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)];
    }
    
    // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
    if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
        fittingSize.height += 1.0 / [UIScreen mainScreen].scale;
    }
    
    if (autoLayoutEnabled) {
        [self fd_debugLog:[NSString stringWithFormat:@"calculate using auto layout - %@", @(fittingSize.height)]];
    } else {
        [self fd_debugLog:[NSString stringWithFormat:@"calculate using frame layout - %@", @(fittingSize.height)]];
    }
      return fittingSize.height;
       }
    

    *重点在这里


    1.png

    当然如果这个能满足你也是很好的,但是却满足不了我的工程,这个是可以完全解决nib 创建的cell 的高度问题,但是纯代码用Masonry加约束的却出现了问题,反而不能换行了,所以这个方法也不适合我,此路不通我再想他法继续往下看

    3.给cell.contentView 加左右约束

    在这里我这样理解的,既然xcode自动帮我们加的左右约束有问题,那我不用他的了, 我自己加

    2.png

    在同样的地方替换掉layoutIfNeeded,换成加左右约束, 记得后面加上priorityLow ,这样是避免跟cell 里面手动加的约束起冲突,这样就技能满足nib cell 也能满足纯代码cell ,也不用一个cell 一个cell 的改。至此这个问题完美的解决了(我的问题是解决了,你的解决了吗?欢迎留言共同探讨,小牛路过,不喜勿喷!)

    <strong>什么,你还懒得敲,要我发源码!好吧忍不了你了<strong>
    - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id))configuration
    {
    if (!identifier) {
    return 0;
    }

    // Fetch a cached template cell for `identifier`.
    UITableViewCell *cell = [self fd_templateCellForReuseIdentifier:identifier];
    
    // Manually calls to ensure consistent behavior with actual cells (that are displayed on screen).
    [cell prepareForReuse];
    
    // Customize and provide content for our template cell.
    if (configuration) {
        configuration(cell);
    }
    
    CGFloat contentViewWidth = CGRectGetWidth(self.frame);
    
    // If a cell has accessory view or system accessory type, its content view's width is smaller
    // than cell's by some fixed values.
    if (cell.accessoryView) {
        contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
    } else {
        static CGFloat systemAccessoryWidths[] = {
            [UITableViewCellAccessoryNone] = 0,
            [UITableViewCellAccessoryDisclosureIndicator] = 34,
            [UITableViewCellAccessoryDetailDisclosureButton] = 68,
            [UITableViewCellAccessoryCheckmark] = 40,
            [UITableViewCellAccessoryDetailButton] = 48
        };
        contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
    }
    
    CGSize fittingSize = CGSizeZero;
    // If auto layout enabled, cell's contentView must have some constraints.
    BOOL autoLayoutEnabled = cell.contentView.constraints.count > 0 && !cell.fd_enforceFrameLayout;
    
    if (autoLayoutEnabled) {
        // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
        // of growing horizontally, in a flow-layout manner.
        if (IOS_VERSION > 10.2) {
            [cell.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.mas_equalTo(0).priorityLow();
                make.right.mas_equalTo(0).priorityLow();
            }];
        }
        NSLayoutConstraint *tempWidthConstraint =
        [NSLayoutConstraint constraintWithItem:cell.contentView
                                     attribute:NSLayoutAttributeWidth
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:nil
                                     attribute:NSLayoutAttributeNotAnAttribute
                                    multiplier:1.0
                                      constant:contentViewWidth];
        [cell.contentView addConstraint:tempWidthConstraint];
        // Auto layout engine does its math
        fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        [cell.contentView removeConstraint:tempWidthConstraint];
        
    } else {
        
        // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
        // This is the same method used in iOS8 self-sizing cell's implementation.
        // Note: fitting height should not include separator view.
        SEL selector = @selector(sizeThatFits:);
        BOOL inherited = ![cell isMemberOfClass:UITableViewCell.class];
        BOOL overrided = [cell.class instanceMethodForSelector:selector] != [UITableViewCell instanceMethodForSelector:selector];
        if (inherited && !overrided) {
            NSAssert(NO, @"Customized cell must override '-sizeThatFits:' method if not using auto layout.");
        }
        fittingSize = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)];
    }
    
    // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
    if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
        fittingSize.height += 1.0 / [UIScreen mainScreen].scale;
    }
    
    if (autoLayoutEnabled) {
        [self fd_debugLog:[NSString stringWithFormat:@"calculate using auto layout - %@", @(fittingSize.height)]];
    } else {
        [self fd_debugLog:[NSString stringWithFormat:@"calculate using frame layout - %@", @(fittingSize.height)]];
    }
    
        return fittingSize.height;
     }
    

    如果你的是其他版本的 自己对比找下,如果你是最新版的看下图

    22.png

    方法


    33.png

    位置


    44.png

    😝😝😝😝😝 客官慢走,喜欢点赞呗 😝😝😝😝😝

    相关文章

      网友评论

      • 爱自由鹏:我没用是xib/nib,用的纯代码+masonry也有问题,设置了label的preferredMaxLayoutWidth就可以了,谢谢!
        另,GitHub上的代码我用cocoapod安装1.6,结果拉取的代码没有变,在github上手动下载的代码是OK的,你是没有发布吗?(用cocoapod管理的1.6不对)
      • 扬生哥哥: 兄弟,textview 上面计算不准的话有解决方法么?
      • 冷酷的心:contentView里如果包含了需要换行的UILabel,超过100个字符,计算就不准了,请验证
        爱自由鹏:@冷酷的心 github的demo和cocoapod管理的1.6不一样,cocoapod还是原来的代码,github的demo是正确的,他应该是没有发布
        冷酷的心: @陈雨尘 问题找到了,使用pod安装的代码有问题,pod里1.6版本的代码和github里的不一样,github的demo没问题,谢谢
        陈雨尘:@冷酷的心 我这边测试的是可以的哦,你的不行吗 ,能发个Dome 过来吗
      • 布鲁克先生:完美解决了我的问题,万分感谢啊
        陈雨尘:不客气,共同学习哈
      • RhythmMaster:6666,看了这个才知道问题所在,xib不能用啊,我直接给contentView来个约束就没问题了
      • 西西西瓜:遇到了,这个坑爹的bug了。。。。
        西西西瓜:@陈雨尘 能把你的UITableView+FDTemplateLayoutCell 发给我吗。。。放git好吗
        西西西瓜:@陈雨尘 你的 UITableView+FDTemplateLayoutCell 是哪个版本的啊,怎么我的和你的代码不一样
        陈雨尘:哈哈 ,遇到问题才能让我们成长
      • c7fbb79c1b88:经过测试还是需要设置UILabel的preferredMaxLayoutWidth的属性,否则上下会留空白。
        当你的XXXCell.xib文件是基于iPhone7 Simulator尺寸布局的,运行于iPhone7 Plus Simulator模拟器就会出现上下留白,系统是以iPhone7 Simulator宽度作为换行的宽度,到了iPhone7 Plus Simulator,自然上下留空白了。希望能解决这个问题,iOS10.3之前没有这个问题。
        c7fbb79c1b88:@陈雨尘 一切正常的情况下当然不管是7还是7P都是正常的,比如10.3之前都是正常的。现在我的意思是在10.3的时候使用UITableView+FDTemplateLayoutCell然后根据你上文仅仅加上以下代码还是有问题的(上下有空白),
        if (IOS_VERSION > 10.2) {
        [cell.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(0).priorityLow();
        make.right.mas_equalTo(0).priorityLow();
        }];
        }

        在7P上运行还有约束冲突,
        "<NSLayoutConstraint:0x6080000990f0 UITableViewCellContentView:0x7fb604f0d500.width == 414>",
        "<NSLayoutConstraint:0x600000097430 UITableViewCellContentView:0x7fb604f0d500.width == 320>"

        如果把cell的XIB的宽度手动拉到414,那么5S、7、7P显示都正常,但是还是会有约束冲突

        我的一个例子你看下(分别在iOS的5S、7、7P下测试),https://github.com/yansun2006/TestLayoutCellBugProj
        陈雨尘:不会的 ,Auto Layout 不会因为你是用的7加的约束,到7P 上显示的时候就不行了,如果这样的话还这么算的上是屏幕适配呢,其实你加好约束的话这个宽度肯定是也是对应的,只是在nib 创建的 时候,刷新之后xcode没有在次去获取约束信息从而计算这个宽度而已,所以你只要想办法让他重新计算下这个宽度就行了(例如我上面所说的)。当然 preferredMaxLayoutWidth 设置这个也是可以的,只要你需要改的地方少,完全没问题。
      • f49607ee0f86:好厉害奥
      • d40f8aff5031:辛亏及时提醒,太赞了,谢啦
      • 21e0c0f0da08:有用有用,谢谢了
      • ff8e77440971:哈哈棒👍🏻

      本文标题:iOS 10.3 Label高度计算问题 (UITableVie

      本文链接:https://www.haomeiwen.com/subject/giphattx.html