Если вы хотите обернуть текст, как метки находок, использование двух меток не поможет вам, так как вам нужно знать, какая максимальная разрывная сумма текста находится в первой строке. Кроме того, если вы создаете что-то, что будет отображать множество элементов, два ярлыка будут излишне завышать GUI.
Установите NSTextField.cell так:
[captionLabel.cell setLineBreakMode: NSLineBreakByCharWrapping];
Затем найдите код "NS (Приписываемые) Струнный + Geometrics" (Google это, это там). Вы должны #import "NS (Attributed) String + Geometrics.h" для измерения текста. Это обезьяны патчей NSString и NSAttributedString
Я включаю следующий код, чтобы обернуть текст так, как это делает Finder в его подписях. Используя один ярлык ниже значка, он предполагает, что, как и Finder, будут две строки надписи.
Во-первых, это, как вы назовете следующий код в код:
NSString *caption = self.textInput.stringValue;
CGFloat w = self.captionLabel.bounds.size.width;
NSString *wrappedCaption = [self wrappedCaptionText:self.captionLabel.font caption:caption width:w];
self.captionLabel.stringValue = wrappedCaption ? [self middleTruncatedCaption:wrappedCaption withFont:self.captionLabel.font width:w] : caption;
Теперь для основного кода:
#define SINGLE_LINE_HEIGHT 21
/*
This is the way finder captions work -
1) see if the string needs wrapping at all
2) if so find the maximum amount that will fit on the first line of the caption
3) See if there is a (word)break character somewhere between the maximum that would fit on the first line and the begining of the string
4) If there is a break character (working backwards) on the first line- insert a line break then return a string so that the truncation function can trunc the second line
*/
-(NSString *) wrappedCaptionText:(NSFont*) aFont caption:(NSString*)caption width:(CGFloat)captionWidth
{
NSString *wrappedCaption = nil;
//get the width for the text as if it was in a single line
CGFloat widthOfText = [caption widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
//1) nothing to wrap
if (widthOfText <= captionWidth)
return nil;
//2) find the maximum amount that fits on the first line
NSRange firstLineRange = [self getMaximumLengthOfFirstLineWithFont:aFont caption:caption width:captionWidth];
//3) find the first breakable character on the first line looking backwards
NSCharacterSet *notAlphaNums = [NSCharacterSet alphanumericCharacterSet].invertedSet;
NSCharacterSet *whites = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSRange range = [caption rangeOfCharacterFromSet:notAlphaNums options:NSBackwardsSearch range:firstLineRange];
NSUInteger splitPos;
if ((range.length == 0) || (range.location < firstLineRange.length * 2/3)) {
// no break found or break is too (less than two thirds) far to the start of the text
splitPos = firstLineRange.length;
} else {
splitPos = range.location+range.length;
}
//4) put a line break at the logical end of the first line
wrappedCaption = [NSString stringWithFormat:@"%@\n%@",
[[caption substringToIndex:splitPos] stringByTrimmingCharactersInSet:whites],
[[caption substringFromIndex:splitPos] stringByTrimmingCharactersInSet:whites]];
return wrappedCaption;
}
/*
Binary search is great..but when we split the caption in half, we dont have far to go usually
Depends on the average length of text you are trying to wrap filenames are not usually that long
compared to the captions that hold them...
*/
-(NSRange) getMaximumLengthOfFirstLineWithFont:(NSFont *)aFont caption:(NSString*)caption width:(CGFloat)captionWidth
{
BOOL fits = NO;
NSString *firstLine = nil;
NSRange range;
range.length = caption.length /2;
range.location = 0;
NSUInteger lastFailedLength = caption.length;
NSUInteger lastSuccessLength = 0;
int testCount = 0;
NSUInteger initialLength = range.length;
NSUInteger actualDistance = 0;
while (!fits) {
firstLine = [caption substringWithRange:range];
fits = [firstLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont] < captionWidth;
testCount++;
if (!fits) {
lastFailedLength = range.length;
range.length-= (lastFailedLength - lastSuccessLength) == 1? 1 : (lastFailedLength - lastSuccessLength)/2;
continue;
} else {
if (range.length == lastFailedLength -1) {
actualDistance = range.length - initialLength;
#ifdef DEBUG
NSLog(@"# of tests:%d actualDistance:%lu iteration better? %@", testCount, (unsigned long)actualDistance, testCount > actualDistance ? @"YES" :@"NO");
#endif
break;
} else {
lastSuccessLength = range.length;
range.length += (lastFailedLength-range.length)/2;
fits = NO;
continue;
}
}
}
return range;
}
-(NSString *)middleTruncatedCaption:(NSString*)aCaption withFont:(NSFont*)aFont width:(CGFloat)captionWidth
{
NSArray *components = [aCaption componentsSeparatedByString:@"\n"];
NSString *secondLine = [components objectAtIndex:1];
NSString *newCaption = aCaption;
CGFloat widthOfText = [secondLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
if (widthOfText > captionWidth) {
//ignore the fact that the length might be an odd/even number "..." will always truncate at least one character
int middleChar = ((int)secondLine.length-1)/2;
NSString *newSecondLine = nil;
NSString *leftSide = secondLine;
NSString *rightSide = secondLine;
for (int i=1; i <= middleChar; i++) {
leftSide = [secondLine substringToIndex:middleChar-i];
rightSide = [secondLine substringFromIndex:middleChar+i];
newSecondLine = [NSString stringWithFormat:@"%@…%@", leftSide, rightSide];
widthOfText = [newSecondLine widthForHeight:SINGLE_LINE_HEIGHT font:aFont];
if (widthOfText <= captionWidth) {
newCaption = [NSString stringWithFormat:@"%@\n%@", [components objectAtIndex:0], newSecondLine];
break;
}
}
}
return newCaption;
}
@end
Ура!
PS Протестировано в прототипе работает отлично, возможно, есть ошибки ... найти их
Хорошо, я сдаюсь - я не могу заставить его даже скомпилировать. Возможно, я использую неправильный патч (я Googled и получил его с http://code.google.com/p/lyricus/source/browse/trunk/NS%28Attributed%29String%2BGeometrics.m?r=60), возможно Я неправильно настраиваю свой интерфейс, используя неправильные объекты или имена привязок, поскольку они не были объяснены.Любой шанс, который вы могли бы предоставить немного более подробно, как и полное содержимое ваших файлов .h и .m и где именно вы получаете NS (Attributed) String + Geometrics? –
Этот код в коде Google должен работать. Просто добавьте .h и .m в свой проект и используйте приведенный выше код. Чтобы вызвать код (см. Первые две строки ответа - часть, которая, вероятно, отключает вас, - это заголовок экземпляра var, и он используется методы изнутри, причина в том, что класс, который я использую, представляет собой элемент в поисковом элементе, в виде сетки которого заголовок является ivar. Если вы хотите сделать из него более общую утилиту, создайте класс утилиты и передайте в –
Я обновил код, исправляя некоторые проблемы: теперь он правильно разбивается на разбитые текстовые поля, которые теперь представляют собой любой символ без буквы и цифры, а не только пробел. Я также изменил его так, чтобы текст субтитров и максимальный width теперь передаются как аргументы. –