UITableViewCell con altezza UITextView in iOS 7?


121

Come posso calcolare l'altezza di un UITableViewCell con un UITextView in esso in iOS 7?

Ho trovato molte risposte a domande simili, ma sizeWithFont:prende parte a ogni soluzione e questo metodo è deprecato!

So di doverlo usare, - (CGFloat)tableView:heightForRowAtIndexPath:ma come faccio a calcolare l'altezza di cui il mio TextView ha bisogno per visualizzare l'intero testo?

Risposte:


428

Prima di tutto, è molto importante notare che c'è una grande differenza tra UITextView e UILabel quando si tratta di come il testo viene reso. Non solo UITextView ha inserti su tutti i bordi, ma anche il layout del testo al suo interno è leggermente diverso.

Pertanto, sizeWithFont:è un brutto modo per utilizzare UITextViews.

Invece esso UITextViewstesso ha una funzione chiamata sizeThatFits:che restituirà la dimensione più piccola necessaria per visualizzare tutti i contenuti UITextViewall'interno di un riquadro di delimitazione, che puoi specificare.

Quanto segue funzionerà allo stesso modo sia per iOS 7 che per le versioni precedenti e al momento non include alcun metodo, che è deprecato.


Soluzione semplice

- (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;
}

Questa funzione prenderà a NSAttributedStringe la larghezza desiderata come a CGFloate restituirà l'altezza necessaria


Soluzione dettagliata

Dato che di recente ho fatto qualcosa di simile, ho pensato di condividere anche alcune soluzioni ai problemi connessi che ho riscontrato. Spero che possa aiutare qualcuno.

Questo è molto più approfondito e coprirà quanto segue:

  • Certamente: impostare l'altezza di un in UITableViewCellbase alla dimensione necessaria per visualizzare l'intero contenuto di un contenutoUITextView
  • Rispondi alle modifiche del testo (e anima le modifiche di altezza della riga)
  • Mantenere il cursore all'interno dell'area visibile e mantenere il primo risponditore UITextViewattivo durante il ridimensionamento UITableViewCelldurante la modifica

Se stai lavorando con una vista tabella statica o hai solo un numero noto di UITextViews, puoi potenzialmente rendere il passaggio 2 molto più semplice.

1. Innanzitutto, sovrascrivi 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. Definisci la funzione che ha calcolato l'altezza necessaria:

Aggiungi un NSMutableDictionary(in questo esempio chiamato textViews) come variabile di istanza alla tua UITableViewControllersottoclasse.

Usa questo dizionario per memorizzare i riferimenti all'individuo in questo UITextViewsmodo:

(e sì, indexPaths sono chiavi valide per i dizionari )

- (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;
}

Questa funzione ora calcolerà l'altezza effettiva:

- (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. Abilita il ridimensionamento durante la modifica

Per le prossime due funzioni, è importante che il delegato di UITextViewssia impostato sul tuo UITableViewController. Se hai bisogno di qualcos'altro come delegato, puoi aggirare il problema effettuando le chiamate pertinenti da lì o utilizzando gli hook NSNotificationCenter appropriati.

- (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. Seguire il cursore durante la modifica

- (void)textViewDidBeginEditing:(UITextView *)textView {
    [self scrollToCursorForTextView:textView];
}

Questo farà UITableViewscorrere fino alla posizione del cursore, se non è all'interno del Rect visibile di 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. Regolare il rettangolo visibile, impostando gli inserti

Durante la modifica, alcune parti UITableViewpotrebbero essere coperte dalla tastiera. Se i riquadri delle visualizzazioni di tabella non vengono regolati, scrollToCursorForTextView:non sarà possibile scorrere fino al cursore, se si trova nella parte inferiore della visualizzazione di tabella.

- (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];
}

E ultima parte:

All'interno della tua visualizzazione è stata caricata, iscriviti alle notifiche per le modifiche alla tastiera tramite 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];
}

Per favore, non arrabbiarti con me, per aver dato questa risposta così a lungo. Sebbene non tutto sia necessario per rispondere alla domanda, credo che ci siano altre persone a cui queste questioni direttamente correlate saranno utili.


AGGIORNARE:

Come ha sottolineato Dave Haupert, ho dimenticato di includere la rectVisiblefunzione:

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

Inoltre ho notato che scrollToCursorForTextView:includeva ancora un riferimento diretto a uno dei TextField nel mio progetto. Se hai un problema con il bodyTextViewnon essere trovato, controlla la versione aggiornata della funzione.


1
Quel codice funziona davvero bene! Ridimensiona tutto! Ma il mio TextView ottiene sempre un'altezza di 30px! Ci sono impostazioni che non posso impostare o c'è qualcosa che non sono autorizzato in UITextView?
MyJBMe

1
Questa soluzione non sembra funzionare su un copia e incolla se il testo è di grandi dimensioni, qualche idea?
Vikings

2
@Tim Bodeit, la tua soluzione funziona, grazie! Ma penso che dovresti notare nel commento che l'assegnazione di attributedTextsenza specificare il carattere, il colore e l'allineamento del testo porta all'impostazione dei valori predefiniti degli attributi NSAttributedString per textView. Nel mio caso, si ottengono diverse altezze della visualizzazione del testo per lo stesso testo.
Alexander

4
Questa è una delle mie risposte Stack Overflow preferite di tutti i tempi - grazie!
Richard Venable

3
@TimBodeit: non sono in grado di farlo funzionare su iOS8. Per favore fatemi sapere come può essere risolto.
Arun Gupta

37

C'è una nuova funzione per sostituire sizeWithFont, che è boundingRectWithSize.

Ho aggiunto la seguente funzione al mio progetto, che utilizza la nuova funzione su iOS7 e quella vecchia su iOS inferiore a 7. Ha fondamentalmente la stessa sintassi di sizeWithFont:

    -(CGSize)text:(NSString*)text sizeWithFont:(UIFont*)font constrainedToSize:(CGSize)size{
        if(IOS_NEWER_OR_EQUAL_TO_7){
            NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                              font, NSFontAttributeName,
                                              nil];

            CGRect frame = [text boundingRectWithSize:size
                                              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                           attributes:attributesDictionary
                                              context:nil];

            return frame.size;
        }else{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
            return [text sizeWithFont:font constrainedToSize:size];
#pragma clang diagnostic pop
        }
    }

Puoi aggiungere quel IOS_NEWER_OR_EQUAL_TO_7 al tuo file prefix.pch nel tuo progetto come:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )

I miei UITextViews continuano a non ridimensionarsi abbastanza bene e diventano scorrevoli quando il testo si estende su 3 righe; pastebin.com/Wh6vmBqh
Martin de Keijzer

La seconda istruzione return genera anche un avviso di deprecazione in XCode.
Martin de Keijzer

Stai anche impostando la dimensione di UItextView sulla dimensione calcolata del testo, in cellForRowAtIndexPath? Inoltre non dovresti preoccuparti dell'avviso nel secondo ritorno in quanto viene utilizzato solo quando l'app viene eseguita su un dispositivo iOS6 in cui la funzione non è deprecata.
manecosta

Potete fornire un semplice esempio di come utilizzare questa funzione?
Zorayr

@manecosta La documentazione di Apple dice che è necessario "fissare" il risultato: in iOS 7 e versioni successive, questo metodo restituisce dimensioni frazionarie (nel componente size del CGRect restituito); per utilizzare una dimensione restituita per ridimensionare le viste, è necessario aumentare il suo valore all'intero più alto più vicino utilizzando la funzione ceil.
HpTerm

9

Se stai usando UITableViewAutomaticDimension ho una soluzione davvero semplice (solo iOS 8). Nel mio caso è una vista tabella statica, ma immagino che potresti adattarla per prototipi dinamici ...

Ho una presa di vincolo per l'altezza della visualizzazione del testo e ho implementato i seguenti metodi come questo:

// Outlets

@property (weak, nonatomic) IBOutlet UITextView *textView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeight;


// Implementation

#pragma mark - Private Methods

- (void)updateTextViewHeight {
    self.textViewHeight.constant = self.textView.contentSize.height + self.textView.contentInset.top + self.textView.contentInset.bottom;
}

#pragma mark - View Controller Overrides

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateTextViewHeight];
}

#pragma mark - TableView Delegate & Datasource

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 80;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

#pragma mark - TextViewDelegate

- (void)textViewDidChange:(UITextView *)textView {
    [self.tableView beginUpdates];
    [self updateTextViewHeight];
    [self.tableView endUpdates];
}

Ma ricorda : la visualizzazione del testo deve essere scorrevole e devi impostare i tuoi vincoli in modo che funzionino per la quota automatica:

  • imposta tutte le viste nella cella in relazione tra loro, con altezze fisse (inclusa l'altezza della vista testo, che cambierai programmaticamente)
  • la vista più in alto ha la spaziatura in alto e la vista più in basso ha la spaziatura in basso rispetto alla vista super;

L'esempio di cella più semplice è:

  • nessun'altra vista nella cella tranne la vista di testo
  • 0 margini attorno a tutti i lati della vista del testo e un vincolo di altezza predefinito per la vista del testo.

1
la visualizzazione del testo NON deve essere scorrevole
Akshit Zaveri

Ricevo sempre la stessa dimensione in updateTextviewHeight. Sembra che la dimensione del contenuto sia sbagliata. Lo scorrimento è disabilitato.
Dvole

5

La risposta di Tim Bodeit è fantastica. Ho utilizzato il codice di Simple Solution per ottenere correttamente l'altezza della visualizzazione del testo e utilizzare tale altezza in heightForRowAtIndexPath. Ma non uso il resto della risposta per ridimensionare la visualizzazione del testo. Invece, scrivo codice per modificare la framevisualizzazione del testo in cellForRowAtIndexPath.

Tutto funziona in iOS 6 e versioni precedenti, ma in iOS 7 il testo nella visualizzazione testo non può essere mostrato completamente anche se la framevisualizzazione testo è effettivamente ridimensionata. (Non sto usando Auto Layout). Dovrebbe essere il motivo per cui in iOS 7 c'è TextKite la posizione del testo è controllata da NSTextContainerin UITextView. Quindi nel mio caso ho bisogno di aggiungere una linea per impostare il someTextViewper farlo funzionare correttamente in iOS 7.

    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        someTextView.textContainer.heightTracksTextView = YES;
    }

Come dice la documentazione, ciò che fa quella proprietà è:

Controlla se il ricevitore regola l'altezza del proprio rettangolo di delimitazione quando viene ridimensionata la visualizzazione del testo. Valore predefinito: NO.

Se lo si lascia con il valore di default, dopo aver ridimensionato il framedi someTextView, la dimensione del textContainernon viene modificata, portando al risultato che il testo può essere visualizzato solo nell'area prima del ridimensionamento.

E forse è necessario impostare il scrollEnabled = NOnel caso ce ne sia più di uno textContainer, in modo che il testo scorrerà da uno textContainerall'altro.


4

Ecco un'altra soluzione che punta alla semplicità e alla prototipazione rapida :

Impostare:

  1. Tavolo con celle prototipo.
  2. Ogni cella contiene dimensioni dinamiche UITextViewcon altri contenuti.
  3. Le cellule prototipo sono associate TableCell.h.
  4. UITableViewè associato a TableViewController.h.

Soluzione:

(1) Aggiungi a TableViewController.m:

 // This is the method that determines the height of each cell.  
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    // I am using a helper method here to get the text at a given cell.
    NSString *text = [self getTextAtIndex:indexPath];

    // Getting the height needed by the dynamic text view.
    CGSize size = [self frameForText:text sizeWithFont:nil constrainedToSize:CGSizeMake(300.f, CGFLOAT_MAX)];

    // Return the size of the current row.
    // 80 is the minimum height! Update accordingly - or else, cells are going to be too thin.
    return size.height + 80; 
}

// Think of this as some utility function that given text, calculates how much 
// space would be needed to fit that text.
- (CGSize)frameForText:(NSString *)text sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
{
    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          font, NSFontAttributeName,
                                          nil];
    CGRect frame = [text boundingRectWithSize:size
                                      options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                   attributes:attributesDictionary
                                      context:nil];

    // This contains both height and width, but we really care about height.
    return frame.size;
}

// Think of this as a source for the text to be rendered in the text view. 
// I used a dictionary to map indexPath to some dynamically fetched text.
- (NSString *) getTextAtIndex: (NSIndexPath *) indexPath
{
    return @"This is stubbed text - update it to return the text of the text view.";
}

(2) Aggiungi a TableCell.m:

// This method will be called when the cell is initialized from the storyboard
// prototype. 
- (void)awakeFromNib
{
    // Assuming TextView here is the text view in the cell. 
    TextView.scrollEnabled = YES;
}

Spiegazione:

Quindi quello che sta succedendo qui è questo: ogni vista di testo è vincolata all'altezza delle celle della tabella da vincoli verticali e orizzontali - ciò significa che quando l'altezza della cella della tabella aumenta, anche la vista del testo aumenta le sue dimensioni. Ho usato una versione modificata del codice di @ manecosta per calcolare l'altezza richiesta di una visualizzazione di testo per adattare il testo specificato in una cella. Quindi questo significa che dato un testo con un numero X di caratteri, frameForText:restituirà una dimensione che avrà una proprietà size.heightche corrisponde all'altezza richiesta dalla vista del testo.

Ora, tutto ciò che resta è l'aggiornamento dell'altezza della cella in modo che corrisponda all'altezza della vista di testo richiesta. E questo si ottiene a heightForRowAtIndexPath:. Come notato nei commenti, poiché size.heightè solo l'altezza per la visualizzazione del testo e non l'intera cella, dovrebbe esserci un po 'di offset aggiunto ad essa. Nel caso dell'esempio, questo valore era 80.


Che cosa significa questo "dream.dream"?
MyJBMe

@MyJBMe mi dispiace che facesse parte del mio progetto - ho aggiornato il codice di conseguenza. dream.dreamera il testo che stavo riproducendo nella visualizzazione testo.
Zorayr

3

Un approccio se stai usando il layout automatico è lasciare che il motore del layout automatico calcoli le dimensioni per te. Questo non è l'approccio più efficiente ma è piuttosto conveniente (e probabilmente il più accurato). Diventa più conveniente con l'aumentare della complessità del layout della cella, ad esempio improvvisamente hai due o più visualizzazioni di testo / campi nella cella.

Ho risposto a una domanda simile con un esempio completo per il dimensionamento delle celle tableview utilizzando il layout automatico, qui:

Come ridimensionare la superview per adattarla a tutte le sottoview con il layout automatico?


1

La soluzione liscia completa è la seguente.

Innanzitutto, abbiamo bisogno della classe cell con un textView

@protocol TextInputTableViewCellDelegate <NSObject>
@optional
- (void)textInputTableViewCellTextWillChange:(TextInputTableViewCell *)cell;
- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell;
@end

@interface TextInputTableViewCell : UITableViewCell
@property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic) NSInteger minLines;
@property (nonatomic) CGFloat lastRelativeFrameOriginY;
@end


#import "TextInputTableViewCell.h"

@interface TextInputTableViewCell () <UITextViewDelegate> {
    NSLayoutConstraint *_heightConstraint;
}
@property (nonatomic) UITextView *textView;
@end

@implementation TextInputTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;

        _textView = [UITextView new];
        _textView.translatesAutoresizingMaskIntoConstraints = NO;
        _textView.delegate = self;
        _textView.scrollEnabled = NO;
        _textView.font = CELL_REG_FONT;
        _textView.textContainer.lineFragmentPadding = 0.0;
        _textView.textContainerInset = UIEdgeInsetsZero;
        [self.contentView addSubview:_textView];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];
        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];

        _heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
                         attribute: NSLayoutAttributeHeight
                         relatedBy: NSLayoutRelationGreaterThanOrEqual
                         toItem: nil
                         attribute: NSLayoutAttributeNotAnAttribute
                         multiplier: 0.0
                         constant: (_textView.font.lineHeight + 15)];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
        [_textView addConstraint:_heightConstraint];
    }
    return self;
}

- (void)prepareForReuse {
    [super prepareForReuse];    
    self.minLines = 1;
}

- (void)setMinLines:(NSInteger)minLines {
    _heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextWillChange:)]) {
        [self.delegate textInputTableViewCellTextWillChange:self];
    }
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {
    if ([self.delegate respondsToSelector:@selector(textInputTableViewCellTextDidChange:)]) {
        [self.delegate textInputTableViewCellTextDidChange:self];
    }
}

Successivamente, lo usiamo nel TableViewController

@interface SomeTableViewController () <TextInputTableViewCellDelegate>
@end

@implementation SomeTableViewController

. . . . . . . . . . . . . . . . . . . .

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    TextInputTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: TextInputTableViewCellIdentifier forIndexPath:indexPath];
    cell.delegate = self;
    cell.minLines = 3;
    . . . . . . . . . .  
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return UITableViewAutomaticDimension;
}

- (void)textInputTableViewCellWillChange:(TextInputTableViewCell *)cell {
    cell.lastRelativeFrameOriginY = cell.frame.origin.y - self.tableView.contentOffset.y;
}

- (void)textInputTableViewCellTextDidChange:(TextInputTableViewCell *)cell {
    NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = cell.frame.origin.y - cell.lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [cell.textView caretRectForPosition:cell.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:cell.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;
    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end
  • Qui minLinesconsente di impostare l'altezza minima per textView (per resistere alla riduzione al minimo dell'altezza da AutoLayout con UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: con lo stesso indexPath avvia il ricalcolo e il re-layout dell'altezza di tableViewCell.

  • performWithoutAnimation: rimuove l'effetto collaterale (offset del contenuto tableView che salta all'inizio di una nuova riga durante la digitazione).

  • È importante preservare relativeFrameOriginY(non contentOffsetY!) Durante l'aggiornamento contentSizedelle celle a causa delle celle prima che la cella corrente possa essere modificata dal calcolo del layout automatico in modo inaspettato. Rimuove i salti visivi sulla sillabazione del sistema durante la digitazione di parole lunghe.

  • Nota che non dovresti impostare la proprietà estimatedRowHeight ! Quanto segue non funziona

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Utilizza solo il metodo tableViewDelegate.

================================================== ========================

Se non ci si preoccupa del legame debole tra tableView e tableViewCell e dell'aggiornamento della geometria del tableView da tableViewCell , è possibile aggiornare la TextInputTableViewCellclasse sopra:

@interface TextInputTableViewCell : UITableViewCell
@property (nonatomic, weak) id<TextInputTableViewCellDelegate> delegate;
@property (nonatomic, weak) UITableView *tableView;
@property (nonatomic, readonly) UITextView *textView;
@property (nonatomic) NSInteger minLines;
@end


#import "TextInputTableViewCell.h"

@interface TextInputTableViewCell () <UITextViewDelegate> {
    NSLayoutConstraint *_heightConstraint;
    CGFloat _lastRelativeFrameOriginY;
}
@property (nonatomic) UITextView *textView;
@end

@implementation TextInputTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;

        _textView = [UITextView new];
        _textView.translatesAutoresizingMaskIntoConstraints = NO;
        _textView.delegate = self;
        _textView.scrollEnabled = NO;
        _textView.font = CELL_REG_FONT;
        _textView.textContainer.lineFragmentPadding = 0.0;
        _textView.textContainerInset = UIEdgeInsetsZero;
        [self.contentView addSubview:_textView];

        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];
        [self.contentView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[view]-|" options:nil metrics:nil views:@{@"view": _textView}]];

        _heightConstraint = [NSLayoutConstraint constraintWithItem: _textView
                         attribute: NSLayoutAttributeHeight
                         relatedBy: NSLayoutRelationGreaterThanOrEqual
                         toItem: nil
                         attribute: NSLayoutAttributeNotAnAttribute
                         multiplier: 0.0
                         constant: (_textView.font.lineHeight + 15)];
        _heightConstraint.priority = UILayoutPriorityRequired - 1;
        [_textView addConstraint:_heightConstraint];
    }
    return self;
}

- (void)prepareForReuse {
    [super prepareForReuse];    
    self.minLines = 1;
    self.tableView = nil;
}

- (void)setMinLines:(NSInteger)minLines {
    _heightConstraint.constant = minLines * _textView.font.lineHeight + 15;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

    _lastRelativeFrameOriginY = self.frame.origin.y - self.tableView.contentOffset.y;
    return YES;
}

- (void)textViewDidChange:(UITextView *)textView {

    NSIndexPath *indexPath = [self.tableView indexPathForCell:self];
    if (indexPath == nil) return;

    [UIView performWithoutAnimation:^{
        [self.tableView moveRowAtIndexPath:indexPath toIndexPath:indexPath];
    }];

    CGFloat contentOffsetY = self.frame.origin.y - _lastRelativeFrameOriginY;
    self.tableView.contentOffset = CGPointMake(self.tableView.contentOffset.x, contentOffsetY);

    CGRect caretRect = [self.textView caretRectForPosition:self.textView.selectedTextRange.start];
    caretRect = [self.tableView convertRect:caretRect fromView:self.textView];

    CGRect visibleRect = self.tableView.bounds;
    visibleRect.origin.y += self.tableView.contentInset.top;
    visibleRect.size.height -= self.tableView.contentInset.top + self.tableView.contentInset.bottom;

    BOOL res = CGRectContainsRect(visibleRect, caretRect);
    if (!res) {
        caretRect.size.height += 5;
        [self.tableView scrollRectToVisible:caretRect animated:NO];
    }
}
@end

1
  1. Metti UILabel dietro al tuo UITextView.
  2. Usa questa risposta: https://stackoverflow.com/a/36054679/6681462 per UILabel che hai creato
  3. Dare loro gli stessi vincoli e caratteri
  4. Imposta lo stesso testo;

L'altezza della tua cella verrà calcolata dal contenuto di UILabel, ma tutto il testo verrà mostrato da TextField.


0
UITextView *txtDescLandscape=[[UITextView alloc] initWithFrame:CGRectMake(2,20,310,2)];

    txtDescLandscape.editable =NO;
    txtDescLandscape.textAlignment =UITextAlignmentLeft;
    [txtDescLandscape setFont:[UIFont fontWithName:@"ArialMT" size:15]];
    txtDescLandscape.text =[objImage valueForKey:@"imgdescription"];
    txtDescLandscape.text =[txtDescLandscape.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    [txtDescLandscape sizeToFit];
    [headerView addSubview:txtDescLandscape];

    CGRect txtViewlandscpframe = txtDescLandscape.frame;
    txtViewlandscpframe.size.height = txtDescLandscape.contentSize.height;
    txtDescLandscape.frame = txtViewlandscpframe;

penso che in questo modo puoi contare l'altezza della visualizzazione del testo e quindi ridimensionare la cella della visualizzazione della tabella in base a quell'altezza in modo da poter mostrare il testo completo sulla cella


0

Versione rapida

func textViewHeightForAttributedText(text: NSAttributedString, andWidth width: CGFloat) -> CGFloat {
    let calculationView = UITextView()
    calculationView.attributedText = text
    let size = calculationView.sizeThatFits(CGSize(width: width, height: CGFloat.max))
    return size.height
}

0

Se si desidera regolare automaticamente UITableViewCelll'altezza in base all'altezza dell'altezza della parte interna UITextView. Vedi la mia risposta qui: https://stackoverflow.com/a/45890087/1245231

La soluzione è abbastanza semplice e dovrebbe funzionare in quanto iOS 7. Assicurarsi che l' Scrolling Enabledopzione è attivata off per la UITextViewparte interna della UITableViewCellnello Storyboard.

Quindi nel viewDidLoad () di UITableViewController imposta tableView.rowHeight = UITableViewAutomaticDimensione tableView.estimatedRowHeight > 0come:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

Questo è tutto. UITableViewCellL'altezza di verrà regolata automaticamente in base UITextViewall'altezza della parte interna .


-2

Per iOS 8 e versioni successive puoi semplicemente usare

your_tablview.estimatedrowheight= minheight tu vuoi

your_tableview.rowheight=UItableviewautomaticDimension
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.