Hoe krijg ik hoogte voor NSAttributedString met een vaste breedte

Ik wil een tekening maken van NSAttributedStrings in vakken met een vaste breedte, maar ik heb problemen met het berekenen van de juiste hoogte die ze innemen als ze worden getekend. Tot nu toe heb ik het volgende geprobeerd:

  1. Aanroepen van - (NSSize) size, maar de resultaten zijn nutteloos (voor dit doel), omdat ze de breedte geven die de string wenst.

  2. Aanroepen van - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)optionsmet een rect gevormd tot de gewenste breedte en NSStringDrawingUsesLineFragmentOriginin de opties, precies zoals ik in mijn tekening gebruik. De resultaten zijn … moeilijk te begrijpen; zeker niet wat ik zoek. (Zoals op een aantal plaatsen is aangegeven, waaronder ditCocoa-Dev-thread).

  3. Een tijdelijke NSTextView maken en doen:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    Als ik het frame van tmpView opvraag, is de breedte nog steeds zoals gewenst, en de hoogte is vaak correct … totdat ik bij langere strings kom, terwijl het vaak de helft van de vereiste grootte is. (Er lijkt geen maximale grootte te zijn: één frame zal 273,0 hoog zijn (ongeveer 300 te kort), de andere zal 478,0 zijn (slechts 60-achtig te kort)).

Ik zou het op prijs stellen als iemand anders dit voor elkaar heeft gekregen.


Antwoord 1, autoriteit 100%

-[NSAttributedString boundingRectWithSize:options:]

U kunt NSStringDrawingUsesDeviceMetricsspecificeren om unie van alle glyph-grenzen.

In tegenstelling tot -[NSAttributedString size], vertegenwoordigt de geretourneerde NSRectde afmetingen van het gebied dat zou veranderen als de string wordt getekend.

Als @Bryan opmerkingen, boundingRectWithSize:options:is verouderd (niet aanbevolen) in OS X 10.11 en hoger. Dit komt omdat de tekenreeksstijl nu dynamisch is, afhankelijk van de context.

Zie voor OS X 10.11 en hoger Apple’s Teksthoogte berekenendocumentatie voor ontwikkelaars.


Antwoord 2, autoriteit 47%

Het antwoord iste gebruiken
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
maar de rectdie u doorgeeft, moet 0,0 hebben in de dimensie die u onbeperkt wilt hebben (wat, eh, volkomen logisch is). Voorbeeld hier.


Antwoord 3, autoriteit 40%

Ik heb een complexe toegeschreven tekenreeks met meerdere lettertypen en kreeg onjuiste resultaten met een paar van de bovenstaande antwoorden die ik eerst probeerde. Het gebruik van een UITextView gaf me de juiste hoogte, maar was te traag voor mijn gebruik (grootte van verzamelingscellen). Ik heb Swift-code geschreven met dezelfde algemene aanpak als beschreven in de Apple-documentwaarnaar eerder werd verwezen en beschreven door Erik. Dit gaf me correcte resultaten met een snellere uitvoering dan wanneer een UITextView de berekening doet.

private func heightForString(_ str : NSAttributedString, width : CGFloat) -> CGFloat {
    let ts = NSTextStorage(attributedString: str)
    let size = CGSize(width:width, height:CGFloat.greatestFiniteMagnitude)
    let tc = NSTextContainer(size: size)
    tc.lineFragmentPadding = 0.0
    let lm = NSLayoutManager()
    lm.addTextContainer(tc)
    ts.addLayoutManager(lm)
    lm.glyphRange(forBoundingRect: CGRect(origin: .zero, size: size), in: tc)
    let rect = lm.usedRect(for: tc)
    return rect.integral.size.height
}

Antwoord 4, autoriteit 17%

Misschien ben je geïnteresseerd in Jerry Krinock’s geweldige (alleen OS X) NS(Attributed)String+Geometricscategorie, die is ontworpen om allerlei soorten stringmetingen uit te voeren, inclusief wat u zoekt.


Antwoord 5, autoriteit 10%

Op OS X 10.11+ werkt de volgende methode voor mij (van Apple’s Teksthoogte berekenendocument)

- (CGFloat)heightForString:(NSAttributedString *)myString atWidth:(float)myWidth
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:myString];
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:
        NSMakeSize(myWidth, FLT_MAX)];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [layoutManager addTextContainer:textContainer];
    [textStorage addLayoutManager:layoutManager];
    [layoutManager glyphRangeForTextContainer:textContainer];
    return [layoutManager
        usedRectForTextContainer:textContainer].size.height;
}

Antwoord 6, autoriteit 10%

Swift 4.2

let attributedString = self.textView.attributedText
let rect = attributedString?.boundingRect(with: CGSize(width: self.textView.frame.width, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
print("attributedString Height = ",rect?.height)

Antwoord 7, autoriteit 3%

Gebruik de NSAttributedString-methode

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context

De grootte is de beperking van het gebied, de berekende gebiedsbreedte is beperkt tot de opgegeven breedte, terwijl de hoogte flexibel is op basis van deze breedte. Men kan nul opgeven voor context als dat niet beschikbaar is. Gebruik NSStringDrawingUsesLineFragmentOrigin voor opties om een ​​tekstgrootte van meerdere regels te krijgen.


Antwoord 8, autoriteit 3%

Swift 3:

let attributedStringToMeasure = NSAttributedString(string: textView.text, attributes: [
        NSFontAttributeName: UIFont(name: "GothamPro-Light", size: 15)!,
        NSForegroundColorAttributeName: ClickUpConstants.defaultBlackColor
])
let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: widthOfActualTextView, height: 10))
placeholderTextView.attributedText = attributedStringToMeasure
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
height = size.height

Dit antwoord werkt geweldig voor mij, in tegenstelling tot de andere die me onjuiste hoogtes gaven voor grotere snaren.

Als u dit wilt doen met gewone tekst in plaats van toegeschreven tekst, doet u het volgende:

let placeholderTextView = UITextView(frame: CGRect(x: 0, y: 0, width: ClickUpConstants.screenWidth - 30.0, height: 10))
placeholderTextView.text = "Some text"
let size: CGSize = placeholderTextView.sizeThatFits(CGSize(width: widthOfActualTextView, height: CGFloat.greatestFiniteMagnitude))
height = size.height

Antwoord 9, autoriteit 3%

Zoals veel mannen hierboven vermeldden, en gebaseerd op mijn test.

Ik gebruik open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRectop iOS zoals hieronder:

let rect = attributedTitle.boundingRect(with: CGSize(width:200, height:0), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil)

Hier is de 200de vaste breedtezoals je verwacht, hoogte, ik geef het 0omdat ik denk dat het beter is om de API-hoogte te vertellen is onbeperkt.

Optie is hier niet zo belangrijk: ik probeer .usesLineFragmentOriginof .usesLineFragmentOrigin.union(.usesFontLeading)of .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), het geeft hetzelfde resultaat.

En het resultaat wordt verwacht als mijn hoewel.

Bedankt.


Antwoord 10, autoriteit 3%

Ik heb hier gewoon een hoop tijd aan verspild, dus ik geef een extra antwoord om anderen in de toekomst te redden. Het antwoord van Graham is voor 90% correct, maar er ontbreekt één belangrijk stuk:

Om nauwkeurige resultaten te verkrijgen met -boundingRectWithSize:options:moet u de volgende opties doorgeven:

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading

Als je de lineFragmentOrigin één weglaat, krijg je onzin terug; de geretourneerde rect zal één regel hoog zijn en zal in het geheel niet de grootte respecteren die u in de methode doorgeeft.

Waarom dit zo ingewikkeld en zo slecht gedocumenteerd is, is mij een raadsel. Maar daar heb je het. Geef die opties door en het zal perfect werken (tenminste op OS X).


Antwoord 11

Geen enkel antwoord op deze pagina werkte voor mij, noch de oude, oude Objective-C-code van Apple’s documentatie. Wat ik uiteindelijk aan het werk ben gegaan voor een UITextViewis om eerst de eigenschap textof attributedTexterop in te stellen en vervolgens de benodigde grootte als volgt te berekenen:

let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))

Werkt perfect. Booy!


Antwoord 12

Ik heb een helperklasse gevonden om de hoogte en breedte van attributeText te vinden (geteste code)

https://gist.github.com/azimin/aa1a79aefa1cec031152fa63401d2292

Voeg bovenstaand bestand toe aan uw project

Hoe te gebruiken

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")

Other episodes