UITableViewCell z wysokością UITextView w iOS 7?

121

Jak mogę obliczyć wysokość UITableViewCell z UITextView w nim w iOS 7?

Znalazłem wiele odpowiedzi na podobne pytania, ale biorę sizeWithFont:udział w każdym rozwiązaniu i ta metoda jest przestarzała!

Wiem, że muszę użyć, - (CGFloat)tableView:heightForRowAtIndexPath:ale jak obliczyć wysokość, której potrzebuje TextView, aby wyświetlić cały tekst?

MyJBMe
źródło

Odpowiedzi:

428

Przede wszystkim należy zauważyć, że istnieje duża różnica między UITextView i UILabel, jeśli chodzi o sposób renderowania tekstu. UITextView nie tylko ma wstawki na wszystkich krawędziach, ale także układ tekstu w nim jest nieco inny.

Dlatego sizeWithFont:jest to zły sposób, aby przejść do UITextViews.

Zamiast tego UITextViewsama ma funkcję o nazwie, sizeThatFits:która zwraca najmniejszy rozmiar potrzebny do wyświetlenia całej zawartości UITextViewwewnątrz obwiedni, którą możesz określić.

Poniższe działania będą działać tak samo dla iOS 7 i starszych wersji i od tej pory nie obejmują żadnych metod, które są przestarzałe.


Proste rozwiązanie

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

Ta funkcja przyjmie a NSAttributedStringi żądaną szerokość jako a CGFloati zwróci wymaganą wysokość


Szczegółowe rozwiązanie

Ponieważ ostatnio zrobiłem coś podobnego, pomyślałem, że podzielę się również rozwiązaniami dla powiązanych problemów, które napotkałem. Mam nadzieję, że to komuś pomoże.

Jest to o wiele bardziej szczegółowe i obejmuje następujące zagadnienia:

  • Oczywiście: ustawienie wysokości na UITableViewCellpodstawie rozmiaru potrzebnego do wyświetlenia pełnej zawartości zawartegoUITextView
  • Reaguj na zmiany tekstu (i animuj zmiany wysokości wiersza)
  • Utrzymywanie kursora wewnątrz widocznego obszaru i utrzymywanie pierwszej osoby na pozycji UITextViewpodczas zmiany rozmiaru UITableViewCellpodczas edycji

Jeśli pracujesz ze statycznym widokiem tabeli lub masz tylko znaną liczbę UITextViews, możesz potencjalnie znacznie uprościć krok 2.

1. Najpierw nadpisz 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. Zdefiniuj funkcję, która obliczyła potrzebną wysokość:

Dodaj NSMutableDictionary(w tym przykładzie o nazwie textViews) jako zmienną instancji do swojej UITableViewControllerpodklasy.

Użyj tego słownika, aby przechowywać odniesienia do osoby, w następujący UITextViewssposób:

(i tak, indexPaths to prawidłowe klucze do słowników )

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

Ta funkcja obliczy teraz rzeczywistą wysokość:

- (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. Włącz zmianę rozmiaru podczas edycji

W przypadku następnych dwóch funkcji ważne jest, aby delegat UITextViewsbył ustawiony na twoją UITableViewController. Jeśli potrzebujesz czegoś innego jako delegata, możesz to obejść, wykonując z tego miejsca odpowiednie wywołania lub używając odpowiednich punktów zaczepienia NSNotificationCenter.

- (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. Podążaj za kursorem podczas edycji

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

Spowoduje to UITableViewprzewinięcie do pozycji kursora, jeśli nie znajduje się on wewnątrz widocznego Recta 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. Dostosuj widoczny prostokąt, ustawiając wypustki

Podczas edycji niektóre elementy UITableViewmogą być zakryte przez klawiaturę. Jeśli wstawki widoku tabeli nie zostaną dostosowane, scrollToCursorForTextView:nie będzie można przewinąć do kursora, jeśli znajduje się on na dole widoku tabeli.

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

I ostatnia część:

Wewnątrz widoku załadowano, zarejestruj się, aby otrzymywać powiadomienia o zmianach klawiatury poprzez 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];
}

Proszę, nie złość się na mnie za tak długą odpowiedź. Chociaż nie wszystko jest potrzebne, aby odpowiedzieć na to pytanie, uważam, że są inne osoby, którym te bezpośrednio związane kwestie będą pomocne.


AKTUALIZACJA:

Jak zauważył Dave Haupert, zapomniałem dodać rectVisiblefunkcji:

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

Zauważyłem również, że scrollToCursorForTextView:nadal zawierało bezpośrednie odniesienie do jednego z pól TextField w moim projekcie. Jeśli masz problem z bodyTextViewnie znalezieniem, sprawdź zaktualizowaną wersję funkcji.

Tim Bodeit
źródło
1
Ten kod w ogóle działa dobrze! Zmienia rozmiar wszystkiego! Ale mój TextView zawsze ma wysokość 30 pikseli! Czy istnieją ustawienia, których nie mogę ustawić, lub czy jest coś, czego nie mogę w UITextView?
MyJBMe
1
Wydaje się, że to rozwiązanie nie działa przy kopiowaniu i wklejaniu, jeśli tekst jest duży, jakieś pomysły?
Wikingowie
2
@Tim Bodeit, Twoje rozwiązanie działa, dziękuję! Ale myślę, że powinieneś zauważyć w komentarzu, że przypisanie attributedTextbez określania czcionki, koloru i wyrównania tekstu prowadzi do ustawienia domyślnych wartości atrybutów NSAttributedString do textView. W moim przypadku powoduje to uzyskanie różnych wysokości widoku tekstu dla tego samego tekstu.
Alexander
4
To jedna z moich ulubionych odpowiedzi na temat przepełnienia stosu - dzięki!
Richard Venable
3
@TimBodeit: Nie mogę uruchomić tego na iOS8. Daj mi znać, jak można to naprawić.
Arun Gupta,
37

Istnieje nowa funkcja zastępująca sizeWithFont, czyli boundingRectWithSize.

Dodałem następującą funkcję do mojego projektu, która korzysta z nowej funkcji na iOS7 i starej na iOS niższej niż 7. Ma w zasadzie taką samą składnię jak 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
        }
    }

Możesz dodać to IOS_NEWER_OR_EQUAL_TO_7 do swojego pliku prefix.pch w swoim projekcie jako:

#define IOS_NEWER_OR_EQUAL_TO_7 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 7.0 )
manecosta
źródło
Moje UITextViews nadal nie skalują się dobrze i można je przewijać, gdy tekst obejmuje 3 linie; pastebin.com/Wh6vmBqh
Martin de Keijzer
Druga instrukcja return również generuje ostrzeżenie o wycofaniu w XCode.
Martin de Keijzer,
Czy ustawiasz również rozmiar UItextView na obliczony rozmiar tekstu w cellForRowAtIndexPath? Nie powinieneś także martwić się ostrzeżeniem w drugim powrocie, ponieważ jest ono używane tylko wtedy, gdy aplikacja jest uruchomiona na urządzeniu z systemem iOS6, na którym funkcja nie jest przestarzała.
manecosta
Czy możesz podać prosty przykład korzystania z tej funkcji?
Zorayr
@manecosta Dokumentacja firmy Apple mówi, że należy „ustrzec” wynik: W systemie iOS 7 i nowszych ta metoda zwraca rozmiary ułamkowe (w składniku rozmiaru zwróconego CGRect); aby użyć zwróconego rozmiaru do rozmiaru widoków, należy zwiększyć jego wartość do najbliższej większej liczby całkowitej za pomocą funkcji ceil.
HpTerm
9

Jeśli używasz UITableViewAutomaticDimension, mam naprawdę proste (tylko iOS 8) rozwiązanie. W moim przypadku jest to statyczny widok tabeli, ale myślę, że można go dostosować do dynamicznych prototypów ...

Mam wyjście ograniczenia dla wysokości widoku tekstu i zaimplementowałem następujące metody:

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

Ale pamiętaj : widok tekstu musi być przewijalny i musisz ustawić swoje ograniczenia tak, aby działały dla automatycznego wymiaru:

  • ustaw wszystkie widoki w komórce względem siebie, z ustalonymi wysokościami (w tym wysokość widoku tekstu, którą zmienisz programowo)
  • widok najwyższy ma odstępy u góry, a widok najniższy ma odstępy u dołu do superwidoku;

Najbardziej podstawowym przykładem komórki jest:

  • żadnych innych widoków w komórce poza widokiem tekstu
  • 0 marginesów ze wszystkich stron widoku tekstu i predefiniowane ograniczenie wysokości dla widoku tekstu.
dec-vt100
źródło
1
widok tekstu NIE MOŻE być przewijalny
Akshit Zaveri,
Cały czas otrzymuję ten sam rozmiar w ramach updateTextviewHeight. Wygląda na to, że rozmiar zawartości jest nieprawidłowy. Przewijanie jest wyłączone.
Dvole
5

Odpowiedź Tima Bodeita jest świetna. Użyłem kodu Prostego rozwiązania, aby poprawnie uzyskać wysokość widoku tekstu i użyć tej wysokości w heightForRowAtIndexPath. Ale nie używam reszty odpowiedzi do zmiany rozmiaru widoku tekstu. Zamiast tego piszę kod, aby zmienić framewidok tekstu w cellForRowAtIndexPath.

Wszystko działa w iOS 6 i niższych, ale w iOS 7 tekst w widoku tekstu nie może być w pełni pokazany, mimo że framewidok tekstu jest rzeczywiście zmieniony. (Nie używam Auto Layout). To powinno być powodem, dla którego w iOS 7 istnieje TextKiti pozycja tekstu jest kontrolowana przez NSTextContainerw UITextView. Więc w moim przypadku muszę dodać linię, aby ustawić someTextView, aby działał poprawnie w iOS 7.

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

Zgodnie z dokumentacją, to, co robi ta właściwość, to:

Określa, czy odbiornik dostosowuje wysokość swojego prostokąta ograniczającego, gdy zmienia się rozmiar widoku tekstu. Wartość domyślna: NIE.

Jeśli pozostawisz wartość domyślną, po zmianie rozmiaru framez someTextView, rozmiar textContainernie zostanie zmieniony, co spowoduje, że tekst będzie wyświetlany tylko w obszarze przed zmianą rozmiaru.

A może trzeba ustawić na scrollEnabled = NOwypadek, gdyby było ich więcej textContainer, aby tekst przepływał z jednego textContainerdo drugiego.

PowerQian
źródło
4

Oto jeszcze jedno rozwiązanie, które ma na celu prostotę i szybkie prototypowanie :

Ustawiać:

  1. Tabela z prototypowymi komórkami.
  2. Każda komórka zawiera dynamiczną wielkość UITextViewz inną zawartością.
  3. Prototypowe komórki są powiązane z TableCell.h.
  4. UITableViewjest powiązany z TableViewController.h.

Rozwiązanie:

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

Wyjaśnienie:

Więc to, co się tutaj dzieje, jest następujące: każdy widok tekstu jest powiązany z wysokością komórek tabeli przez ograniczenia pionowe i poziome - co oznacza, że ​​gdy wzrasta wysokość komórki tabeli, widok tekstu również zwiększa swój rozmiar. Użyłem zmodyfikowanej wersji kodu @ manecosta, aby obliczyć wymaganą wysokość widoku tekstu, aby zmieścić podany tekst w komórce. Oznacza to, że dany tekst z liczbą X znaków frameForText:zwróci rozmiar, który będzie miał właściwość size.heightodpowiadającą wymaganej wysokości widoku tekstu.

Teraz pozostaje tylko aktualizacja wysokości komórki, aby odpowiadała wymaganej wysokości widoku tekstu. I to osiąga się o godz heightForRowAtIndexPath:. Jak zauważono w komentarzach, ponieważ size.heightjest to tylko wysokość widoku tekstu, a nie cała komórka, należy dodać do niej przesunięcie. W przypadku przykładu ta wartość wynosiła 80.

Zorayr
źródło
Co oznacza ten „sen. Marzenie”?
MyJBMe
@MyJBMe przepraszam, że to część mojego własnego projektu - odpowiednio zaktualizowałem kod. dream.dreambył tekstem, który renderowałem w widoku tekstu.
Zorayr
3

Jedną z metod, jeśli używasz automatycznego układu, jest umożliwienie silnikowi automatycznego układania obliczenia rozmiaru za Ciebie. Nie jest to najbardziej wydajne podejście, ale jest całkiem wygodne (i prawdopodobnie najbardziej dokładne). Staje się to wygodniejsze, gdy rośnie złożoność układu komórki - np. Nagle masz dwa lub więcej widoków tekstu / pól w komórce.

Odpowiedziałem na podobne pytanie z pełną próbką rozmiaru komórek widoku tabeli przy użyciu układu automatycznego, tutaj:

Jak zmienić rozmiar nadzoru, aby dopasować wszystkie podglądy do automatycznego układu?

TomSwift
źródło
1

Całkowicie gładkie rozwiązanie jest następujące.

Najpierw potrzebujemy klasy komórki z tekstem 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];
    }
}

Następnie używamy go w 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
  • Tutaj minLinespozwala ustawić minimalną wysokość dla textView (aby zapobiec minimalizacji wysokości przez AutoLayout z UITableViewAutomaticDimension).

  • moveRowAtIndexPath:indexPath: z tym samym indexPath uruchamia ponowne obliczenie wysokości tableViewCell i zmianę układu.

  • performWithoutAnimation: usuwa efekt uboczny (przesunięcie zawartości tableView przeskakuje przy rozpoczynaniu nowej linii podczas pisania).

  • Ważne jest, aby zachować relativeFrameOriginY(nie contentOffsetY!) Podczas aktualizacji komórki, ponieważ contentSizekomórki, zanim bieżąca komórka może zostać zmieniona przez rachunek autoLayout w nieoczekiwany sposób. Usuwa wizualne skoki przy dzieleniu wyrazów systemowych podczas wpisywania długich słów.

  • Pamiętaj, że nie powinieneś ustawiać właściwości estimatedRowHeight ! Poniższe nie działa

    self.tableView.estimatedRowHeight = UITableViewAutomaticDimension;

    Używaj tylko metody tableViewDelegate.

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

Jeśli ktoś nie ma nic przeciwko słabemu wiązaniu między tableView i tableViewCell i aktualizowaniu geometrii tableView z tableViewCell , można zaktualizować TextInputTableViewCellklasę powyżej:

@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
malex
źródło
1
  1. Umieść UILabel za swoim UITextView.
  2. Użyj tej odpowiedzi: https://stackoverflow.com/a/36054679/6681462 do utworzonego UILabel
  3. Daj im te same ograniczenia i czcionki
  4. Ustaw im ten sam tekst;

Wysokość twojej komórki zostanie obliczona na podstawie zawartości UILabel, ale cały tekst zostanie wyświetlony przez TextField.

ilnur
źródło
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;

Myślę, że w ten sposób możesz policzyć wysokość widoku tekstu, a następnie zmienić rozmiar komórki widoku tabeli zgodnie z tą wysokością, abyś mógł wyświetlić pełny tekst w komórce

D-eptdeveloper
źródło
0

Szybka wersja

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
}
Adam Smaka
źródło
0

Jeśli chcesz automatycznie dopasować UITableViewCellwysokość na podstawie wysokości wewnętrznej UITextViewwysokości. Zobacz moją odpowiedź tutaj: https://stackoverflow.com/a/45890087/1245231

Rozwiązanie jest dość proste i powinno działać od iOS 7. Upewnij się, że Scrolling Enabledopcja jest wyłączona dla UITextViewwnętrza UITableViewCellw StoryBoard.

Następnie w Twoim viewDidLoad () UITableViewControllera ustaw tableView.rowHeight = UITableViewAutomaticDimensioni tableView.estimatedRowHeight > 0takie jak:

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = 44.0
}

Otóż ​​to. UITableViewCellWysokość zostanie automatycznie dostosowana na podstawie wysokości wewnętrznej UITextView.

petrsyn
źródło
-2

W przypadku iOS 8 i nowszych możesz po prostu użyć

your_tablview.estimatedrowheight= minheight chcesz

your_tableview.rowheight=UItableviewautomaticDimension
Piaszczysty
źródło