6

Finderのファイルラベルに似たテキストフィールドを作成しようとしています。最後の(2番目の)行を途中で切り捨ててほしい。

私は複数行から始めましたNSTextField

ただし、呼び出すと[self.cell setLineBreakMode:NSLineBreakByTruncatingMiddle];、テキストフィールドに1つの切り捨てられた行のみが表示されます(改行はなくなります)。

Finderでの表示は次のとおりです。

ファインダーの例

4

2 に答える 2

6

ファインダーラベルのようにテキストを折り返したい場合は、最初の行のテキストの最大分割可能量を知る必要があるため、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はうまく機能し、おそらくバグがあります...それらを見つけてください

于 2012-07-30T12:45:30.647 に答える
0

そこには2つのラベルがあると思います。上の文字にはファイル名の最初の20文字が含まれ、2番目の文字には切り捨てられたオーバーフローが含まれます。

最初のラベルの長さは、おそらくユーザーのフォント設定に基づいて制限されています。

于 2012-06-21T13:12:07.970 に答える