Finderのファイルラベルに似たテキストフィールドを作成しようとしています。最後の(2番目の)行を途中で切り捨ててほしい。
私は複数行から始めましたNSTextField
。
ただし、呼び出すと[self.cell setLineBreakMode:NSLineBreakByTruncatingMiddle];
、テキストフィールドに1つの切り捨てられた行のみが表示されます(改行はなくなります)。
Finderでの表示は次のとおりです。
Finderのファイルラベルに似たテキストフィールドを作成しようとしています。最後の(2番目の)行を途中で切り捨ててほしい。
私は複数行から始めましたNSTextField
。
ただし、呼び出すと[self.cell setLineBreakMode:NSLineBreakByTruncatingMiddle];
、テキストフィールドに1つの切り捨てられた行のみが表示されます(改行はなくなります)。
Finderでの表示は次のとおりです。
ファインダーラベルのようにテキストを折り返したい場合は、最初の行のテキストの最大分割可能量を知る必要があるため、2つのラベルを使用しても効果はありません。さらに、多くのアイテムを表示するものを構築している場合、2つのラベルがGUIに不必要に負担をかけます。
NSTextField.cellを次のように設定します。
[captionLabel.cell setLineBreakMode: NSLineBreakByCharWrapping];
次に、「NS(Attributed)String + Geometrics」のコードを見つけます(Google it、そこにあります)。テキストを測定するには、「NS(Attributed)String+Geometrics.h」を#importする必要があります。NSStringとNSAttributedStringにモンキーパッチを適用します
Finderがキャプションで行うのとまったく同じようにテキストをラップするために、次のコードを含めます。アイコンの下に1つのラベルを使用すると、Finderのように、2行のキャプションがあると想定されます。
まず、これがコード内で次のコードを呼び出す方法です。
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はうまく機能し、おそらくバグがあります...それらを見つけてください
そこには2つのラベルがあると思います。上の文字にはファイル名の最初の20文字が含まれ、2番目の文字には切り捨てられたオーバーフローが含まれます。
最初のラベルの長さは、おそらくユーザーのフォント設定に基づいて制限されています。