2010-02-17 15 views
43

Quiero hacer algo de dibujo de NSAttributedStrings en cajas de ancho fijo, pero estoy teniendo problemas para calcular la altura correcta que va a tomar cuando dibujado. Hasta ahora, he intentado:Como llegar a la altura NSAttributedString a un ancho fijo

  1. Calling - (NSSize) size, pero los resultados son inútiles (para este propósito), ya que va a dar todo lo ancho de los deseos de cuerda.

  2. Llamando - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options con una forma recta al ancho que quiero y NSStringDrawingUsesLineFragmentOrigin en las opciones, exactamente como estoy usando en mi dibujo. Los resultados son ... difíciles de entender; ciertamente no es lo que estoy buscando. (Como se señala en varios lugares, incluido this hilo Cocoa-Dev).

  3. Creación de una NSTextView temporal y haciendo:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    Cuando consultará el marco de tmpView, la anchura sigue siendo tan deseada, y la altura es a menudo correcta ... hasta que llego a cadenas más largas, cuando es a menudo la mitad del tamaño que se requiere. (No parece que se alcance un tamaño máximo: un fotograma tendrá 273.0 de alto (alrededor de 300 demasiado corto), el otro será 478.0 (solo 60-ish demasiado corto)).

Agradecería cualquier sugerencia, si alguien más ha logrado esto.

Respuesta

15

La respuesta es utilizar
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
pero el rect se pasa en 0.0 debería tener en la dimensión que desea ser ilimitado (que, er, tiene mucho sentido). Ejemplo here.

+2

Esta respuesta no está actualizada. [La respuesta de Graham] (http://stackoverflow.com/a/2460091/39155) a continuación es el método correcto a partir de 2014. –

6

Puede que le interese la gran categoría de Jerry Krinock (solo OS X) NS(Attributed)String+Geometrics, que está diseñada para realizar todo tipo de medición de cuerdas, incluso lo que está buscando.

+1

Ese código parece ser específico de Mac. No ayudará con el desarrollo de iOS. – Brennan

+3

Tienes razón, la pregunta era sobre 'NSTextView', que es una clase de Mac. Ni siquiera estoy seguro de que iOS tuviera 'NSAttributedString' cuando respondí esta pregunta. –

+1

Entrada tardía, pero solo un aviso: he tenido problemas con NS (atribuido) String + Geometrics subestimando constantemente la altura que requieren las cadenas largas. Hasta ahora, en realidad acabo de cambiar el resultado añadiendo un 25% extra. Ahora estoy buscando una solución nueva y mejor. No creo que NS (Attributed) String + Geometrics funcione bien en 10.9, 10.10 y 10.11. – Bryan

31
-[NSAttributedString boundingRectWithSize:options:] 

Puede especificar NSStringDrawingUsesDeviceMetrics para obtener union of all glyph bounds.

A diferencia de -[NSAttributedString size], la NSRect devuelta representa las dimensiones del área que cambiarían si se extrae la cadena.

comentarios

Como @Bryan, boundingRectWithSize:options: está en desuso (no se recomienda) en OS X 10.11 y posteriores. Esto se debe a que el estilo de las cuerdas ahora es dinámico según el contexto.

Para OS X 10.11 y más tarde, consulte la documentación Calculating Text Height de desarrolladores de Apple.

+0

Parece que si usa 'NSStringDrawingUsesDeviceMetrics' y no hay glifos que esto devolverá' 0'. ¿Hay alguna manera de manejar ambas situaciones? ¿O debe usted saber cómo saber o realizar un seguimiento de si agregó un glifo? (Probado en iOS 8.) – zekel

+0

Tiene sentido que una cadena sin contenido visible devuelva un rect de tamaño cero. ¿Estás viendo un nuevo comportamiento en iOS 8 en comparación con iOS 7? –

+0

Quise decir que si tienes 'NSAttributedString' con texto (y sin glifos) y usas' NSStringDrawingUsesDeviceMetrics' el tamaño es 'CGSizeZero'. Entonces, parece que tienes que saber si hay glifos antes de decidir cómo obtener el tamaño ... – zekel

2

método Uso NSAttributedString

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

El tamaño es la restricción sobre el área, la anchura del área calculada se limita a la anchura especificada mientras que la altura es flexible basado en esta anchura. Uno puede especificar nil para el contexto si eso no está disponible. Para obtener el tamaño de texto multilínea, use NSStringDrawingUsesLineFragmentOrigin para ver las opciones.

1

que acaba de perder un montón de tiempo en esto, así que estoy proporcionando una respuesta adicional a salvar a otros en el futuro.La respuesta de Graham es 90% correcto, pero le falta una pieza clave:

para obtener resultados precisos con -boundingRectWithSize:options:que deben pasar las siguientes opciones:

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading` 

Si se omite el lineFragmentOrigin uno, se le recuperar el sinsentido; el rect devuelto tendrá una sola línea alta y no respetará en absoluto el tamaño que se transfiere al método.

Por qué esto es tan complicado y tan poco documentado me supera. Pero ahí lo tienes. Pase esas opciones y funcionará perfectamente (al menos en OS X).

+0

También: esto supone que tiene un NSAttributedString creado con una fuente determinada y el NSParagraphStyle que está buscando; en mi caso, se trata de un estilo de párrafo alineado por la izquierda, ajuste de palabras y sin división de palabras. No te olvides de crear eso cuando creas tu attributeString. – Bryan

+0

Este método está en desuso en 10.11+ y ya no funciona correctamente; la altura devuelta es demasiado pequeña en El Capitán. – Bryan

1

En OS X 10.11+, el siguiente método funciona para mí (de documento de Apple Calculating Text Height)

- (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; 
} 
0

Ni una sola respuesta en esta página trabajó para mí, ni tampoco que el antiguo código antiguo de Objective-C de Apple's documentation. Lo que finalmente llegué a trabajar para una UITextView está estableciendo por primera vez su text o attributedText propiedad en él y luego calcular el tamaño necesario de esta manera:

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

funciona perfectamente. Booyah!

1

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 

Esta respuesta funciona muy bien para mí, a diferencia de los otros que me estaban dando alturas incorrectas para las cadenas más grandes.

Si desea hacer esto con el texto normal en lugar de texto atribuido, haga lo siguiente:

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 
0

como un montón de chicos mencionados anteriormente, y la base de mi prueba.

utilizo open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect en iOS como esto abajo:

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

Aquí el 200 es el ancho fijo como su espera, altura que le dan 0 ya que creo que es mejor que contar tipo altura API es ilimitado.

La opción no es tan importante aquí, he intentado .usesLineFragmentOrigin o .usesLineFragmentOrigin.union(.usesFontLeading) o .usesLineFragmentOrigin.union(.usesFontLeading).union(.usesDeviceMetrics), da el mismo resultado.

Y el resultado se espera como mi pensamiento.

Gracias.

2

Tengo una cadena compleja atribuida con varias fuentes y obtuve resultados incorrectos con algunas de las respuestas anteriores que probé primero. El uso de una UITextView me dio la altura correcta, pero fue demasiado lenta para mi caso de uso (tamaño de las celdas de recolección). Escribí un código rápido usando el mismo enfoque general descrito en el Apple doc mencionado anteriormente y descrito por Erik. Esto me dio los resultados correctos con una ejecución más rápida que tener una UITextView hacer el cálculo.

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 
} 
+0

trabajado como un encanto – Harish

Cuestiones relacionadas