iOS的自调整单元格大小和动态大小控件

iOS的自调整单元格大小和动态大小控件,ios,uitableview,uiview,Ios,Uitableview,Uiview,问题定义 我正在尝试构建一个自定义控件,其行为类似于UILabel。我应该能够将这样一个控件放置在自调整大小的表格单元格中,并且它应该: 包装它的内容(如numberOfLines=0的UILabel) 自动扩展自调整大小的单元格高度大小 处理设备旋转 不需要UITableCellView或ViewControll中的任何特殊代码来实现此功能(UILabel不需要任何特殊代码) 研究 我做的第一件事很简单。我决定观察UILabel是如何工作的。我做了以下工作: 创建了具有自调整大小单元格的

问题定义

我正在尝试构建一个自定义控件,其行为类似于
UILabel
。我应该能够将这样一个控件放置在自调整大小的表格单元格中,并且它应该:

  • 包装它的内容(如numberOfLines=0的UILabel)
  • 自动扩展自调整大小的单元格高度大小
  • 处理设备旋转
  • 不需要UITableCellView或ViewControll中的任何特殊代码来实现此功能(UILabel不需要任何特殊代码)
研究

我做的第一件事很简单。我决定观察UILabel是如何工作的。我做了以下工作:

  • 创建了具有自调整大小单元格的表
  • 创建一个自定义单元格,将UILabel(带
    numberOfLines=0)
    放入其中
  • 创建约束以确保UILabel占用整个单元格
  • 子类UILabel并重写一组方法以查看其行为
我检查了以下事项

  • 以纵向方式运行(标签在几行上正确显示),单元格高度正确
  • 旋转它。表格的宽度和高度已更新,它们也正确无误
我观察到它表现得很好。它不需要任何特殊的代码,我看到了系统渲染它的(一些)调用顺序

部分解决方案

@Wingzero在下面写了一个部分解决方案。它可以创建大小正确的单元格

然而,他的解决方案有两个问题:

  • 它使用“self.superview.bounds.size.width”。如果控件占用整个单元格,则可以使用此选项。但是,如果单元格中还有其他使用约束的内容,则此类代码将无法正确计算宽度

  • 它根本不处理旋转。我很确定它不会处理其他的调整大小事件(有一些不太常见的调整大小事件,比如电话中的状态栏变大等等)

你知道如何解决这个案件的这些问题吗? 我发现了一些文章,其中讨论了如何构建更多静态自定义控件,以及如何在自调整大小的单元格中使用预构建控件


然而,我还没有找到任何解决方案来处理这两个问题。

我必须使用答案部分来发布我的想法并继续前进,尽管这可能不是你的答案,因为我不完全理解是什么阻碍了你,因为我想你已经知道了内在的规模,就这样

基于这些注释,我尝试创建一个具有文本属性的视图,并覆盖内在属性:

头文件,后来我发现maxPreferredWidth没有完全使用,所以忽略它:

@interface LabelView : UIView

IB_DESIGNABLE
@property (nonatomic, copy) IBInspectable NSString *text;
@property (nonatomic, assign) IBInspectable CGFloat maxPreferredWidth;

@end
.m文件:

#import "LabelView.h"

@implementation LabelView

-(void)setText:(NSString *)text {
    if (![_text isEqualToString:text]) {
        _text = text;
        [self invalidateIntrinsicContentSize];
    }
}

-(CGSize)intrinsicContentSize {
    CGRect boundingRect = [self.text boundingRectWithSize:CGSizeMake(self.superview.bounds.size.width, CGFLOAT_MAX)
                                           options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
                                        attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]}
                                           context:nil];
    return boundingRect.size;
}

@end
@implementation LabelCell

- (void)awakeFromNib {
    [super awakeFromNib];
}

@end
以及具有xib的UITableViewCell:

头文件:

@interface LabelCell : UITableViewCell

@property (weak, nonatomic) IBOutlet LabelView *labelView;

@end
.m文件:

#import "LabelView.h"

@implementation LabelView

-(void)setText:(NSString *)text {
    if (![_text isEqualToString:text]) {
        _text = text;
        [self invalidateIntrinsicContentSize];
    }
}

-(CGSize)intrinsicContentSize {
    CGRect boundingRect = [self.text boundingRectWithSize:CGSizeMake(self.superview.bounds.size.width, CGFLOAT_MAX)
                                           options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading
                                        attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]}
                                           context:nil];
    return boundingRect.size;
}

@end
@implementation LabelCell

- (void)awakeFromNib {
    [super awakeFromNib];
}

@end
xib,很简单,只是顶部、底部、前导和尾随约束:

所以运行它,基于文本的边界矩形,单元格的高度是不同的,在我的例子中,我有两个要循环的文本:1。“哈哈”,2。“asdf”{重复多次以创建长字符串}

所以奇数单元格的高度为19,偶数单元格的高度为58:

这就是你要找的吗

我的想法是:

UITableView的单元格宽度始终与表视图相同,因此这就是宽度。UICollectionView可能会有更多的问题,但关键是我们会计算它并返回它就足够了

演示项目:

(我根据我的旧项目进行了更改,其中有一些图像,请忽略这些图像。)

要基于@Wingzero的另一个答案,布局是一个复杂的问题

提到的
maxPreferredWidth
很重要,它与
UILabel
preferredMaxLayoutWidth
有关。此属性的要点是告诉标签不要只是一条长线,而是在宽度达到该值时更喜欢换行。因此,在计算内部大小时,您将使用
preferredMaxLayoutWidth
(如果设置)或视图宽度中的最小值作为最大宽度

另一个关键方面是
invalidateIntrinsicContentSize
,当某些内容发生更改并意味着需要新布局时,视图应自行调用它

UILabel
不处理旋转-它不知道旋转。视图控制器负责检测和处理旋转,通常是在触发新布局运行之前使布局无效并更新视图大小。标签(和其他视图)仅用于处理生成的布局。作为旋转的一部分,您(即视图控制器)可以更改
preferredMaxLayoutWidth
,因为在横向布局中允许更大的宽度是有意义的。

你在找这样的东西吗?单元格具有动态高度以方便UILabel的内容,并且没有任何代码来计算大小/宽度/高度-只是一些约束

从本质上讲,左侧的标签具有指向单元格的顶部、底部和前导边距,而右侧标签具有指向单元格的尾部边距。只需要一个标签?然后忽略右侧标签,并使用单元格的尾部约束配置左侧标签

如果需要多行标签,请进行配置。根据需要将
numberOfLines
设置为2、3或0

您不需要计算表视图单元格的高度,自动布局将为您计算;但您需要让它知道这一点,告诉它使用自动标注:
self.tableView.rowHeight=UITableViewAutomaticDimension
,或者在
tableView:heightForRowAtIndexPath:
中返回它。您还可以在
tableView:estimatedheightforrowatinexpath:
中告诉表视图一个“粗略”估计,以获得更好的性能

还是不工作?将内容压缩阻力优先级-垂直设置为1000