UITextView qui se développe en text en utilisant la layout automatique

J'ai une vue qui est présentée complètement en utilisant la layout automatique par programme. J'ai un UITextView au milieu de la vue avec des éléments au-dessus et en dessous. Tout fonctionne bien, mais je veux pouvoir développer UITextView au fur et à mesure que du text est ajouté. Cela devrait pousser tout en bas pendant qu'il se développe.

Je sais comment faire cela de la manière "ressorts et jambes", mais y a-t-il une manière automatique de faire cela? La seule façon dont je peux penser est en enlevant et en rajoutant la contrainte chaque fois qu'elle doit grandir.

La vue contenant UITextView se verra atsortingbuer sa taille avec setBounds par AutoLayout. Donc, c'est ce que j'ai fait. La vue d'set est initialement définie toutes les autres contraintes comme elles devraient être, et à la fin j'ai mis une contrainte spéciale pour la taille d'UITextView, et je l'ai sauvée dans une variable d'instance.

  _descriptionHeightConstraint = [NSLayoutConstraint constraintWithItem:_descriptionTextView atsortingbute:NSLayoutAtsortingbuteHeight relatedBy:NSLayoutRelationEqual toItem:nil atsortingbute:NSLayoutAtsortingbuteNotAnAtsortingbute multiplier:0.f constant:100]; [self addConstraint:_descriptionHeightConstraint]; 

Dans la méthode setBounds , j'ai ensuite changé la valeur de la constante.

 -(void) setBounds:(CGRect)bounds { [super setBounds:bounds]; _descriptionTextView.frame = bounds; CGSize descriptionSize = _descriptionTextView.contentSize; [_descriptionHeightConstraint setConstant:descriptionSize.height]; [self layoutIfNeeded]; } 

Je suis juste entré dans ce problème, je ne sais pas si je suis hors sujet, mais si vous définissez votre UITextView à "non" scrollable:

 [textView setScrollEnabled:NO]; 

il sera capable de calculer une taille insortingnsèque, permettant ainsi à la layout automatique de déplacer les autres éléments par lui-même, alors c'est à vous de configurer les contraintes correctement.

(sur iOS 7)

UITextView ne fournit pas de insortingnsèqueContentSize, vous devez donc le sous-classr et en fournir un. Pour le faire grandir automatiquement, invalidez le insortingnsicContentSize dans layoutSubviews. Si vous utilisez autre chose que contentInset par défaut (ce que je ne recommand pas), vous devrez peut-être ajuster le calcul insortingnsicContentSize.

 @interface AutoTextView : UITextView @end 

 #import "AutoTextView.h" @implementation AutoTextView - (void) layoutSubviews { [super layoutSubviews]; if (!CGSizeEqualToSize(self.bounds.size, [self insortingnsicContentSize])) { [self invalidateInsortingnsicContentSize]; } } - (CGSize)insortingnsicContentSize { CGSize insortingnsicContentSize = self.contentSize; // iOS 7.0+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) { insortingnsicContentSize.width += (self.textContainerInset.left + self.textContainerInset.right ) / 2.0f; insortingnsicContentSize.height += (self.textContainerInset.top + self.textContainerInset.bottom) / 2.0f; } return insortingnsicContentSize; } @end 

Vous pouvez le faire via le storyboard, il suffit de désactiver "Défilement activé" 🙂

StoryBoard

J'ai trouvé que ce n'est pas tout à fait inhabituel dans les situations où vous pourriez avoir besoin d'isScrollEnabled à true pour permettre une interaction raisonnable de l'interface user. Un cas simple pour cela est lorsque vous souhaitez autoriser une vue de text à expansion automatique tout en limitant sa hauteur maximale à quelque chose de raisonnable dans un UITableView.

Voici une sous-class de UITextView que j'ai trouvé qui permet l'expansion automatique avec la layout automatique, mais que vous pouvez toujours contraindre à une hauteur maximale et qui va gérer si la vue est défilable en fonction de la hauteur. Par défaut, la vue se développera indéfiniment si vous avez configuré vos contraintes de cette façon.

 import UIKit class FlexibleTextView: UITextView { // limit the height of expansion per insortingnsicContentSize var maxHeight: CGFloat = 0.0 private let placeholderTextView: UITextView = { let tv = UITextView() tv.translatesAutoresizingMaskIntoConstraints = false tv.backgroundColor = .clear tv.isScrollEnabled = false tv.textColor = .disabledTextColor tv.isUserInteractionEnabled = false return tv }() var placeholder: Ssortingng? { get { return placeholderTextView.text } set { placeholderTextView.text = newValue } } override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) isScrollEnabled = false autoresizingMask = [.flexibleWidth, .flexibleHeight] NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: Notification.Name.UITextViewTextDidChange, object: self) placeholderTextView.font = font addSubview(placeholderTextView) NSLayoutConstraint.activate([ placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor), placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor), placeholderTextView.topAnchor.constraint(equalTo: topAnchor), placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var text: Ssortingng! { didSet { invalidateInsortingnsicContentSize() placeholderTextView.isHidden = !text.isEmpty } } override var font: UIFont? { didSet { placeholderTextView.font = font invalidateInsortingnsicContentSize() } } override var contentInset: UIEdgeInsets { didSet { placeholderTextView.contentInset = contentInset } } override var insortingnsicContentSize: CGSize { var size = super.insortingnsicContentSize if size.height == UIViewNoInsortingnsicMesortingc { // force layout layoutManager.glyphRange(for: textContainer) size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom } if maxHeight > 0.0 && size.height > maxHeight { size.height = maxHeight if !isScrollEnabled { isScrollEnabled = true } } else if isScrollEnabled { isScrollEnabled = false } return size } @objc private func textDidChange(_ note: Notification) { // needed incase isScrollEnabled is set to true which stops automatically calling invalidateInsortingnsicContentSize() invalidateInsortingnsicContentSize() placeholderTextView.isHidden = !text.isEmpty } } 

En prime, il est possible d'inclure un text d'espace réservé similaire à UILabel.

Vous pouvez également le faire sans sous- UITextView . Jetez un oeil à ma réponse à Comment puis-je dimensionner un UITextView à son contenu sur iOS 7?

Utilisez la valeur de cette expression:

 [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height 

pour mettre à jour la constant de la hauteur de UILayoutConstraint .

Voici une solution pour les personnes qui préfèrent tout faire par layout automatique:

Dans l'inspecteur de taille:

  1. Définissez la priorité de résistance à la compression de contenu verticalement sur 1000.

  2. Diminuez la priorité de hauteur de la contrainte en cliquant sur "Modifier" dans Contraintes. Juste le faire less de 1000.

entrez la description de l'image ici

Dans l'inspecteur des attributes:

  1. Décochez "Défilement activé"

Une chose importante à noter:

Puisque UITextView est une sous-class de UIScrollView, il est soumis à la propriété automaticallyAdjustsScrollViewInsets de UIViewController.

Si vous configurez la layout et que TextView est la première sous-vue dans une hiérarchie UIViewControllers, ses éléments contentInsets seront modifiés si la propriété automaticallyAdjustsScrollViewInsets est vraie, entraînant parfois un comportement inattendu dans la layout automatique.

Par conséquent, si vous rencontrez des problèmes avec la layout automatique et les affichages de text, essayez de définir automaticallyAdjustsScrollViewInsets = false sur le controller de vue ou de déplacer le textView vers l'avant dans la hiérarchie.

BTW, j'ai construit un UITextView en expansion en utilisant une sous-class et la taille du contenu insortingnsèque remplaçant. J'ai découvert un bogue dans UITextView que vous pourriez vouloir étudier dans votre propre implémentation. Voici le problème:

L'affichage du text en expansion augmentera pour s'adapter au text croissant si vous tapez des lettres simples à la fois. Mais si vous collez un tas de text dedans, il ne descendrait pas mais le text défilerait et le text en haut serait hors de vue.

La solution: Remplacer setBounds: dans votre sous-class. Pour une raison inconnue, le collage a fait que la valeur de bounds.origin.y soit non-zee (33 dans chaque cas que j'ai vu). Donc, j'ai surclassé setBounds: pour toujours mettre le bounds.origin.y à zéro. Correction du problème

 Obj C: #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (nonatomic) UITextView *textView; @end #import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize textView; - (void)viewDidLoad{ [super viewDidLoad]; [self.view setBackgroundColor:[UIColor grayColor]]; self.textView = [[UITextView alloc] initWithFrame:CGRectMake(30,10,250,20)]; self.textView.delegate = self; [self.view addSubview:self.textView]; } - (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; } - (void)textViewDidChange:(UITextView *)txtView{ float height = txtView.contentSize.height; [UITextView beginAnimations:nil context:nil]; [UITextView setAnimationDuration:0.5]; CGRect frame = txtView.frame; frame.size.height = height + 10.0; //Give it some padding txtView.frame = frame; [UITextView commitAnimations]; } @end