objective c - ¿Cómo ajusto SizeToFit a UILabel cambiando solo la altura y no el ancho?
objective-c xcode4.5 (9)
[self.Review sizeToFit];
Resultado antes de sizeToFit:
NSStringFromCGRect(self.Review.frame): {{90, 20}, {198, 63}}
Resultado después de sizeToFit:
NSStringFromCGRect(self.Review.frame): {{90, 20}, {181, 45}}
Quiero que el ancho permanezca igual. Solo quiero cambiar la altura. El automask es
(lldb) po self.Review
(UILabel *) $1 = 0x08bb0fe0 <UILabel: 0x8bb0fe0; frame = (90 20; 181 45); text = ''I''m at Mal Taman Anggrek ...''; clipsToBounds = YES; opaque = NO; autoresize = LM+RM+H; userInteractionEnabled = NO; layer = <CALayer: 0x8bb68b0>>
Sé que hay una manera de hacerlo con: ¿Cómo ajustar y hacer que el ancho de una UILabel se ajuste al tamaño del texto?
Las respuestas son extrañas (necesitamos reabastecer la información de la fuente). O no está claro
También necesitará definir un ancho máximo y decirle a su programa qué hacer si sizeToFit le da un ancho mayor que ese máximo.
sizeWithFont
la extraña solución de usar sizeWithFont
. Es extraño porque UILabel ya conoce la fuente en la etiqueta.
En realidad, ¿cómo se comporta sizeToFit de todos modos? ¿Cómo decide si necesitamos UILabel más delgado o más alto?
Respuesta para 2017 ...
c = CGSize (ancho: su Ancho, alto: CGFloat.greatestFiniteMagnitude)
resultado = tamañoThatFits (c) .height
Asi que...
let t = .... your UITextView
let w = .... your known width of the item
let trueHeight = t.sizeThatFits(
CGSize(width: t, height: CGFloat.greatestFiniteMagnitude)).height
Eso es.
Muy a menudo, desea saber la diferencia de altura en comparación con una entrada de una sola línea "normal" .
Esto surge en particular si está creando algún tipo de celdas que no tienen nada que ver con UTableView (por ejemplo, algún tipo de vista de pila personalizada). Debe establecer la altura manualmente en algún punto. En su guión gráfico, contruya como desee, utilizando un texto de ejemplo de cualquier cadena corta (por ejemplo, "A") que, por supuesto, tendrá solo una línea en cualquier situación normal. Entonces haces algo como esto ...
func setTextWithSizeCorrection() { // an affaire iOS
let t = .... your UITextView
let w = .... your known width of the item
let oneLineExample = "A"
let actualText = yourDataSource[row].description.whatever
// get the height as if with only one line...
experimental.text = oneLineExample
let oneLineHeight = t.sizeThatFits(
CGSize(width: w, height: CGFloat.greatestFiniteMagnitude)).height
// get the height with the actual long text...
// (note that usually, you do not want a one-line minimum)
experimental.text = (actualText == "") ? oneLineExample : actualText
let neededHeight = t.sizeThatFits(
CGSize(width: w, height: CGFloat.greatestFiniteMagnitude)).height
experimental.text = actualText
let delta = neededHeight - oneLineHeight
set the height of your cell, or whatever it is(standardHeight + delta)
}
Punto final: una de las cosas realmente estúpidas en iOS es que UITextView tiene una especie de margen extraño dentro de él. Esto significa que no puede simplemente intercambiar entre UITextViews y etiquetas; y causa muchos otros problemas. Apple no ha solucionado este problema desde el momento de la escritura. Solucionar el problema es difícil: el siguiente enfoque generalmente es correcto:
@IBDesignable class UITextViewFixed: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero;
textContainer.lineFragmentPadding = 0;
}
}
El UITextView roto e inutilizable de Apple ...
UITextViewFixed
que es, en la mayoría de los casos, una solución aceptable:
(Amarillo añadido para mayor claridad)
Así es como se hace. Debido a que la etiqueta ya contiene la información de la fuente, incluyéndola en este método, la llamada es trivial.
CGSize size = [label.text sizeWithFont:label.font
constrainedToSize:CGSizeMake(maxWidth, MAXFLOAT)
lineBreakMode:UILineBreakModeWordWrap];
CGRect labelFrame = label.frame;
labelFrame.size.height = size.height;
label.frame = labelFrame;
La versión Swift utiliza el límite más boundingRectWithSize
:
let maxHeight = CGFloat.infinity
let rect = label.attributedText?.boundingRectWithSize(CGSizeMake(maxWidth, maxHeight),
options: .UsesLineFragmentOrigin, context: nil)
var frame = label.frame
frame.size.height = rect.size.height
label.frame = frame
Creo que no debes usar los métodos de size...
UILabel
, UILabel
ya tiene una API para eso, como señala @vatrif (que internamente probablemente usa solo los métodos de NSString
).
Sin embargo, creo que la API de UILabel
podría mejorarse. Por eso creo que una categoría sería la solución más elegante:
// UILabel+Resize.h
@interface UILabel (Resize)
- (void)sizeToFitHeight;
@end
// UILabel+Resize.m
@implementation UILabel (Resize)
- (void)sizeToFitHeight {
CGSize size = [self sizeThatFits:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)];
CGRect frame = self.frame;
frame.size.height = size.height;
self.frame = frame;
}
@end
Solo una cosa: no estoy seguro del nombre apropiado para ese método:
- sizeToFitHeight?
- heightToFit?
Comentarios muy apreciados, gracias!
La API acordada se beneficiaría de esta adición. Haga su vida más fácil y agregue una extensión a la clase UILabel en Swift de la siguiente manera:
extension UILabel {
func sizeToFitHeight() {
let size:CGSize = self.sizeThatFits(CGSizeMake(self.frame.size.width, CGFloat.max))
var frame:CGRect = self.frame
frame.size.height = size.height
self.frame = frame
}
}
Puede obtener el mismo resultado con sizeThatFits.
CGSize size = [label sizeThatFits:CGSizeMake(label.frame.size.width, CGFLOAT_MAX)];
CGRect frame = label.frame;
frame.size.height = size.height;
label.frame = frame;
O, como alternativa, con sizeToFit.
CGRect frame = label.frame;
[label sizeToFit];
frame.size.height = label.frame.size.height;
label.frame = frame;
Swift 3 versión de la respuesta de Mundi :
let maxHeight: CGFloat = 10000
let rect = label.attributedText!.boundingRect(with: CGSize(width: maxWidth, height: maxHeight),
options: .usesLineFragmentOrigin,
context: nil)
var frame = labe.frame
frame.size.height = rect.size.height
label.frame = frame
Una extensión fácil para este problema para Swift 3.
extension UILabel {
func sizeToFitHeight() {
let size: CGSize = self.sizeThatFits(CGSize.init(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
var frame:CGRect = self.frame
frame.size.height = size.height
self.frame = frame
}
}
sizeToFit funciona de maravilla. El único problema es que se basa en el tamaño actual del control.
Por ejemplo, si está reciclando celdas de la vista de tabla con una etiqueta, la etiqueta puede haberse reducido en ancho en una celda anterior y, por lo tanto, la llamada a sizeToFit puede parecer poco confiable.
Simplemente restablezca el ancho original de su control antes de enviar el mensaje sizeToFit.
SOLUCIÓN FIJA PARA SWIFT 3
Use CGFloat.greatestFiniteMagnitude
para maxHeight.
let size = CGSize.init(width: yourLabel.frame.size.width,
height: CGFloat.greatestFiniteMagnitude)
let rect = errorLabel.attributedText?.boundingRect(with: size, options: .usesLineFragmentOrigin, context: nil)
var frame = errorLabel.frame
frame.size.height = (rect?.size.height)!
errorLabel.frame = frame
O UIlabel
extensión UIlabel
y llama al método yourLabel. sizeToFitHeight()
yourLabel. sizeToFitHeight()
extension UILabel {
func sizeToFitHeight() {
let maxHeight : CGFloat = CGFloat.greatestFiniteMagnitude
let size = CGSize.init(width: self.frame.size.width, height: maxHeight)
let rect = self.attributedText?.boundingRect(with: size, options: .usesLineFragmentOrigin, context: nil)
var frame = self.frame
frame.size.height = (rect?.size.height)!
self.frame = frame
}
}