Victor Ronin Victor Ronin - 5 months ago 15
iOS Question

Self-sizing cells and dynamic size controls for iOS

Problem definition

I am trying to build a custom control which will behave similarly to

UILabel
. I should be able to place such a control inside of a self-sizing table cell and it should:


  • Wrap it's content (like UILabel with numberOfLines=0 does)

  • automatically extend self-sized cell height size

  • handle a device rotation

  • don't require any special code in UITableCellView or ViewControll to implement this functionality (UILabel doesn't require any special code for that).



Research

The first thing which I did is very simple. I decided to observe how UILabel works. I did following:


  • created a table with self-sizing cells

  • created a custom cell, put UILabel (with
    numberOfLines=0)
    in it

  • created constraints to make sure that UILabel occupies a whole cell

  • subclasses UILabel and overrode a bunch of methods to see how it behaves



I checked following things


  • Run it in a portrait (the label is displayed correctly over several lines) and the cell height is correct

  • Rotate it. The table width and height was updated and they are correct too.



I observed that it behaves well. It doesn't require any special code and I saw the order of (some) calls which system does to render it.

A partial solution

@Wingzero wrote a partial solution below. It creates cells of a correct size.

However, his solution has two problems:


  • It uses "self.superview.bounds.size.width". This could be used if your control occupies a whole cell. However, if you have anything else in the cell which uses constraints then such code won't calculate a width correctly.

  • It doesn't handle rotation at all. I am pretty sure it doesn't handle other resizing events (there are bunch of less common resizing events - like a statusbar getting bigger on a phone call etc).



Do you know how to solve these problems for this case?
I found a bunch of articles which talks about building more static custom controls and using pre-built controls in self-sizing cells.

However, I haven't found anything which put together a solution to handle both of these.

Answer

I have to use the answer section to post my ideas and moving forward, though it may not be your answer, since I am not fully understand what's blocking you, because I think you already know the intrinsic size and that's it.

based on the comments, I tried to create a view with a text property and override the intrinsic:

header file, later I found maxPreferredWidth is not used totally, so ignore it:

@interface LabelView : UIView

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

@end

.m file:

#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

and a UITableViewCell with xib:

header file:

@interface LabelCell : UITableViewCell

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

@end

.m file:

@implementation LabelCell

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

@end

xib, it's simple, just top, bottom, leading, trailing constraints: enter image description here

So running it, based on the text's bounding rect, the cell's height is different, in my case, I have two text to loop: 1. "haha", 2. "asdf"{repeat many times to create a long string}

so the odd cell is 19 height and even cell is 58 height: enter image description here

Is this what are you looking for?

My ideas:

the UITableView's cell's width is always the same as the table view, so that's the width. UICollectionView may be more issues there, but the point is we will calculate it and just return it is enough.

Demo project: https://github.com/liuxuan30/StackOverflow-DynamicSize
(I changed based on my old project, which has some images, ignore those.)