Centre el text verticalmente en una UITextView

Quiero centrar el text verticalmente dentro de un gran UITextView que llena toda la pantalla, para que cuando haya poco text, digamos un par de palabras, esté centrado en altura. No se trata de centrar el text (una propiedad que se puede encontrar en IB), sino de poner el text verticalmente en el medio de UITextView si el text 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 key 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 el contentOffset del contentOffset cada vez que cambie el valor 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 subclass de UITextView que se actualiza cada vez que cambia el contentSize .

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

 class VerticallyCentenetworkingTextView: 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 contenido de inicio en su lugar. Si KVO 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 las inserciones izquierda, derecha e inferior 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 export de este código a una function 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 editas el text en el código. No olvide configurar el controller como el delegado

Versión Swift 3:

 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) } 

Acabo de crear una vista personalizada de text centrado verticalmente en Swift 3:

 class VerticallyCentenetworkingTextView: 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/

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 UITableViewCell con UITextView . UITableViewCell método en una subclass UITableViewCell personalizada, estado de la 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 llame a este método en los methods:

 - (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 text que estoy usando con autolayout y con la configuration de lineFragmentPadding y textContainerInset a cero. Ninguna de las soluciones anteriores funcionó en mi situación. Sin embargo, esto funciona para mí. Probado con iOS 9

 @interface VerticallyCentenetworkingTextView : UITextView @end @implementation VerticallyCentenetworkingTextView -(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 

Aquí hay una extensión UITextView que centra el 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(1, topOffset) contentOffset.y = -positiveTopOffset } } 

Agregue a Carlos la respuesta, solo en caso de que tenga text en la TV más grande que el tamaño de la TV, no necesita un text más reciente, 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 layout automático:

  1. Cree una UIView que actúe como contenedor para UITextView.
  2. Agregue las siguientes restricciones:
    • TextView: alinear el espacio principal a: contenedor
    • TextView: alinea el espacio posterior con: Container
    • TextView: Alinear el centro Y para: Contenedor
    • TextView: altura igual a: contenedor, relación: ≤

Puede probar el código a continuación, no se requiere obligatoriamente ningún observador. El observador arroja un error a veces cuando la vista se desasigna. Puede mantener este código en viewDidLoad, viewWillAppear o 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é UITextView en una UIView (esto debería funcionar también para mac OS). Luego fijé los cuatro lados de la UIView externa a los lados de su contenedor, dándole una forma y un tamaño similar o igual al de UITextView. Por lo tanto, tenía un contenedor adecuado para UITextView. Luego fijé los bordes izquierdo y derecho de UITextView a los lados de UIView, y le di a UITextView una altura. Finalmente, centre el UITextView verticalmente en UIView. Bingo 🙂 ahora UITextView está centrado verticalmente en UIView, por lo tanto, el text dentro de UITextView también está centrado verticalmente.

Estoy escribiendo esta respuesta en context con Swift3

Hay 3 restricciones que agregué para alinear el text vertical y horizontalmente en restricciones como se muestra a continuación:

introduzca la descripción de la imagen aquí

  1. Cree altura 0 y agregue restricciones mayores que
  2. Agregue alignment vertical a las restricciones principales.
  3. Agregar alignment horizontal a las restricciones principales

introduzca la descripción de la imagen aquí

UITextView + VerticalAlignment.h

 // UITextView+VerticalAlignment.h // (c) The Internet 2015 #import <UIKit/UIKit.h> @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 VerticallyCentenetworkingTextView < 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