Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with UILabel text wrapping the view's intrinsic width in an expression. #150

Open
Dragonspell99 opened this issue Oct 17, 2018 · 8 comments

Comments

@Dragonspell99
Copy link

Dragonspell99 commented Oct 17, 2018

I'm trying to create a UILabel that will size itself automatically according to the following rules:

  1. When the text length is short, the UILabel width should match the intrinsic content size. In other words, the label should grow or shrink to wrap the intrinsic content size.
  2. When the width of the label exceeds a certain threshold, it should stop growing in width and wrap to another line so that it never exceeds the width threshold.

In order to accomplish this, I have defined my view as follows:

<UIViewController>
    <UILabel
        backgroundColor="#CCC"
        left="24"
        top="24"
        width="min(auto, 320)"
        numberOfLines="0"
        text="This text is to verify line wrapping when using an expression that contains the auto keyword."
    />
</UIViewController>

As can be seen in the width expression, I would like the width to be the smaller of the auto width or 320. When I reduce the text to just a few words, this works as expected. However, when the text is too long, as in the supplied example, the label doesn't grow beyond 320 as desired but it doesn't wrap the text. It truncates it instead and remains at 1 line.

Conversely, if I change my width expression to something like:
width="min(1000, 320)"
Leaving everything else the same, the label wraps properly with a width at 320.

Therefore, there seems to be a problem with causing the label to wrap text when using the auto keyword in the width expression.

@nicklockwood
Copy link
Owner

@Dragonspell99 it looks like a bug - I'll investigate.

In the meantime, it works if you use min(auto, 100%), so you can get the same effect by wrapping the label in a fixed-width container view:

<UIView
    left="24"
    top="24"
    width="320"
    height="auto">
    <UILabel
        backgroundColor="#CCC"
        width="min(auto, 100%)"
        numberOfLines="0"
        text="This text is to verify line wrapping when using an expression that contains the auto keyword."
    />
</UIView>

@Dragonspell99
Copy link
Author

Dragonspell99 commented Oct 17, 2018

@nicklockwood Thanks for the prompt reply!

I've tested your suggestion and it works great. Unfortunately, I'm having trouble applying it to my desired layout. I simplified the code example above to focus on the problem but in my real layout, I already have the UILabel wrapped by a UIView whose width depends on its children. Therefore, I can't set it to a fixed width and trying to reference it otherwise in the child's width expression will result in a circular reference.

What I'm trying to achieve is a layout where I'm showing a chat bubble next to a user's profile picture. The bubble would be a rounded rectangle (wrapping view) with a grey background that contains the user's name (top label) followed by the message text. The bubble has to grow to wrap both the user name and message.

I'm including a larger piece of the layout code below to see if an easy fix jumps out at you. Otherwise, I can try to fix it by measuring the string in code and passing the measured width as a parameter to the layout to be used in the expression.

    <UIView 
        backgroundColor="#EFF1F3"
        top="SPACING"
        left="previous.right + SPACING"
        height="auto + PADDING"
        width="auto + PADDING">

        <UILabel
            left="PADDING"
            top="PADDING"
            width="auto"
            font="medium 15"
            text="User Name"
        />

        <UILabel
            left="PADDING"
            top="previous.bottom + SPACING"
            width="min(auto, 320)"
            numberOfLines="0"
            text="This text is to verify line wrapping when using an expression that contains the auto keyword."
        />
    </UIView>

P.S.> Great job on the library! I've been looking for something like Layout for a long time and stumbled upon it by chance the other day.

@nicklockwood
Copy link
Owner

@Dragonspell99 as far as I can see, there's no way to do this with the current release. I've pushed a fix to the develop branch that should make it work as you've written it above. Can you try it out and see if it solves your use-case?

@Dragonspell99
Copy link
Author

@nicklockwood

I've tested the changes that you pushed to the develop branch and it works perfectly for my use case. Thanks!

@nicklockwood
Copy link
Owner

@Dragonspell99 great. I'll do some more testing and assuming everything's OK with the other test projects I'll push a release with those changes asap.

@Dragonspell99
Copy link
Author

Dragonspell99 commented Oct 19, 2018

Hi @nicklockwood ,

Although the above problem was resolved with the commit that you pushed to address the issue, I have found another interesting case that is related to the same layout so I've included it in this same issue.

Context: As described previously, this layout is for a "chat bubble". I'm trying to add the time at which the message was posted in the upper right corner of the bubble along with an icon of a clock adjacent and to the left of the time.

Aligning the UILabel that contains the time to the top right of the parent view (bubble) works fine. However, when I add the clock icon in the UIImageView, the pair of subviews shift outside and to the left of the parent UIView.

I've tested several things and I've isolated the problem which can be reproduced using the following layout which simply attempts to place an UIImageView in the top-right corner of the parent UIView.

<UIViewController>

    <macro name="PADDING" value="8"/>
    <macro name="SPACING" value="12"/>

    <UIView 
        backgroundColor="#EEF1F6"
        top="50"
        left="20"
        height="auto + PADDING"
        width="auto + PADDING">

        <UILabel
            id="AuthorLabel"
            left="PADDING"
            top="PADDING"
            width="auto"
            font="medium 15"
            text="User Name"
        />

        <UIImageView
            top="PADDING"
            right="PADDING"
            width="20"
            height="20"
            backgroundColor="#F00"
        />

        <UILabel
            left="PADDING"
            top="#AuthorLabel.bottom + SPACING"
            width="min(auto, 320)"
            numberOfLines="0"
            text="This text is to verify line wrapping when using an expression that contains the auto keyword."
        />
    </UIView>
</UIViewController>

The above layout produces a red rectangle outside and to the left of the parent UIView (gray chat bubble). Note that the same behaviour applies if an actual image is used instead of just a background colour. If the width expression of the parent UIView is changed from auto + PADDING to a constant value (e.g. 320), then the UIImageView is properly located in the top-right corner of the parent.

What's even more interesting is that if a UILabel is used instead of the UIImageView, it is correctly aligned to the top-right corner despite the above provided that the width expression is completely omitted from the node. Otherwise, the same behaviour occurs. I also tried with a UIView instead of the UIImageView and the same thing happens.

@nicklockwood
Copy link
Owner

@Dragonspell99 I've not worked out why this is happening yet, but replacing right="PADDING" with left="parent.width - width - PADDING" in the UIImageView seems to fix it.

To be clear, these should be equivalent, so this is definitely a Layout bug, but the workaround should allow you to proceed for now.

@Dragonspell99
Copy link
Author

Ok. This works fine for my scenario. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants