Обтекание надписью заголовка UIButton с усечением хвоста - PullRequest
19 голосов
/ 11 сентября 2011

Мне нужно включить перенос слов и усечение хвоста одновременно на UIButton х titleLabel.Установка для NumberOfLines значения больше 0 не работает, текст остается на одной строке.

Я уже искал и не нашел решения.Есть идеи?

Ответы [ 7 ]

41 голосов
/ 31 мая 2013

Это не правильно:

lblTemp.lineBreakMode = NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail
lblTemp.numberOfLines = 0;

NSLineBreakMode определен в NSParagraphStyle.h как:

typedef NS_ENUM(NSInteger, NSLineBreakMode) {       /* What to do with long lines */
    NSLineBreakByWordWrapping = 0,      /* Wrap at word boundaries, default */
    NSLineBreakByCharWrapping,      /* Wrap at character boundaries */
    NSLineBreakByClipping,      /* Simply clip */
    NSLineBreakByTruncatingHead,    /* Truncate at head of line: "...wxyz" */
    NSLineBreakByTruncatingTail,    /* Truncate at tail of line: "abcd..." */
    NSLineBreakByTruncatingMiddle   /* Truncate middle of line:  "ab...yz" */
} NS_ENUM_AVAILABLE_IOS(6_0);

Обратите внимание, что это NS_ENUM, а не NS_OPTION, поэтому он не предназначен для использования в качестве маски. Для получения дополнительной информации см это .

В действительности использование оператора | для этих констант приводит к маске, совпадающей с NSLineBreakByTruncatingTail:

(NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail) == 4
NSLineBreakByTruncatingTail == 4

Насколько я знаю, усечение последней строки в Базовом тексте и также перенос слов не может быть сделано с помощью простых API-интерфейсов CTFramesetterCreateWithAttributedString & CTFrameDraw , но может быть выполнено с построчной разметкой, которая UILabel должен делать.

iOS 6 упрощает это, предоставляя новые API рисования в NSStringDrawing.h:

typedef NS_ENUM(NSInteger, NSStringDrawingOptions) {
    NSStringDrawingTruncatesLastVisibleLine = 1 << 5, // Truncates and adds the ellipsis character to the last visible line if the text doesn't fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set.
    NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin
    NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights
    NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds
} NS_ENUM_AVAILABLE_IOS(6_0);

@interface NSAttributedString (NSExtendedStringDrawing)
- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
@end

Так что, если вы используете UILabel, вы хотите, чтобы NSParagraphStyle вашего NSAttributedString или lineBreakMode на самой метке были установлены на:

NSLineBreakByTruncatingTail

И свойство numberOfLines на метке должно быть установлено в 0.

Из заголовков UILabel на numberOfLines:

// if the height of the text reaches the # of lines or the height of the view is less than the # of lines allowed, the text will be
// truncated using the line break mode.

Из документации UILabel:

This property controls the maximum number of lines to use in order to fit the label’s text into its bounding rectangle. The default value for this property is 1. To remove any maximum limit, and use as many lines as needed, set the value of this property to 0.
If you constrain your text using this property, any text that does not fit within the maximum number of lines and inside the bounding rectangle of the label is truncated using the appropriate line break mode.

Единственная проблема, которая возникает с этой несколько непонятной функцией UILabel, заключается в том, что вы не можете получить размер перед рисованием (что необходимо для некоторых динамических макетов UITableView + UITableViewCell), не прибегая к изменению NSParagraphStyle NSAttributedString на лету.

Начиная с iOS 6.1.4, вызов -boundingRectWithSize: options: context с NSAttributedString с режимом разрыва строки NSLineBreakByTruncatingTail (для UILabel), возвращает неверную высоту одной строки , даже если следующие параметры передано:

(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine)

(Обратите внимание, что NSStringDrawingUsesLineFragmentOrigin является необходимостью для многострочных строк.)

Что еще хуже, так это то, что lineBreakMode UILabel не переопределяет стиль абзаца NSAttributedStrings, поэтому вам нужно изменить стиль абзаца приписанной строки для расчета размера, а затем передать его UILabel, чтобы он мог нарисуй его.

То есть NSLineBreakByWordWrapping для -boundingRectWithSize: options: context и NSLineBreakByTruncatingTail для UILabel (поэтому он может использовать NSStringDrawingTruncatesLastVisibleLine для внутреннего использования или для любых других действий, чтобы обрезать последнюю строку)

Единственная альтернатива, если вы не хотите изменять стиль абзаца вашей строки более одного раза, - это сделать простой подкласс UIView, который переопределяет -drawRect: (с соответствующим параметром contentMode, также настроенным на перерисовку), и использует iOS 6 новый API рисования:

- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);

Не забывайте использовать NSLineBreakByWordWrapping и передавать (NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine) в качестве параметров.

Наконец, перед iOS 6, если вы хотите сделать перенос слов + усечение хвоста для приписанной строки, вам придется делать построчное размещение самостоятельно с Core Text.

4 голосов
/ 09 июня 2012
[self.costomButton.titleLabel setTextAlignment:UITextAlignmentLeft];
[self.costomButton.titleLabel setNumberOfLines:3];

Убедитесь, что вы установили Alignment first ps: это работает, только если версия системы больше 5.0

4 голосов
/ 11 октября 2011

Я решил это в тот же день, когда опубликовал этот вопрос, поставив UIButton поверх UILabel с numberOfLines, установленным на 3. Я оставил это недопустимым, чтобы увидеть, если у кого-то есть идея получше, но, видимо, другого решения нет.

0 голосов
/ 30 ноября 2017
button.titleLabel.numberOfLines = 2;
button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
UIFont * theFont = [UIFont systemFontOfSize: 14]; // you set
CGSize textSize = [titleStr sizeWithAttributes:@{NSFontAttributeName: theFont}];
CGFloat theWidth = kScreenWidth-otherWidthYouSet;// I thought the button's frame is content driving ,and is limited 
CGFloat ratio = theWidth*heightYouSet/((textSize.width+4)*(textSize.height+6));// 4 , 6 , is made by experience . I think the textSize is taken one line text default by the system 
NSUInteger validNum = ratio * titleStr.length;

if(ratio<1){
    [button setTitle: [[titleStr substringToIndex: validNum] stringByAppendingString: @"..."] state: yourState];

}
else{
    [button setTitle: titleStr state: yourState];
}
0 голосов
/ 17 ноября 2013

Все свойства пользовательского интерфейса устарели в iOS. Используйте аббревиатуры NS вместо пользовательского интерфейса. Как показано на примере здесь - NSLineBreakByTruncatingMiddle

0 голосов
/ 08 июня 2012

Попробуйте установить числоOfLines больше 2, и соответственно установите высоту.

    m_button = [UIButton buttonWithType:UIButtonTypeCustom];
[m_button setFrame:CGRectMake(isLandscape?20:10, 40, isLandscape?300:250, 40)];
m_button.titleLabel.font  = [UIFont fontWithName:@"HelveticaNeue" size:17];
[m_btnDiscoverPoint setTitle:@"Title" forState:UIControlStateNormal];
CGRect buttonFrame = [m_button frame];

if ([m_button.titleLabel.text length]>0) {
    CGSize suggestedSize = [m_button.titleLabel.text sizeWithFont:[UIFont fontWithName:@"HelveticaNeue" size:17] constrainedToSize:CGSizeMake(FLT_MAX,m_button.frame.size.height) lineBreakMode:UILineBreakModeWordWrap];

    if (suggestedSize.width >= self.view.frame.size.width) {
        suggestedSize.width = self.view.frame.size.width-10;
        suggestedSize.height=suggestedSize.height+20;
        m_button.titleLabel.numberOfLines=2;
    }
    else{
        m_button.titleLabel.numberOfLines=1;
    }

    buttonFrame.size.width = suggestedSize.width;

    [m_button setFrame:buttonFrame];
}
[m_button setBackgroundColor:[UIColor clearColor]];
[m_button addTarget:self action:@selector(btnClickAction) forControlEvents:UIControlEventTouchUpInside];
0 голосов
/ 11 октября 2011

Вы можете указать более одного lineBreakMode для метки, используя побитовый оператор ИЛИ.

Например, следующий код обернул бы текст метки и добавил бы многоточие в конце текста, когда он расширялся за пределы размера рамки метки.

lblTemp.lineBreakMode = UILineBreakModeWordWrap | UILineBreakModeTailTruncation;
lblTemp.numberOfLines = 0;

ОБНОВЛЕНИЕ: это не правильно. Кажется, это работает, потому что UILineBreakModeWordWrap равно 0 в перечислении. Смотрите комментарии ниже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...