Tout d'abord, il est très important de noter qu'il existe une grande différence entre UITextView et UILabel en ce qui concerne le rendu du texte. Non seulement UITextView a des encarts sur toutes les bordures, mais la disposition du texte à l'intérieur est également légèrement différente.
Par conséquent, sizeWithFont:
c'est une mauvaise façon d'aller pour UITextViews.
Au lieu de cela, UITextView
elle a une fonction appelée sizeThatFits:
qui retournera la plus petite taille nécessaire pour afficher tout le contenu de l' UITextView
intérieur d'une boîte englobante, que vous pouvez spécifier.
Ce qui suit fonctionnera de la même manière pour iOS 7 et les versions antérieures et n'inclut pour le moment aucune méthode obsolète.
Solution simple
- (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width {
UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:text];
CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
Cette fonction prendra a NSAttributedString
et la largeur désirée comme a CGFloat
et retournera la hauteur nécessaire
Solution détaillée
Depuis que j'ai récemment fait quelque chose de similaire, j'ai pensé partager également quelques solutions aux problèmes liés que j'ai rencontrés. J'espère que cela aidera quelqu'un.
Ceci est beaucoup plus détaillé et couvrira les points suivants:
- Bien sûr: définir la hauteur d'un en
UITableViewCell
fonction de la taille nécessaire pour afficher le contenu complet d'un contenuUITextView
- Répondre aux changements de texte (et animer les changements de hauteur de la ligne)
- Garder le curseur à l'intérieur de la zone visible et garder le premier répondant sur le
UITextView
lors du redimensionnement de la UITableViewCell
pendant l'édition
Si vous travaillez avec une vue de table statique ou si vous n'avez qu'un nombre connu de UITextView
s, vous pouvez potentiellement simplifier l'étape 2.
1. Tout d'abord, écrasez heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// check here, if it is one of the cells, that needs to be resized
// to the size of the contained UITextView
if ( )
return [self textViewHeightForRowAtIndexPath:indexPath];
else
// return your normal height here:
return 100.0;
}
2. Définissez la fonction qui a calculé la hauteur nécessaire:
Ajoutez un NSMutableDictionary
(dans cet exemple appelé textViews
) comme variable d'instance à votre UITableViewController
sous - classe.
Utilisez ce dictionnaire pour stocker les références à l'individu UITextViews
comme ceci:
(et oui, indexPaths sont des clés valides pour les dictionnaires )
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Do you cell configuring ...
[textViews setObject:cell.textView forKey:indexPath];
[cell.textView setDelegate: self]; // Needed for step 3
return cell;
}
Cette fonction va maintenant calculer la hauteur réelle:
- (CGFloat)textViewHeightForRowAtIndexPath: (NSIndexPath*)indexPath {
UITextView *calculationView = [textViews objectForKey: indexPath];
CGFloat textViewWidth = calculationView.frame.size.width;
if (!calculationView.attributedText) {
// This will be needed on load, when the text view is not inited yet
calculationView = [[UITextView alloc] init];
calculationView.attributedText = // get the text from your datasource add attributes and insert here
textViewWidth = 290.0; // Insert the width of your UITextViews or include calculations to set it accordingly
}
CGSize size = [calculationView sizeThatFits:CGSizeMake(textViewWidth, FLT_MAX)];
return size.height;
}
3. Activer le redimensionnement lors de la modification
Pour les deux fonctions suivantes, il est important que le délégué de UITextViews
soit défini sur votre UITableViewController
. Si vous avez besoin d'autre chose en tant que délégué, vous pouvez le contourner en effectuant les appels appropriés à partir de là ou en utilisant les hooks NSNotificationCenter appropriés.
- (void)textViewDidChange:(UITextView *)textView {
[self.tableView beginUpdates]; // This will cause an animated update of
[self.tableView endUpdates]; // the height of your UITableViewCell
// If the UITextView is not automatically resized (e.g. through autolayout
// constraints), resize it here
[self scrollToCursorForTextView:textView]; // OPTIONAL: Follow cursor
}
4. Suivez le curseur lors de l'édition
- (void)textViewDidBeginEditing:(UITextView *)textView {
[self scrollToCursorForTextView:textView];
}
Cela fera UITableView
défiler jusqu'à la position du curseur, s'il n'est pas à l'intérieur du Rect visible de l'UITableView:
- (void)scrollToCursorForTextView: (UITextView*)textView {
CGRect cursorRect = [textView caretRectForPosition:textView.selectedTextRange.start];
cursorRect = [self.tableView convertRect:cursorRect fromView:textView];
if (![self rectVisible:cursorRect]) {
cursorRect.size.height += 8; // To add some space underneath the cursor
[self.tableView scrollRectToVisible:cursorRect animated:YES];
}
}
5. Ajustez le rect visible, en définissant des encarts
Lors de l'édition, certaines parties de votre UITableView
peuvent être couvertes par le clavier. Si les insertions de tableview ne sont pas ajustées, scrollToCursorForTextView:
ne pourra pas faire défiler jusqu'à votre curseur, s'il se trouve en bas de la tableview.
- (void)keyboardWillShow:(NSNotification*)aNotification {
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, kbSize.height, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
}
- (void)keyboardWillHide:(NSNotification*)aNotification {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.35];
UIEdgeInsets contentInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0.0, 0.0, 0.0);
self.tableView.contentInset = contentInsets;
self.tableView.scrollIndicatorInsets = contentInsets;
[UIView commitAnimations];
}
Et dernière partie:
À l'intérieur de votre vue s'est chargée, inscrivez-vous aux notifications pour les modifications du clavier via NSNotificationCenter
:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
S'il vous plaît, ne vous fâchez pas contre moi, pour avoir rendu cette réponse si longue. Même si tout n'est pas nécessaire pour répondre à la question, je crois qu'il y a d'autres personnes à qui ces questions directement liées seront utiles.
METTRE À JOUR:
Comme l'a souligné Dave Haupert, j'ai oublié d'inclure la rectVisible
fonction:
- (BOOL)rectVisible: (CGRect)rect {
CGRect visibleRect;
visibleRect.origin = self.tableView.contentOffset;
visibleRect.origin.y += self.tableView.contentInset.top;
visibleRect.size = self.tableView.bounds.size;
visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
return CGRectContainsRect(visibleRect, rect);
}
J'ai également remarqué que cela scrollToCursorForTextView:
incluait toujours une référence directe à l'un des TextFields de mon projet. Si vous rencontrez un problème pour bodyTextView
ne pas être trouvé, vérifiez la version mise à jour de la fonction.