2014-12-16 13 views
6

Ich möchte die Mindestgrenzen für ein Rechteck berechnen, das benötigt wird, um eine Zeichenfolge (mehrere Zeilen) mit einer bestimmten Schriftart zu versehen.String: Mindestgrenzen für die maximale Höhe

Es sollte wie folgt aussehen:

FROM: 

---------------------------------- 
|Sent when the application is about| 
|to move from active to inactive | 
|state.       | 
---------------------------------- 

TO: 

------------------------- 
|Sent when the application| 
|is about to move from | 
|active to inactive state.| 
------------------------- 

Wie Sie sehen können, bleibt die Höhe die gleichen, die Breite ändert sich auf den Minimalwert notwendig.

Zunächst konnte ich boundingRectWithSize (Beschränkung auf maximale Breite) verwenden, um zuerst die erforderliche Mindesthöhe zu erhalten und dann boundingRectWithSize (Beschränkung auf berechnete Höhe) aufzurufen, um die Breite zu erhalten. Dies führt jedoch zu falschen Ergebnissen bei der Berechnung der Breite im zweiten Schritt. Es berücksichtigt nicht die maximale Höhe, sondern berechnet einfach die Breite für eine einzelne Zeile.

Danach fand ich einen Weg, um das richtige Ergebnis zu erhalten, aber diese Ausführung von Code dauert wirklich lange, die es von keinerlei Nutzen für mich macht:

zunächst die dafür benötigte rect für Constraint Breite berechnen:

var objectFrame = Class.sizeOfString(string, font: objectFont, width: Double(width), height: DBL_MAX) 

dann die Breite:

objectFrame.size.width = Class.minWidthForHeight(string, font: objectFont, objectFrame.size.height) 

verwendet:

class func minWidthForHeight(string: NSString, font: UIFont, height: CGFloat) -> CGFloat 
{ 
    let deltaWidth: CGFloat = 5.0 
    let neededHeight: CGFloat = rect.size.height 
    var testingWidth: CGFloat = rect.size.width 

    var done = false 
    while (done == false) 
    { 
     testingWidth -= deltaWidth 

     var newSize = Class.sizeOfString(string, font: font, width: Double(testingWidth), height: DBL_MAX) 

     if (newSize.height > neededHeight) 
     { 
      testingWidth += deltaWidth 
      done = true 
     } 
    } 
    return testingWidth 
} 

class func sizeOfString(string: NSString, font: UIFont, width: Double, height: Double) -> CGRect 
{ 
    return string.boundingRectWithSize(CGSize(width: width, height: height), 
     options: NSStringDrawingOptions.UsesLineFragmentOrigin, 
     attributes: [NSFontAttributeName: font], 
     context: nil) 
} 

Er berechnet schrittweise die Höhe für eine gegebene Breite (- 5,0 Pixel bei jedem neuen Schritt) und prüft, ob die Höhe gleich bleibt. Sobald sich die Höhe ändert, wird die Breite des vorherigen Schritts zurückgegeben. So, jetzt haben wir ein begrenzendes Rechteck, wo die Zeichenfolge für eine bestimmte Schriftart perfekt ohne verschwendeten Platz passt.

Aber wie gesagt, das benötigt eine sehr lange Zeit zu berechnen, besonders wenn es für viele verschiedene Strings gleichzeitig gemacht wird.

Gibt es einen besseren und schnelleren Weg, dies zu tun?

+1

Ich kenne keine Lösung, wie Sie dies mit einem Schritt berechnen, aber vielleicht können Sie Ihre lineare Suche nach der besten Breite durch eine binäre Suche ersetzen. Nimm zwei Werte, wobei der kleinere zu schmal und der größere zu breit ist. Überprüfen Sie die Breite zwischen diesen Werten. Wenn es immer noch gut ist, stellen Sie den größeren Wert auf die getestete Breite ein, wenn es zu klein ist, passen Sie den kleineren Wert an. Wiederholen. Es ist nicht O (1), sondern O (log n) anstelle von O (n). Abhängig davon, wie viele Versuche Sie für jede Zeichenfolge durchführen müssen, könnte dies bereits zu einer Beschleunigung führen. – Nero

+1

Natürlich erfordert dies ziemlich clevere (nicht perfekte) Annahmen für die Anfangswerte, besonders die kleinere, die zu klein sein soll. Je länger der Text, desto größer kann diese Zahl gewählt werden, denke ich. Vielleicht kann die kleinere Zahl anfangs auf "(1 - 1/numberOfLinesWithMaxWidth) * maxWidth" gesetzt werden. – Nero

+0

Das funktioniert ziemlich gut. Es dauert immer noch ziemlich lange, bis diese Zeit berechnet ist, aber diese Zeit wurde deutlich reduziert. – freshking

Antwort

-1

Also dieser Code unten ist mein letzter Ansatz, um in Frage zu stellen. Es ist ziemlich schnell und funktioniert gut für mich. Danke an @Nero, dass Sie mich auf den richtigen Weg gebracht haben.

class func minFrameWidthForHeight(string: NSString, font: UIFont, rect: CGRect) -> CGFloat 
{ 
    if (string.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).count <= 1) 
    { 
     return rect.size.width 
    } 

    var minValue: CGFloat = rect.size.width/2 
    var maxValue: CGFloat = rect.size.width 

    var testingWidth: CGFloat = rect.size.width 
    var lastTestingWidth: CGFloat = testingWidth 
    let neededHeight: CGFloat = rect.size.height 

    var newSize = rect 

    while (newSize.height <= neededHeight) 
    { 
     lastTestingWidth = testingWidth 

     testingWidth = (maxValue + minValue)/2 

     newSize = CalculationHelper.sizeOfString(string, font: font, width: Double(testingWidth), height: DBL_MAX) 

     if (newSize.height <= neededHeight) 
     { 
      maxValue = testingWidth 
     } 
     else 
     { 
      minValue = testingWidth 
     } 
    } 

    return lastTestingWidth 
} 
+0

Dies löst das Problem nicht wirklich, da Benutzer die Berechnungshilfsklasse nicht haben. –

+0

Auch der Algorithmus unten wird definitiv schneller, sowie genauer –

+0

Bitte sehen Sie sich meine ursprüngliche Post an, es gibt die Klasse, auf die Sie sich beziehen. Es ist einfach ein 'BoundingRectWithSize'-Aufruf. Ich werde Ihren Code bald testen. Bis dahin wollte ich nur meinen Ansatz teilen. – freshking