2010-03-09 5 views
5

Ho una vista che contiene due NSTextFieldCell s. La dimensione con cui queste celle vengono disegnate deriva dalla dimensione della vista e voglio che il testo in ciascuna cella sia il più grande che si adatta alla dimensione derivata della cella. Ecco quello che ho, che non impostare la dimensione del carattere:Come si imposta la dimensione del carattere in una cella di testo in modo che la stringa riempia il rect della cella?

- (void)drawRect:(NSRect)dirtyRect { 
    /* 
    * Observant readers will notice that I update the whole view here. If 
    * there is a perceived performance problem, then I'll switch to just 
    * updating the dirty rect. 
    */ 
    NSRect boundsRect = self.bounds; 
    const CGFloat monthHeight = 0.25 * boundsRect.size.height; 
    NSRect monthRect = NSMakeRect(boundsRect.origin.x, 
            boundsRect.origin.y + boundsRect.size.height 
            - monthHeight, 
            boundsRect.size.width, 
            monthHeight); 
    [monthCell drawWithFrame: monthRect inView: self]; 

    NSRect dayRect = NSMakeRect(boundsRect.origin.x, 
           boundsRect.origin.y, 
           boundsRect.size.width, 
           boundsRect.size.height - monthHeight); 
    [dayCell drawWithFrame: dayRect inView: self]; 

    [[NSColor blackColor] set]; 
    [NSBezierPath strokeRect: boundsRect]; 
} 

quindi so che posso chiedere una stringa che dimensione che ci sarebbe voluto per determinati attributi, e so che posso chiedere un controllo al cambiamento le sue dimensioni per adattarsi al suo contenuto. Nessuna di queste sembra applicabile: voglio che il contenuto (in questo caso,) della cella sia ridimensionato per adattarlo alle dimensioni rect conosciute, con gli attributi necessari per ottenere quell'essere sconosciuto. Come posso trovare la dimensione necessaria? Supponiamo che io sappia quale font userò (perché lo faccio).

aggiornamento Nota: io non voglio Tronca la stringa, voglio crescere o shrink in modo che il tutto si adatta, con la dimensione del testo più grande possibile, nella rect fornito .

risposta

5

io uso un codice simile ma gestisce diversi tipi di carattere, dimensioni fino a 10.000 e prende in considerazione l'altezza disponibile così come la larghezza dell'area di testo viene visualizzato in.

#define kMaxFontSize 10000 

- (CGFloat)fontSizeForAreaSize:(NSSize)areaSize withString:(NSString *)stringToSize usingFont:(NSString *)fontName; 
{ 
    NSFont * displayFont = nil; 
    NSSize stringSize = NSZeroSize; 
    NSMutableDictionary * fontAttributes = [[NSMutableDictionary alloc] init]; 

    if (areaSize.width == 0.0 || areaSize.height == 0.0) { 
     return 0.0; 
    } 

    NSUInteger fontLoop = 0; 
    for (fontLoop = 1; fontLoop <= kMaxFontSize; fontLoop++) { 
     displayFont = [[NSFontManager sharedFontManager] convertWeight:YES ofFont:[NSFont fontWithName:fontName size:fontLoop]]; 
     [fontAttributes setObject:displayFont forKey:NSFontAttributeName]; 
     stringSize = [stringToSize sizeWithAttributes:fontAttributes]; 

     if (stringSize.width > areaSize.width) 
      break; 
     if (stringSize.height > areaSize.height) 
      break; 
    } 

    [fontAttributes release], fontAttributes = nil; 

    return (CGFloat)fontLoop - 1.0; 
} 
+0

Grazie, mi piace questo approccio. Sarei propenso a rimuovere il limite di 10k e puntare a "quando è fatto, è fatto" - non cambierà il comportamento in quasi tutti i casi. –

+0

Forse mi manca qualcosa, ma non dovrebbe essere: 'if (areaSize.width == 0.0 || areaSize.height == 0.0) {return 0.0;}' O invece di AND, come il carattere non sarà in grado di visualizzare non importa se l'area è zero pixel di larghezza o alta –

+0

Matt, sei completamente corretto. Il codice funziona ancora (e puoi rimuovere completamente quel controllo iniziale e funziona ancora) ma per motivi di velocità impostarlo su OR è ottimale. Grazie. – sgaw

0

Due idee. Una Ho provato, l'altro potrebbe funzionare:

1) Fare come in questa domanda: How to truncate an NSString based on the graphical width? cioè basta provare diverse dimensioni fino a quando non si adatta più

2) Creare la cellula, dare il massimo rect e impostalo per adattarlo al suo testo nella cella, quindi chiedilo per la sua dimensione ideale (c'è un metodo che lo fa) quindi ridimensiona nuovamente le celle. Alla fine se ho capito bene il tuo problema.

+0

@uliwitness: grazie, ma devo aver travisato il mio obiettivo. Ho chiarito la domanda: non voglio troncare il testo, voglio scegliere una dimensione del carattere appropriata. –

2

È stato consigliato al di fuori della banda di provare una ricerca binaria di dimensioni appropriate. Questo è un esempio molto limitato di che:

- (NSFont *)labelFontForText: (NSString *)text inRect: (NSRect)rect { 
    CGFloat prevSize = 0.0, guessSize = 16.0, tempSize; 
    NSFont *guessFont = nil; 
    while (fabs(guessSize - prevSize) > 0.125) { 
     guessFont = [NSFont labelFontOfSize: guessSize]; 
     NSSize textSize = [text sizeWithAttributes: 
          [NSDictionary dictionaryWithObject: guessFont 
                 forKey: NSFontAttributeName]]; 
     if (textSize.width > rect.size.width || 
      textSize.height > rect.size.height) { 
      tempSize = guessSize - (guessSize - prevSize)/2.0; 
     } 
     else { 
      tempSize = guessSize + (guessSize - prevSize)/2.0; 
     } 
     prevSize = guessSize; 
     guessSize = tempSize; 
    } 
    return [[guessFont retain] autorelease]; 
} 

Le limitazioni (è meglio non avrebbero bisogno di un 32PT o font più grande, o qualsiasi cosa che non sia Lucida Grande) non sono importanti per il mio bisogno, ma certamente sarebbe mettere fuori alcune persone usando questo metodo. Lascerò la domanda aperta e accetto un approccio più solido.

1

ecco un metodo che non indovina e controlla. A seconda del carattere, potrebbe essere necessario un po 'di padding per evitare un overflow (sizeWithAttributes non scala perfettamente). Boom!

-(float)scaleToAspectFit:(CGSize)source into:(CGSize)into padding:(float)padding 
{ 
    return MIN((into.width-padding)/source.width, (into.height-padding)/source.height); 
} 

-(NSFont*)fontSizedForAreaSize:(NSSize)size withString:(NSString*)string usingFont:(NSFont*)font; 
{ 
    NSFont* sampleFont = [NSFont fontWithDescriptor:font.fontDescriptor size:12.];//use standard size to prevent error accrual 
    CGSize sampleSize = [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:sampleFont, NSFontAttributeName, nil]]; 
    float scale = [self scaleToAspectFit:sampleSize into:size padding:10]; 
    return [NSFont fontWithDescriptor:font.fontDescriptor size:scale * sampleFont.pointSize]; 
} 
0

Nel mio caso ho utilizzare il seguente:

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 
{ 

    //Create attributes 
    NSColor *text_color = nil; 
    NSFont *font = [self font]; 
    NSString *fontName = [font fontName]; 
    double fontSize = [font pointSize]; 

    NSInteger text_size = (int) fontSize; 

    if([self isHighlighted]) 
     text_color = [NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:1]; 
    else 
     text_color = [NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1]; 


    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: 
           [NSFont fontWithName:fontName size:fontSize], NSFontAttributeName, 
           text_color, NSForegroundColorAttributeName, 
           nil]; 


    NSAttributedString * currentText=[[NSAttributedString alloc] initWithString:[self title] attributes: attributes]; 

    NSSize attrSize = [currentText size]; 

    while (attrSize.width > cellFrame.size.width && --text_size > 0) { 


     attributes = [NSDictionary dictionaryWithObjectsAndKeys: 
         [NSFont fontWithName:fontName size:text_size], NSFontAttributeName, 
         text_color, NSForegroundColorAttributeName, 
         nil]; 

     currentText=[[NSAttributedString alloc] initWithString:[self title] attributes: attributes]; 

     attrSize = [currentText size]; 

    } 

    switch ([self alignment]) { 
     default: 
     case NSLeftTextAlignment: 
      [currentText drawAtPoint:NSMakePoint(cellFrame.origin.x, 
               cellFrame.origin.y + (cellFrame.size.height/2) - (attrSize.height/2))]; 
      break; 

     case NSRightTextAlignment: 
      [currentText drawAtPoint:NSMakePoint(cellFrame.origin.x + (cellFrame.size.width) - (attrSize.width), 
               cellFrame.origin.y + (cellFrame.size.height/2) - (attrSize.height/2))]; 
      break; 

     case NSCenterTextAlignment: 
      [currentText drawAtPoint:NSMakePoint(cellFrame.origin.x + (cellFrame.size.width /2) - (attrSize.width/2), 
               cellFrame.origin.y + (cellFrame.size.height/2) - (attrSize.height/2))]; 
      break; 


    } 




} 
0

dispiace: sono passati cinque anni. La larghezza del testo potrebbe non essere più la preoccupazione principale nella tua vita da sveglia. Tuttavia, ho la risposta; forse gli altri ne trarranno beneficio.

La chiave cruciale per l'accurato dimensionamento del testo (e questo funziona anche per l'altezza del testo) consiste nel rendersi conto che la larghezza del testo renderizzato ovviamente varia, ma in modo lineare! - con l'impostazione dell'attributo dimensione carattere. Non è necessario effettuare una ricerca binaria, né selezionare e testare tutti i possibili valori degli attributi di dimensione font, quando uno ha una funzione lineare; bisogna solo essere sicuri di due punti sul grafico.

Per preparare, non disegnare la stringa, ma calcolare la larghezza della stringa di rendering su, ad esempio, la dimensione del testo 20 e con la dimensione del testo 40. Ciò fornisce due punti dati sulla funzione lineare "rendering della larghezza della stringa in funzione dell'attributo dimensione del testo ". Quindi, estrapolare per adattare la stringa a qualsiasi larghezza di rendering attualmente necessaria.

Ho trovato questo metodo per ottenere risultati buoni e rapidi uniformemente. Con le variazioni nel carattere, ovviamente, a volte è possibile ottenere caratteri che pendono due o tre pixel oltre il bordo del riquadro di delimitazione, ma questo è un artefatto del design del font. I font ben progettati funzionano bene, e anche con font pazzi, uno di solito deve solo fornire un margine di margine di pochi pixel.

Ecco le routine che ho utilizzato quando ho avuto questo problema il mese scorso. Sentiti libero di usare questo codice.

/******************************************************************************************/ 

// 
// text.m 
// 

/******************************************************************************************/ 

@interface drawtext : NSObject { 

    // name of the font to be used 
    NSString *fontname; 

    // instantiations of that font, at size 20 and at size 40, and at the currently-best size 
    NSFont *font20, *font40, *font; 

    // first sizing function: rendered string height as a function of the font-size attribute 
    CGFloat mh, bh; 

    // second sizing function: rendered string width as a function of the font-size attribute 
    CGFloat mw, bw; 

} 

@end 

/******************************************************************************************/ 

@implementation drawtext 

/******************************************************************************************/ 

// CLASS METHODS 

/******************************************************************************************/ 

// The caller specifies the text string (all capitals! no descenders!) to be drawn, the 
// name of the font to use, the box in which to draw the text, and a border if desired. 
// 
// The routine returns the fontsize to be used, and the origin to be used for the 
// "drawAtPoint" message. This will result in the largest rendition of the text string 
// which meets the constraints. 

+ (void) sizeText: (NSString *) captext // the string of text to evaluate for font size 
    usingFontName: (NSString *) fontname // the string name of the font to be employed 
      inBox: (NSRect)  box  // the containing box on the screen 
     withBorder: (NSSize)  border // the # of pixels to leave blank as X & Y borders 
    usingFontSize: (CGFloat *) fontsize // (returned) what font-size to use 
     atOrigin: (NSPoint *) origin // (returned) where to execute the drawAtPoint 
{ 
    // let's start by redefining the containing box to presume the borders 

    NSRect newBox; 
    newBox.origin.x = box.origin.x + border.width; 
    newBox.origin.y = box.origin.y + border.height; 
    newBox.size.width = box.size.width - 2.0 * border.width; 
    newBox.size.height = box.size.height - 2.0 * border.height; 

    // find out dimensions at font size = 20, then at font size = 40, to use for extrapolation 

    NSSize s20, s40; 

    NSFont *f20 = [NSFont fontWithName:fontname size:20]; 
    NSMutableAttributedString *mtext20 = [[NSMutableAttributedString alloc] initWithString:captext]; 
    [mtext20 addAttribute:NSFontAttributeName value:f20 range:NSMakeRange(0,[mtext20 length])]; 
    s20.width = mtext20.size.width; 
    s20.height = f20.capHeight; 

    NSFont *f40 = [NSFont fontWithName:fontname size:40]; 
    NSMutableAttributedString *mtext40 = [[NSMutableAttributedString alloc] initWithString:captext]; 
    [mtext40 addAttribute:NSFontAttributeName value:f40 range:NSMakeRange(0,[mtext40 length])]; 
    s40.width = mtext40.size.width; 
    s40.height = f40.capHeight; 

    // hsize is "font size to cause height of rendered string to match box height" 
    // wsize is "font size to cause width of rendered string to match box width" 

    CGFloat x1, x2, y1, y2, m, b, hsize, wsize; 

    // cap height as function of text size, in y = mx + b format 

    x1 = 20; 
    y1 = s20.height; 
    x2 = 40; 
    y2 = s40.height; 
    m = (y2 - y1)/(x2 - x1); 
    b = y1 - (m * x1); 
    hsize = (newBox.size.height - b)/m; 

    // string len as function of text size, y = mx + b format 

    x1 = 20; 
    y1 = s20.width; 
    x2 = 40; 
    y2 = s40.width; 
    m = (y2 - y1)/(x2 - x1); 
    b = y1 - (m * x1); 
    wsize = (newBox.size.width - b)/m; 

    // choose the lesser of the two extrapolated font-sizes to fit the string into the box, 
    // and at the same time, find the origin point at which to render the string 
    // 
    // if (hsize < wsize) { // there will be east-west spaces 
    // else { // there will be north-south spaces 

    *fontsize = fmin(hsize, wsize); 

    NSSize textSize; 

    NSMutableAttributedString *mtext = [[NSMutableAttributedString alloc] initWithString:captext]; 
    NSFont *f = [NSFont fontWithName:fontname size:*fontsize]; 
    [mtext addAttribute:NSFontAttributeName value:f range:NSMakeRange(0,[mtext length])]; 
    textSize.width = mtext.size.width; 
    textSize.height = f.capHeight; 

    // don't forget "descender", as this is an all-caps string (strings with descenders are 
    // left as an extra credit exercise for the reader :) 
    origin->y = newBox.origin.y + f.descender + ((newBox.size.height/2.0) - (textSize.height/2.0)); 
    origin->x = (newBox.origin.x + (newBox.size.width/2.0)) - (textSize.width/2.0); 
} 

/******************************************************************************************/ 

// Like the previous routine, except the font size is specified by the caller (this is 
// employed in the case it is desired that various text strings, in different containing 
// boxes, are to be drawn in the same font size). 

+ (void) placeText: (NSString *) captext // the string of text to evaluate for positioning 
    usingFontName: (NSString *) fontname // the string name of the font to be employed 
      inBox: (NSRect)  box  // the containing box on the screen 
     withBorder: (NSSize)  border // the # of pixels to leave blank as X & Y borders 
    usingFontSize: (CGFloat) fontsize // (passed) what font-size to use 
      atOrigin: (NSPoint *) origin // (returned) where to execute the drawAtPoint 
{ 
    NSRect newBox; 

    newBox.origin.x = box.origin.x + border.width; 
    newBox.origin.y = box.origin.y + border.height; 
    newBox.size.width = box.size.width - 2.0 * border.width; 
    newBox.size.height = box.size.height - 2.0 * border.height; 

    NSSize textSize; 

    NSMutableAttributedString *mtext = [[NSMutableAttributedString alloc] initWithString:captext]; 
    NSFont *f = [NSFont fontWithName:fontname size:fontsize]; 
    [mtext addAttribute:NSFontAttributeName value:f range:NSMakeRange(0,[mtext length])]; 
    textSize.width = mtext.size.width; 
    textSize.height = f.capHeight; 

    // don't forget "descender", as this is an all-caps string 
    origin->y = newBox.origin.y + f.descender + ((newBox.size.height/2.0) - (textSize.height/2.0)); 
    origin->x = (newBox.origin.x + (newBox.size.width/2.0)) - (textSize.width/2.0); 
} 

/******************************************************************************************/ 

// This routine actually draws the text (the previous routines only determine how it 
// should be drawn). 
// 
// The second routine can be used to draw a string with attributes such as color (i.e., 
// attributes which don't affect the size of the rendered string). 

+ (void) drawText: (NSString *) captext // the string of text to be drawn 
    usingFontName: (NSString *) fontname // the string name of the font to be employed 
     andFontSize: (CGFloat)  fontsize // what font-size to use 
     atOrigin: (NSPoint)  origin // where to execute the drawAtPoint 
{ 
    NSMutableAttributedString *mtext = [[NSMutableAttributedString alloc] initWithString:captext]; 
    NSFont *f = [NSFont fontWithName:fontname size:fontsize]; 
    [mtext addAttribute:NSFontAttributeName value:f range:NSMakeRange(0,[mtext length])]; 
    [mtext drawAtPoint:origin]; 
} 

+ (void) drawMText: (NSMutableAttributedString *) captext // the string of Mtext to be drawn 
    usingFontName: (NSString *) fontname // the string name of the font to be employed 
     andFontSize: (CGFloat)  fontsize // what font-size to use 
     atOrigin: (NSPoint)  origin // where to execute the drawAtPoint 
{ 
    NSFont *f = [NSFont fontWithName:fontname size:fontsize]; 
    [captext addAttribute:NSFontAttributeName value:f range:NSMakeRange(0,[captext length])]; 
    [captext drawAtPoint:origin]; 
} 

/******************************************************************************************/ 

// INSTANCE METHODS 

/******************************************************************************************/ 

// When you instantiate the object, you set the font; from this, you can elucidate the 
// first of the two sizing functions: rendered string height as a function of the 
// font-size attribute. The function is stored in the instance variables of the object, 
// in the variables { mh, bh }, to be used with the classic "y(x) = mx + b" format, where: 
// 
//  y is rendered string height 
//  m is mh 
//  x is font size attribute 
//  b is bh 

- (id) initUsingFontName: (NSString *) fname // string name of font to be employed 
{ 
    if (!self) self = [super init]; 

    fontname = [[NSString alloc] initWithString:fname]; 

    font20 = [NSFont fontWithName:fontname size:20]; 
    font40 = [NSFont fontWithName:fontname size:40]; 

    // "cap height as function of text size", in y = mx + b format (mh is m, bh is b) 

    CGFloat x1, x2, y1, y2; 

    x1 = 20; 
    y1 = font20.capHeight; 
    x2 = 40; 
    y2 = font40.capHeight; 

    mh = (y2 - y1)/(x2 - x1); 
    bh = y1 - (mh * x1); 

    return self; 
} 

/******************************************************************************************/ 

// After initializing the object, you size a text string; this stores a second sizing 
// function: rendered string width as a function of the font-size attribute, in { mw, bw }. 

- (void) sizeString: (NSString *) captext // one string of text to evaluate for font size 
{ 
    CGFloat x1, x2, y1, y2; 

    NSMutableAttributedString *mtext = 
    [[NSMutableAttributedString alloc] initWithString:captext]; 

    [mtext addAttribute:NSFontAttributeName 
       value:font20 
       range:NSMakeRange(0,[mtext length])]; 

    x1 = 20; 
    y1 = mtext.size.width; 

    [mtext addAttribute:NSFontAttributeName 
       value:font40 
       range:NSMakeRange(0,[mtext length])]; 

    x2 = 40; 
    y2 = mtext.size.width; 

    // "string width as function of text size", in y = mx + b format (mw is m, bw is b) 

    mw = (y2 - y1)/(x2 - x1); 
    bw = y1 - (mw * x1); 
} 

/******************************************************************************************/ 

// Then to draw the text string in a box, you use this routine, which will draw it at the 
// largest size possible given all the constraints, including the provided box and border. 
// 
// A similar routine is provided following this one, to draw a mutable string which may 
// contain attributes, such as color, which do not affect the size of the rendered string. 

- (void) drawString: (NSString *) captext // string of text to be drawn 
       inBox: (NSRect)  box  // containing box on the screen 
     withBorder: (NSSize)  border // # of pixels to leave blank as X & Y borders 
{ 
    NSRect newBox; 

    newBox.origin.x = box.origin.x + border.width; 
    newBox.origin.y = box.origin.y + border.height; 
    newBox.size.width = box.size.width - 2.0 * border.width; 
    newBox.size.height = box.size.height - 2.0 * border.height; 

    // solve linear sizing functions for text size, and choose the smaller text size 
    // 
    // if (hsize < wsize) there will be east-west spaces 
    // if (wsize < hsize) there will be north-south spaces 

    CGFloat hsize, wsize, fontsize; 

    hsize = (newBox.size.height - bh)/mh; 
    wsize = (newBox.size.width - bw)/mw; 

    fontsize = fmin(hsize, wsize); 

    font = [NSFont fontWithName:fontname size:fontsize]; 

    NSMutableAttributedString *mtext = 
    [[NSMutableAttributedString alloc] initWithString:captext]; 

    [mtext addAttribute:NSFontAttributeName value:font range:NSMakeRange(0,[mtext length])]; 

    // find the origin-point at which to render the given string, 
    // so that the text is centered in the box 

    NSSize textSize; 

    textSize.width = mtext.size.width; 
    textSize.height = font.capHeight; 

    NSPoint origin; 

    origin.y = newBox.origin.y + font.descender + 
       ((newBox.size.height/2.0) - (textSize.height/2.0)); 
    origin.x = (newBox.origin.x + (newBox.size.width/2.0)) - (textSize.width/2.0); 

    [mtext drawAtPoint:origin]; 
} 

/******************************************************************************************/ 

// To draw a mutable text string in a box (a string containing attributes e.g. color, which 
// do not affect the sizing of the rendered string), use this routine. 

- (void) drawMString: (NSMutableAttributedString *) captext // the M-string to be drawn 
       inBox: (NSRect)  box  // containing box on the screen 
      withBorder: (NSSize)  border // # of pixels to leave blank as X & Y borders 
{ 
    NSRect newBox; 

    newBox.origin.x = box.origin.x + border.width; 
    newBox.origin.y = box.origin.y + border.height; 
    newBox.size.width = box.size.width - 2.0 * border.width; 
    newBox.size.height = box.size.height - 2.0 * border.height; 

    // solve linear sizing functions for text size, and choose the smaller text size 
    // 
    // if (hsize < wsize) there will be east-west spaces 
    // if (wsize < hsize) there will be north-south spaces 

    CGFloat hsize, wsize, fontsize; 

    hsize = (newBox.size.height - bh)/mh; 
    wsize = (newBox.size.width - bw)/mw; 

    fontsize = fmin(hsize, wsize); 

    font = [NSFont fontWithName:fontname size:fontsize]; 

    [captext addAttribute:NSFontAttributeName 
        value:font 
        range:NSMakeRange(0,[captext length])]; 

    // find the origin-point at which to render the given string, 
    // so that the text is centered in the box 

    NSSize textSize; 

    textSize.width = captext.size.width; 
    textSize.height = font.capHeight; 

    NSPoint origin; 

    origin.y = newBox.origin.y + font.descender + 
       ((newBox.size.height/2.0) - (textSize.height/2.0)); 
    origin.x = (newBox.origin.x + (newBox.size.width/2.0)) - (textSize.width/2.0); 

    [captext drawAtPoint:origin]; 
} 

/******************************************************************************************/ 

@end 

/******************************************************************************************/ 
Problemi correlati