Centrar el texto verticalmente en una UITextView

Quiero centrar el texto verticalmente dentro de una gran UITextView que llena toda la pantalla, de modo que cuando hay poco texto, por ejemplo, un par de palabras, está centrado por la altura. No se trata de centrar el texto (una propiedad que se puede encontrar en IB) sino de colocar el texto verticalmente en el medio de UITextView si el texto es corto, por lo que no hay áreas en blanco en UITextView . Se puede hacer esto? ¡Gracias por adelantado!

Primero agregue un observador para el valor de la clave UITextView de UITextView cuando se carga la vista:

 - (void) viewDidLoad { [textField addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL]; [super viewDidLoad]; } 

A continuación, agregue este método para ajustar contentOffset cada vez que cambie el valor de contentOffset :

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { UITextView *tv = object; CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; } 

Debido a que UIKit no es compatible con KVO , decidí implementar esto como una subclase de UITextView que se actualiza cada vez que UITextView el UITextView del contentSize .

Es una versión ligeramente modificada de la respuesta de Carlos que establece el contentInset lugar del contentOffset . Además de ser compatible con iOS 9 , también parece tener menos errores en iOS 8.4.

 class VerticallyCenteredTextView: UITextView { override var contentSize: CGSize { didSet { var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0 topCorrection = max(0, topCorrection) contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0) } } } 

Para iOS 9.0.2. tendremos que configurar el contentInset en su lugar. Si KVO el contentOffset, iOS 9.0.2 lo establece en 0 en el último momento, anulando los cambios en contentOffset.

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { UITextView *tv = object; CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); [tv setContentInset:UIEdgeInsetsMake(topCorrect,0,0,0)]; } - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:NO]; [questionTextView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL]; } 

Usé 0,0 y 0 para los recuadros izquierdo, inferior y derecho respectivamente. Asegúrese de calcularlos también para su caso de uso.

Si no desea utilizar KVO, también puede ajustar manualmente el desplazamiento con la exportación de este código a una función como esta:

 -(void)adjustContentSize:(UITextView*)tv{ CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height); CGFloat inset = MAX(0, deadSpace/2.0); tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right); } 

y llamándolo

 -(void)textViewDidChange:(UITextView *)textView{ [self adjustContentSize:textView]; } 

y cada vez que edite el texto en el código. No te olvides de configurar el controlador como el delegado

Swift 3 versión:

 func adjustContentSize(tv: UITextView){ let deadSpace = tv.bounds.size.height - tv.contentSize.height let inset = max(0, deadSpace/2.0) tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right) } func textViewDidChange(_ textView: UITextView) { self.adjustContentSize(tv: textView) } 
 func alignTextVerticalInTextView(textView :UITextView) { let size = textView.sizeThatFits(CGSizeMake(CGRectGetWidth(textView.bounds), CGFloat(MAXFLOAT))) var topoffset = (textView.bounds.size.height - size.height * textView.zoomScale) / 2.0 topoffset = topoffset < 0.0 ? 0.0 : topoffset textView.contentOffset = CGPointMake(0, -topoffset) } 

Swift 3:

 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() textField.frame = self.view.bounds var topCorrect : CGFloat = (self.view.frame.height / 2) - (textField.contentSize.height / 2) topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect textField.contentInset = UIEdgeInsetsMake(topCorrect,0,0,0) } 

También tengo este problema y lo resolví con una UITableViewCell con UITextView . UITableViewCell método en una subclase personalizada UITableViewCell , propiedad statusTextView :

 - (void)centerTextInTextView { CGFloat topCorrect = ([self.statusTextView bounds].size.height - [self.statusTextView contentSize].height * [self.statusTextView zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); self.statusTextView.contentOffset = (CGPoint){ .x = 0, .y = -topCorrect }; 

Y llama a este método en métodos:

 - (void)textViewDidBeginEditing:(UITextView *)textView - (void)textViewDidEndEditing:(UITextView *)textView - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 

Esta solución funcionó para mí sin problemas, puedes intentarlo.

Tengo una vista de texto que estoy usando con autolayout y con la configuración de lineFragmentPadding y textContainerInset en cero. Ninguna de las soluciones anteriores funcionó en mi situación. Sin embargo, esto funciona para mí. Probado con iOS 9

 @interface VerticallyCenteredTextView : UITextView @end @implementation VerticallyCenteredTextView -(void)layoutSubviews{ [self recenter]; } -(void)recenter{ // using self.contentSize doesn't work correctly, have to calculate content size CGSize contentSize = [self sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)]; CGFloat topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0; self.contentOffset = CGPointMake(0, -topCorrection); } @end 

Acabo de crear una vista de texto centrada verticalmente personalizada en Swift 3:

 class VerticallyCenteredTextView: UITextView { override var contentSize: CGSize { didSet { var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0 topCorrection = max(0, topCorrection) contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0) } } } 

ref: http://geek-is-stupid.github.io/blog/2017/03/14/how-to-center-text-vertically-in-a-uitextview/

Puede configurarlo directamente con solo restricciones:

Hay 3 restricciones que agregué para alinear el texto verticalmente y horizontalmente en las restricciones de la siguiente manera:

enter image description here

  1. Haga que la altura sea 0 y agregue restricciones mayores que
  2. Añadir alineado verticalmente a las restricciones principales
  3. Añadir alineación horizontal a las restricciones principales

enter image description here

Aquí hay una extensión UITextView que centra contenido verticalmente:

 extension UITextView { func centerVertically() { let fittingSize = CGSize(width: bounds.width, height: CGFloat.max) let size = sizeThatFits(fittingSize) let topOffset = (bounds.size.height - size.height * zoomScale) / 2 let positiveTopOffset = max(0, topOffset) contentOffset.y = -positiveTopOffset } } 

Agregue a la respuesta de Carlos, solo en caso de que tenga texto en la TV más grande que el tamaño de la TV, no necesita volver a centrar el texto, así que cambie este código:

 tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; 

a esto:

 if ([tv contentSize].height < [tv bounds].size.height) { tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; } 

Solución de diseño automático:

  1. Cree una UIView que actúe como un contenedor para UITextView.
  2. Agregue las siguientes restricciones:
    • TextView: Alinee el espacio delantero a: Contenedor
    • TextView: Alinee el espacio final a: Contenedor
    • TextView: Alinea el centro Y con: Contenedor
    • TextView: altura igual a: contenedor, relación: ≤

Puede probar el código siguiente, no se requiere necesariamente un observador. el observador arroja un error a veces cuando la vista lo desasigna. Puede mantener este código en viewDidLoad, viewWillAppear o en viewDidAppear en cualquier lugar.

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^(void) { UITextView *tv = txtviewDesc; CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; }); }); 

Lo hice así: en primer lugar, incrustó el UITextView en una UIView (esto también debería funcionar para Mac OS). Luego fijé los cuatro lados de la UIView externa a los lados de su contenedor, dándole una forma y tamaño similar o igual al de UITextView. Por lo tanto, tenía un contenedor adecuado para el UITextView. Luego fijé los bordes izquierdo y derecho de UITextView a los lados de la UIView y le di a UITextView una altura. Finalmente, centré el UITextView verticalmente en UIView. Bingo 🙂 ahora el UITextView está centrado verticalmente en el UIView, por lo tanto, el texto dentro del UITextView también está centrado verticalmente.

UITextView + VerticalAlignment.h

 // UITextView+VerticalAlignment.h // (c) The Internet 2015 #import  @interface UITextView (VerticalAlignment) - (void)alignToVerticalCenter; - (void)disableAlignment; @end 

UITextView + VerticalAlignment.m

 #import "UITextView+VerticalAlignment.h" @implementation UITextView (VerticalAlignment) - (void)alignToVerticalCenter { [self addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:NULL]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { UITextView *tv = object; CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0; topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect ); tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect}; } - (void)disableAlignment { [self removeObserver:self forKeyPath:@"contentSize"]; } @end 

Solución para iOS10 en RubyMotion:

 class VerticallyCenteredTextView < UITextView def init super end def layoutSubviews self.recenter end def recenter contentSize = self.sizeThatFits(CGSizeMake(self.bounds.size.width, Float::MAX)) topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0; topCorrection = 0 if topCorrection < 0 self.contentInset = UIEdgeInsetsMake(topCorrection, 0, 0, 0) end end