gpt4 book ai didi

cocoa - 截断多行 NSTextField 的最后一行

转载 作者:行者123 更新时间:2023-12-03 16:10:54 26 4
gpt4 key购买 nike

我正在尝试创建一个类似于 Finder 的文件标签的文本字段。我希望最后(第二)行在中间被截断。

我从多行 NSTextField 开始.

但是,调用[self.cell setLineBreakMode:NSLineBreakByTruncatingMiddle];结果文本字段仅显示单个截断行(不再换行)。

这是它在 Finder 中的样子:

Finder example

最佳答案

如果您想像查找器标签一样换行文本,那么使用两个标签没有任何好处,因为您需要知道第一行的最大可破坏文本量是多少。另外,如果您正在构建将显示大量项目的东西,那么两个标签将不必要地加重 GUI 的负担。

像这样设置你的 NSTextField.cell:

[captionLabel.cell setLineBreakMode: NSLineBreakByCharWrapping];

然后找到“NS(Attributed)String+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;
}

干杯!

PS 在原型(prototype)中测试效果很好,可能有错误......找到它们

关于cocoa - 截断多行 NSTextField 的最后一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11138786/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com