2013-12-09 6 views
16

Ich habe etwas Text, den ich über einen NSAttributedString (Code unten) in einen festen Rahmen zeichne. Im Moment bin ich hart um die Textgröße auf 16 zu kodieren. Meine Frage ist, gibt es eine Möglichkeit, die bestmögliche Größe für den Text für den gegebenen Rahmen zu berechnen?Berechne Schriftgröße, um Rahmen zu passen - Kerntext - NSAttributedString - iOS

- (void)drawText:(CGContextRef)contextP startX:(float)x startY:(float) 
y withText:(NSString *)standString 
{ 
    CGContextTranslateCTM(contextP, 0, (bottom-top)*2); 
    CGContextScaleCTM(contextP, 1.0, -1.0); 

    CGRect frameText = CGRectMake(1, 0, (right-left)*2, (bottom-top)*2); 

    NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:standString]; 
    [attrString addAttribute:NSFontAttributeName 
         value:[UIFont fontWithName:@"Helvetica-Bold" size:16.0] 
         range:NSMakeRange(0, attrString.length)]; 

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString)); 
    struct CGPath * p = CGPathCreateMutable(); 
    CGPathAddRect(p, NULL, frameText); 
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL); 

    CTFrameDraw(frame, contextP); 
} 
+0

Diese benutzerdefinierte UILabel verwendet dies. Ich denke, das kann helfen [https://github.com/vigorouscoding/KSLabel](https://github.com/vigorouscoding/KSLabel) –

+0

Ich benutze kein UILabel, wie sie quadratisch sein müssen - das ist Text sein gezeichnet in eine 2D erstellte Quarzform. – GuybrushThreepwood

+0

UILabels können quadratisch sein? – Ash

Antwort

7

Der einzige Weg, stelle ich dies ist möglich sehen kann, ist ein System zu haben, das die Größe Berechnung läuft dann passt die Größe und wiederholt, bis sie die richtige Größe findet .

I.e. Richten Sie einen halbierenden Algorithmus ein, der zwischen bestimmten Größen liegt.

, d. H. Es für Größe 10 laufen lassen. Zu klein. Größe 20. Zu klein. Größe 30. Zu groß. Größe 25. Zu klein. Größe 27. Genau richtig, Größe 27 verwenden.

Sie könnten sogar in Hunderten starten.

Größe 100. Zu groß. Größe 50. etc ...

+0

Danke - ich denke, die Frage ist, wie würde ich wissen, ob es die richtige Größe ist. Du bringst mich zum Nachdenken - vielleicht ist das, was ich machen möchte, nicht möglich. – GuybrushThreepwood

+0

Warum die down vote? – Fogmeister

+0

Nicht von mir .... – GuybrushThreepwood

4

könnten Sie sizeWithFont verwenden:

[myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24] 
constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame 

Aber es ist veraltet in iOS 7, so dass ich empfehlen, wenn mit einer Schnur in UILabel arbeiten:

[string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}]; 

Wenn Sie mit einem arbeiten rect:

CGRect textRect = [text boundingRectWithSize:mySize 
           options:NSStringDrawingUsesLineFragmentOrigin 
           attributes:@{NSFontAttributeName:FONT} 
           context:nil]; 

CGSize size = textRect.size; 
+0

Aber ich arbeite an einem festen Rahmen - Entschuldigung, wenn ich etwas verpasse, aber nicht sicher, wie das hilft? – GuybrushThreepwood

+0

Ich habe meine Antwort bearbeitet, um Ihnen zu helfen – Pull

+0

Wie würde ich die Schriftgröße (Float) aus der CGSize-Größe extrahieren? – GuybrushThreepwood

5

Die derzeit akzeptierte Antwort spricht von einem Algorithmus, aber iOS bietet Berechnungen für ein NSString-Objekt. Ich würde sizeWithAttributes: der NSString Klasse verwenden.

sizeWithAttributes:

Gibt die Begrenzungskastengröße nimmt der Empfänger, wenn sie mit den gegebenen Attributen gezeichnet.

- (CGSize)sizeWithAttributes:(NSDictionary *)attributes 

Quelle: Apple Docs - NSString UIKit Additions Reference

EDIT die Frage falsch interpretiert, so diese Antwort ist abwegig.

+0

das sollte die beste Antwort sein. Ab iOS 7.0 ist dies der beste Weg, um das umgebende Rechteck für eine Zeichenkette herauszufinden. – cmyr

19

Hier ist ein einfaches Stück Code, der die maximale Schriftgröße wird herauszufinden, innerhalb der Grenzen eines passen:

UILabel *label = [[UILabel alloc] initWithFrame:frame]; 
label.text = @"Some text"; 
float largestFontSize = 12; 
while ([label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:largestFontSize]}].width > modifierFrame.size.width) 
{ 
    largestFontSize--; 
} 
label.font = [UIFont systemFontOfSize:largestFontSize]; 
+1

funktioniert super! außer nur für eine Textzeile. Ich frage mich, ob es eine Möglichkeit gibt, Multi-Linie zu tun – skinsfan00atg

+0

Ich weiß, es ist ein altes Posting, aber es kann immer noch einige Menschen heute und morgen helfen. Also gebe ich meine 2 Cent ein: Um die Schriftgröße für mehrere Zeilen zu erhalten, können Sie die Anzahl der Zeilen übergeben, dann können Sie Folgendes tun: ... while ([label.text sizeWithAttributes: @ {NSFontAttributeName: [UIFont systemFontOfSize: largestFontSize]}]. width> modifierFrame.size.width * numberOfLines) – Rexb

1

dies der Code ist dynamisch Schriftgröße zu ändern, indem die Rahmenbreite zu haben, die Logik aus den anderen Antworten verwenden. Die While-Schleife könnte gefährlich sein, also zögere bitte nicht, Verbesserungsvorschläge einzureichen.

float fontSize = 17.0f; //initial font size 
CGSize rect; 
while (1) { 
    fontSize = fontSize+0.1; 
    rect = [watermarkText sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}]; 
    if ((int)rect.width == (int)subtitle1Text.frame.size.width) { 
     break; 
    } 
} 
subtitle1Text.fontSize = fontSize; 
+0

Kann mir jemand erklären, warum es eine Minus-Bewertung bekommen hat? stimmt etwas in diesem Code nicht? – vatti

0

noch einfacher/schneller (aber natürlich ungefähr) Art und Weise wäre dies:

class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat 
    { 
     let area:CGFloat = boundingBox.width * boundingBox.height 
     return sqrt(area/textLength) 
    } 

Wir jedes Zeichen sind unter der Annahme N N Pixel x, so dass wir nur berechnen, wie viele Male N x N geht in die Bounding Box.

0

Hier ist eine Methode, die gut für iOS 9 mit UITextView Objekte funktioniert. Sie müssen es möglicherweise ein wenig für andere Anwendungen twittern.

/*! 
* Find the height of the smallest rectangle that will enclose a string using the given font. 
* 
* @param string   The string to check. 
* @param font    The drawing font. 
* @param width    The width of the drawing area. 
* 
* @return The height of the rectngle enclosing the text. 
*/ 

- (float) heightForText: (NSString *) string font: (UIFont *) font width: (float) width { 
    NSDictionary *fontAttributes = [NSDictionary dictionaryWithObject: font 
                   forKey: NSFontAttributeName]; 
    CGRect rect = [string boundingRectWithSize: CGSizeMake(width, INT_MAX) 
             options: NSStringDrawingUsesLineFragmentOrigin 
            attributes: fontAttributes 
             context: nil]; 
    return rect.size.height; 
} 

/*! 
* Find the largest font size that will allow a block of text to fit in a rectangle of the given size using the system 
* font. 
* 
* The code is tested and optimized for UITextView objects. 
* 
* The font size is determined to ±0.5. Change delta in the code to get more or less precise results. 
* 
* @param string   The string to check. 
* @param size    The size of the bounding rectangle. 
* 
* @return: The font size. 
*/ 

- (float) maximumSystemFontSize: (NSString *) string size: (CGSize) size { 
    // Hack: For UITextView, the last line is clipped. Make sure it's not one we care about. 
    if ([string characterAtIndex: string.length - 1] != '\n') { 
     string = [string stringByAppendingString: @"\n"]; 
    } 
    string = [string stringByAppendingString: @"M\n"]; 

    float maxFontSize = 16.0; 
    float maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width]; 
    while (maxHeight < size.height) { 
     maxFontSize *= 2.0; 
     maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width]; 
    } 

    float minFontSize = maxFontSize/2.0; 
    float minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width]; 
    while (minHeight > size.height) { 
     maxFontSize = minFontSize; 
     minFontSize /= 2.0; 
     maxHeight = minHeight; 
     minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width]; 
    } 

    const float delta = 0.5; 
    while (maxFontSize - minFontSize > delta) { 
     float middleFontSize = (minFontSize + maxFontSize)/2.0; 
     float middleHeight = [self heightForText: string font: [UIFont systemFontOfSize: middleFontSize] width: size.width]; 
     if (middleHeight < size.height) { 
      minFontSize = middleFontSize; 
      minHeight = middleHeight; 
     } else { 
      maxFontSize = middleFontSize; 
      maxHeight = middleHeight; 
     } 
    } 

    return minFontSize; 
} 
1

Ein kleiner Trick hilft Einsatz von sizeWithAttributes: ohne die Notwendigkeit von Iterieren für das richtige Ergebnis zu machen:

NSSize sampleSize = [wordString sizeWithAttributes: 
    @{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }]; 
CGFloat ratio = rect.size.width/sampleSize.width; 
fontSize *= ratio; 

Sicherstellen, dass die fontSize für die Probe groß genug ist, um gute Ergebnisse zu erzielen.

0

Hier ist Code, der genau das tun wird: berechne optimale Schriftgröße innerhalb einiger Grenzen. Diese Probe ist in Zusammenhang mit UITextView Unterklasse, so ist es mit seinen Grenzen als „gegebenen Rahmen“:

func binarySearchOptimalFontSize(min: Int, max: Int) -> Int { 
    let middleSize = (min + max)/2 

    if min > max { 
     return middleSize 
    } 

    let middleFont = UIFont(name: font!.fontName, size: CGFloat(middleSize))! 

    let attributes = [NSFontAttributeName : middleFont] 
    let attributedString = NSAttributedString(string: text, attributes: attributes) 

    let size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude) 
    let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading] 
    let textSize = attributedString.boundingRect(with: size, options: options, context: nil) 

    if textSize.size.equalTo(bounds.size) { 
     return middleSize 
    } else if (textSize.height > bounds.size.height || textSize.width > bounds.size.width) { 
     return binarySearchOptimalFontSize(min: min, max: middleSize - 1) 
    } else { 
     return binarySearchOptimalFontSize(min: middleSize + 1, max: max) 
    } 
} 

Ich hoffe, das hilft.

0

Ich mag den Ansatz von @holtwick, aber fand heraus, dass es manchmal überbewerten würde, was passen würde. Ich habe einen Tweak erstellt, der in meinen Tests gut funktioniert. Tipp: Vergessen Sie nicht, mit wirklich breiten Buchstaben wie "WWW" oder sogar "௵௵௵" zu testen

func idealFontSize(for text: String, font: UIFont, width: CGFloat) -> CGFloat { 
    let baseFontSize = CGFloat(256) 
    let textSize = text.size(attributes: [NSFontAttributeName: font.withSize(baseFontSize)]) 
    let ratio = width/textSize.width 

    let ballparkSize = baseFontSize * ratio 
    let stoppingSize = ballparkSize/CGFloat(2) // We don't want to loop forever, if we've already come down to 50% of the ballpark size give up 
    var idealSize = ballparkSize 
    while (idealSize > stoppingSize && text.size(attributes: [NSFontAttributeName: font.withSize(idealSize)]).width > width) { 
     // We subtract 0.5 because sometimes ballparkSize is an overestimate of a size that will fit 
     idealSize -= 0.5 
    } 

    return idealSize 
} 
Verwandte Themen