Domanda

I have an addition to NSString which automatically resizes a UILabel depending on the text that's being read into it (I have a simple app showing quotations, so some are a few words, some a couple sentences). Below that quote label, I also have an author label, which (oddly enough) has the author of the quote in it.

I'm trying to position that author label directly beneath the quote label (as in, its y coordinate would be the quote label's y coordinate plus the quote label's height. What I'm seeing is some space being placed between the two labels, that depending on the length of the quote, changes size. Smaller quotes have more space, while longer quotes have less space. Here's a quick diagram of what I'm seeing:

enter image description here enter image description here

Note the gap between the red and blue boxes (which I've set up using layer.borderColor/borderWidth so I can see them in the app), is larger the shorter the quote is.

If anyone can sift through the code below and help point me towards exactly what's causing the discrepancy, I'd be really grateful. From what I can see, the author label should always be 35 pixels beneath the quote label's y + height value.

Just to confirm: everything is hooked up correctly in Interface Builder, etc. The content of the quote's getting in there fine, everything else works, so it's hooked up, that isn't the issue.

To clarify, my question is: Why is the gap between the labels changing dependant on the quote's length, and how can I get a stable, settable gap of 35 pixels correctly?

Here's the code I'm using to position the labels:

// Fill and format Quote Details
_quoteLabel.text = [NSString stringWithFormat:@"\"%@\"", _selectedQuote.quote];
_authorLabel.text = _selectedQuote.author;
[_quoteLabel setFont: [UIFont fontWithName: kScriptFont size: 28.0f]];
[_authorLabel setFont: [UIFont fontWithName: kScriptFontAuthor size: 30.0f]];

// Automatically resize the label, then center it again.
[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];

// Position the author label below the quote label, however high it is.
[_authorLabel setFrame: CGRectMake(11, 11 + _quoteLabel.frame.size.height + 35, _authorLabel.frame.size.width, _authorLabel.frame.size.height)];

Here's my custom method for sizeToFitMultipleLines:

- (void) sizeToFitMultipleLines
{
    if (self.adjustsFontSizeToFitWidth) {
        CGFloat adjustedFontSize = [self.text fontSizeWithFont: self.font constrainedToSize: self.frame.size minimumFontSize: self.minimumScaleFactor];
        self.font = [self.font fontWithSize: adjustedFontSize];
    }
    [self sizeToFit];
}

And here's my fontSizeWithFont:constrainedToSize:minimumFontSize: method:

- (CGFloat) fontSizeWithFont: (UIFont *) font constrainedToSize: (CGSize) size minimumFontSize: (CGFloat) minimumFontSize
{
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self sizeWithFont: font constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
    UIFont *newFont = font;

    // Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0 && fontSize > minimumFontSize) {
        fontSize--;
        newFont = [UIFont fontWithName: font.fontName size: fontSize];
        height = [self sizeWithFont: newFont constrainedToSize: CGSizeMake(size.width, FLT_MAX) lineBreakMode: NSLineBreakByWordWrapping].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self componentsSeparatedByCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
        CGFloat width = [word sizeWithFont: newFont].width;
        while (width > size.width && width != 0 && fontSize > minimumFontSize) {
            fontSize--;
            newFont = [UIFont fontWithName: font.fontName size: fontSize];
            width = [word sizeWithFont: newFont].width;
        }
    }
    return fontSize;
}
È stato utile?

Soluzione

After you called size to fit on both labels, calculate the distance between their frames and change them accordingly:

[quoteLabel sizeToFit];
[authorLabel sizeToFit];

float distance = authorLabel.frame.origin.y - quoteLabel.frame.size.height;
float difference = distance - 35;

authorLabel.frame = CGRectMake(authorLabel.frame.origin.x,(authorLabel.frame.origin.y - difference),authorLabel.frame.size.width,authorLabel.frame.size.height);

The reason the gap changes is that the quote label frame changes its height dependent on its content when you call sizeToFit.

UPDATE

Given the recent developments in the comments, I think you have 3 possibilities:

  • resize the whitespace instead of only the words, so that the string actually fits in the frame correctly
  • somehow access the CTFramesetter of UILabel to see what the actual frame, when all is said and done, amounts to
  • make your own UIView subclass that handles Core Text drawing in its draw rect method (should be easy in your case), since after all you are trying to give to UILabel a behavior that it's not meant for

Altri suggerimenti

It probably is moving where you want it, but then an auto-layout constraint or a spring/strut is moving it afterwards.

EDIT:
My first thought (which I ruled out because you said that the box around the words was the label frame. In later comments, you say that this is not an actual screen shot, but just a representation of it, so it could still be correct) was that you are doing this wrong:

[_quoteLabel sizeToFitMultipleLines];
[_quoteLabel setFrame: CGRectMake(11, 11, 298, _quoteLabel.frame.size.height)];

In the first line, you are sizing the text to fit in whatever the current width of the label might be, and then you turn around in the second line and change the width of the label. So most likely, what is happening is that you are sizing the label for some smaller width, which makes it tall. You then make the label wider than it was before and the text expands to fit the wider label, leaving a blank area beneath the actual text, although the frame has not changed. This makes the space betwee the labels exactly 35 as you want, however the top label's text does not go all of the way to the bottom of its frame so the white space is more than you want. Basically, you have this:

*************
* text text *
* text text *
*           *
*           *
*           *
*************

*************
* text text *
*************

If this is the case, then you would fix it by setting the width first, like this:

// You could put anything for the 200 height since you will be changing it in the next line anyway.
[_quoteLabel setFrame: CGRectMake(11, 11, 298, 200];  
[_quoteLabel sizeToFitMultipleLines];

I ended up solving the problem by using a single UILabel, and CoreText with an NSAttributedString. Kind of a cop-out, but it works.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top