tomknig / TOMSMorphingLabel

Configurable morphing transitions between text values of a label.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

intrinsicSize not correct

fatuhoku opened this issue · comments

I want to self-size cells in my collection view, so I need a means of updating the cell's constraints based on the content. The content in this case is a TOMSMorphingLabel.

It appears to report a size of {0, 0} regardless of what text is set onto it.

This is surprising, considering it is just a UILabel subclass, right.

I thought I could spend just 5 minutes fixing this but I saw the way that the label works and I think it's better if the author had a look at how best to approach the problem.

I wrote a couple of tests you might find useful...

- (void)test_set_text_makes_text_available
{
    TOMSMorphingLabel *sut = [[TOMSMorphingLabel alloc] initWithFrame:CGRectMake(0, 0, 42, 42)];
    sut.text = @"Text";

    XCTAssertEqual(sut.text, @"Text");
}

- (void)test_intrinsic_size_of_target_text_should_be_the_same_as_ui_label
{
    NSString *string = @"TOMSMorphingLabel rocks";

    TOMSMorphingLabel *sut = [[TOMSMorphingLabel alloc] initWithFrame:CGRectMake(0, 0, 42, 42)];
    sut.text = string;

    UILabel *label = [[UILabel alloc] init];
    label.font = sut.font;
    label.text = string;

    NSLog(@"normal label: %@", NSStringFromCGSize(sut.intrinsicContentSize));
    NSLog(@"morphing label: %@", NSStringFromCGSize(label.intrinsicContentSize));

    XCTAssertTrue(CGSizeEqualToSize(sut.intrinsicContentSize, label.intrinsicContentSize));
}

Hi. The problem with the intrinsicContentSize probably originates from the fact that setText: method animates by default and uses the actual text property to store intermediate results while animating.

As a result we have the following:

  1. Calling sut.text = string; does not actually set the string as the text of the label immediately. It will asynchronously change the text to this value after the given amount of time. The intrinsic size is also updated with the text value being updated.
  2. text and other properties which depend on it (including intrinsicContentSize) cannot be reliably read from the TOMSMorphingLabel because at any given time the text property does not represent the final state of the text (which we try to set using setText: setter), but rather it represents the current intermediate value which is displayed by the label in the current frame of the animation.

For example in the TOMSMorphingLabelExample project when I add the following code to the toggleTextForLabel: method:

- (void)toggleTextForLabel:(UILabel *)label
{
    NSString *text = self.textValues[self.idx++];
    label.text = text;
    NSLog(@"text: %@, label.text: %@", text, label.text);
    .... // the rest unchanged
}

I get the following output on the iOS Simulator:

First label animation completed
text: Swift, label.text: SwifTest
text: Swiftilicious, label.text: Swiftilicious
text: delicious, label.text: delSwiftilicious
text: 开, label.text: 开delicious
text: 开源, label.text: 开源
text: 2⃣3⃣4⃣, label.text: 2⃣3⃣4⃣开源
text: 1⃣2⃣3⃣4⃣5⃣, label.text: 1⃣2⃣3⃣4⃣5⃣
text: , label.text: 1⃣2⃣3⃣4⃣5⃣
text: Swift, label.text: Swift

Note the various 'hybrid' strings which are actually combinations of the text values passed to the setText:. I guess the actual output would be dependent on the hardware on which the app is running.

So we can semi-reliably read the text value only after the animation has finished. The same goes for the intrinsic content size.

A possible solution would be keeping an internal UILabel for displaying the text and animations, but exposing the reliable label properties with getters and setters directly corresponding to each other.

This approach was used in cbpowell/MarqueeLabel which provides an UILabel subclass with a special animated behavior.