cocoa nstextview appkit nsattributedstring

cocoa - Cómo obtener la altura para NSAttributedString en un ancho fijo



nstextview appkit (11)

Quiero hacer un dibujo de NSAttributedStrings en cajas de ancho fijo, pero tengo problemas para calcular la altura correcta que tomarán cuando se dibujen. Hasta ahora, lo he intentado:

  1. Llamada - (NSSize) size , pero los resultados son inútiles (para este propósito), ya que darán el ancho que desee la cadena.

  2. Llamar - (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options con un rect en forma del ancho que quiero y NSStringDrawingUsesLineFragmentOrigin en las opciones, exactamente como lo 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. Creando un NSTextView temporal y haciendo:
    [[tmpView textStorage] setAttributedString:aString];
    [tmpView setHorizontallyResizable:NO];
    [tmpView sizeToFit];

    Cuando consulto el marco de tmpView, el ancho sigue siendo el deseado, y la altura suele ser correcta ... hasta que obtengo cadenas más largas, cuando a menudo es la mitad del tamaño requerido. (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.


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

Para obtener resultados precisos con -boundingRectWithSize:options: DEBE pasar las siguientes opciones :

NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesDeviceMetrics|NSStringDrawingUsesFontLeading`

Si omites el lineFragmentOrigin uno, obtendrás un 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 mal documentado está más allá de mí. Pero ahí lo tienes. Pase esas opciones y funcionará perfectamente (al menos en OS X).


Como muchos chicos mencionaron anteriormente, y se basan en mi prueba.

Yo uso open func boundingRect(with size: CGSize, options: NSStringDrawingOptions = [], context: NSStringDrawingContext?) -> CGRect en iOS como este a continuación:

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

Aquí el 200 es el ancho fijo como su altura esperada, le doy 0 ya que creo que es mejor decir que la altura API es ilimitada .

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

Y el resultado se espera como mi pensamiento.

Gracias.


En OS X 10.11+, el siguiente método funciona para mí (del documento Apple''s Calculating Height 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; }


La respuesta es usar
- (void)drawWithRect:(NSRect)rect options:(NSStringDrawingOptions)options
pero el rect que pasas debe tener 0.0 en la dimensión que quieres que sea ilimitado (que, por ejemplo, tiene perfecto sentido). Ejemplo here .


No funcionó ni una sola respuesta en esta página para mí, ni tampoco el viejo y antiguo código Objective-C de la documentación de Apple . Lo que finalmente llegué a trabajar para un UITextView es primero establecer su text o propiedad de text attributedText 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!


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


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 daban alturas incorrectas para cuerdas más grandes.

Si desea hacer esto con 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


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 documento de Apple 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 }


Use el método NSAttributedString

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

El tamaño es la restricción en el área, el ancho del área calculada se restringe al ancho especificado, mientras que la altura es flexible en función de este ancho. 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.


Encontré clase de ayuda para encontrar la altura y el ancho de attributeText (código probado)

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

Agrega el archivo anterior en tu proyecto

Cómo utilizar

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


-[NSAttributedString boundingRectWithSize:options:]

Puede especificar NSStringDrawingUsesDeviceMetrics para obtener la unión de todos los límites de glifos .

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

Como comentarios de @Bryan, boundingRectWithSize:options: está en desuso (no recomendado) en OS X 10.11 y versiones 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 posterior, consulte la documentación del desarrollador de Cálculo de la altura del texto de Apple.