Jak uzyskać wysokość dla NSAttributedString przy stałej szerokości

Chcę zrobić kilka rysunków NSAttributedStrings w polach o stałej szerokości, ale mam problem z obliczeniem odpowiedniej wysokości, którą zajmą podczas rysowania. Do tej pory próbowałem:

  1. Wywołanie - (NSSize) size, ale wyniki są bezużyteczne (do tego celu), ponieważ dadzą dowolną szerokość ciągu.

  2. Wywołanie - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options z rekta w kształcie do żądanej szerokości i NSStringDrawingUsesLineFragmentOrigin w opcjach, dokładnie tak, jak używam w moim rysunku. Wyniki są ... trudne do zrozumienia; na pewno nie tego Szukam. (Jak zaznaczono w wielu miejscach, w tym Ten wątek Cocoa-Dev).

  3. Tworzenie tymczasowego NSTextView i wykonywanie:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    Kiedy odpytywam ramkę tmpView, szerokość jest nadal pożądana, a wysokość jest często poprawna ... dopóki nie dojdę do dłuższych strun, kiedy często jest o połowę mniejszy. (Nie wydaje mi się, że trafiony jest maksymalny rozmiar: jedna klatka będzie wysoka na 273,0 (o 300 za krótka), druga będzie 478.0 (tylko 60-prawie za krótkie)).

Byłbym wdzięczny za wszelkie wskazówki, jeśli ktoś jeszcze się tym zajął.
Author: bonaldi, 2010-02-17

11 answers

-[NSAttributedString boundingRectWithSize:options:]

Możesz określić NSStringDrawingUsesDeviceMetrics, Aby uzyskać Unię wszystkich granic glifów .

W przeciwieństwie do -[NSAttributedString size], zwracany NSRect reprezentuje wymiary obszaru, który zmieniłby się w przypadku narysowania ciągu znaków.

Jako @ Bryan komentuje, boundingRectWithSize:options: jest przestarzały (niezalecany) w OS X 10.11 i nowszych. Dzieje się tak, ponieważ stylizacja ciągów jest teraz dynamiczna w zależności od kontekstu.

Dla OS X 10.11 i nowszych, zobacz dokumentację programisty Apple obliczanie wysokości tekstu .

 31
Author: Graham Miln,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-25 06:26:04

Odpowiedź to Aby użyć
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
ale rect, który podajesz, powinien mieć 0.0 w wymiarze, który chcesz być nieograniczony(co ma sens). Przykład tutaj .

 15
Author: bonaldi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-17 20:34:54

Możesz być zainteresowany Jerry Krinock ' s great (OS X only) NS(Attributed)String+Geometrics kategoria, która jest przeznaczona do wykonywania wszelkiego rodzaju pomiarów ciągów, w tym tego, czego szukasz.

 6
Author: Rob Keniger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-01 09:06:19

Użyj metody NSAttributedString

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

Rozmiar jest ograniczeniem obszaru, obliczona szerokość obszaru jest ograniczona do określonej szerokości, podczas gdy wysokość jest elastyczna na podstawie tej szerokości. Można określić nil Dla kontekstu, jeśli nie jest on dostępny. Aby uzyskać rozmiar tekstu wielowierszowego, użyj opcji NSStringDrawingUsesLineFragmentOrigin.

 2
Author: CodeBrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-14 15:02:41

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

Ta odpowiedź działa świetnie dla mnie, w przeciwieństwie do innych, które dawały mi nieprawidłowe wysokości dla większych strun.

Jeśli chcesz to zrobić z zwykłym tekstem zamiast przypisanego tekstu, wykonaj następujące czynności:

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
 2
Author: Josh O'Connor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-10 23:11:18

Mam złożony przypisany ciąg z wieloma czcionkami i otrzymałem nieprawidłowe wyniki z kilku powyższych odpowiedzi, które wypróbowałem jako pierwszy. Korzystanie z UITextView dało mi prawidłową wysokość, ale było zbyt wolne dla mojego przypadku użycia(rozmiar komórek kolekcji). Napisałem kod swift używając tego samego ogólnego podejścia opisanego w Apple doc , o którym wcześniej wspominał Erik. To dało mi poprawne wyniki z musi szybsze wykonanie niż mając UITextView zrobić obliczenia.

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:CGPoint(x:0,y:0),size:size), in: tc)

    let rect = lm.usedRect(for: tc)
    return rect.size.height
}
 2
Author: Matt R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 02:49:42

Zmarnowałem trochę czasu na to, więc udzielam dodatkowej odpowiedzi, aby uratować innych w przyszłości. Odpowiedź Grahama jest w 90% poprawna, ale brakuje w niej jednego kluczowego elementu:

Aby uzyskać dokładne wyniki z -boundingRectWithSize:options: musisz przejść następujące opcje :

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

Jeśli pominiesz lineFragmentOrigin jeden, otrzymasz nonsens z powrotem; zwrócony rect będzie pojedynczą linią wysoką i w ogóle nie będzie respektować rozmiaru, który PRZEKAZUJESZ do metody.

Dlaczego tak jest skomplikowane i tak słabo udokumentowane to mnie przerasta. Ale masz to. Podaj te opcje i będzie działać idealnie (przynajmniej na OS X).

 1
Author: Bryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-15 02:16:34

Na OS X 10.11+ działa dla mnie następująca metoda (z obliczania wysokości tekstu dokumentu)

- (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;
}
 1
Author: Erik Wegener,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-24 20:31:10

Ani jedna odpowiedź na tej stronie nie zadziałała dla mnie, ani starożytny stary kod Objective-C z dokumentacji Apple. To, co w końcu udało mi się zrobić dla UITextView, to najpierw ustawić na nim swoją właściwość text lub attributedText, a następnie obliczyć potrzebny rozmiar w następujący sposób:

let size = textView.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.max))
Działa idealnie. Booyah!
 0
Author: Patrick Lynch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-21 05:09:23

Jak wielu facetów wspomniało powyżej, i bazować na moim teście.

Używam open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect na iOS jak poniżej:

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

Tutaj 200 czy Stała szerokość jako oczekiwana, Wysokość daję 0 ponieważ myślę, że lepiej powiedzieć, że wysokość API jest nieograniczona .

Opcja nie jest tutaj tak ważna,mam try .usesLineFragmentOrigin lub .usesLineFragmentOrigin.union(.usesFontLeading) lub .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), daje taki sam wynik.

A wynik jest oczekiwany jako mój choć.

Dzięki.
 0
Author: JerryZhou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-24 11:24:45

I found helper class to find height and width of attributedText (Tested code)

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

Dodaj powyższy plik do swojego projektu

Jak stosować

let attribString = AZTextFrameAttributes(attributedString: lbl.attributedText!)
let width : CGFloat = attribString.calculatedTextWidth()
print("width is :: >> \(width)")
 0
Author: Hardik Thakkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-28 09:30:35