Bisakah Anda menganimasikan perubahan ketinggian pada UITableViewCell saat dipilih?

393

Saya menggunakan a UITableView di dalam aplikasi iPhone saya, dan saya memiliki daftar orang-orang yang termasuk dalam grup. Saya ingin agar ketika pengguna mengklik orang tertentu (sehingga memilih sel), sel tumbuh tinggi untuk menampilkan beberapa kontrol UI untuk mengedit properti orang itu.

Apakah ini mungkin?

Mark Amery
sumber

Jawaban:

879

Saya menemukan solusi SANGAT SEDERHANA untuk ini sebagai efek samping untuk UITableViewsaya sedang mengerjakan .....

Menyimpan tinggi sel dalam variabel yang melaporkan tinggi asli secara normal melalui tableView: heightForRowAtIndexPath:, lalu ketika Anda ingin menghidupkan perubahan tinggi, cukup ubah nilai variabel dan panggil ini ...

[tableView beginUpdates];
[tableView endUpdates];

Anda akan menemukan itu tidak melakukan isi ulang penuh tetapi cukup bagi UITableViewuntuk tahu itu harus menggambar ulang sel, meraih nilai tinggi baru untuk sel .... dan coba tebak? Ini menganimasikan perubahan untuk Anda. Manis.

Saya memiliki penjelasan yang lebih terperinci dan contoh kode lengkap di blog saya ... Animate UITableView, Perubahan Tinggi Sel

Simon Lee
sumber
6
Ini brilian. Tetapi apakah ada cara kita dapat mengontrol kecepatan animasi tabel?
Josh Kahane
7
Ini bekerja tetapi jika saya membuat sel lebih besar katakanlah dari 44 menjadi 74 dan kemudian membuatnya lebih kecil lagi ke 44 garis pemisah bertindak sangat aneh. Bisakah seseorang mengkonfirmasi ini?
plaetzchen
46
Ini adalah solusi yang aneh, tetapi itulah yang direkomendasikan Apple dalam sesi "Menguasai Table View" WWDC 2010 juga. Saya akan mengajukan laporan bug tentang menambahkan ini ke dokumentasi karena saya baru saja menghabiskan sekitar 2 jam meneliti.
bpapa
5
Saya telah mencoba solusi ini dan kadang-kadang hanya berfungsi, ketika Anda menekan sel ada kemungkinan 50% untuk bekerja. Adakah yang memiliki bug yang sama? Ini karena iOS7?
Joan Cardona
7
Sekarang ini juga ditulis dalam dokumentasi resmi: You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell. developer.apple.com/library/ios/documentation/UIKit/Reference/…
Jaroslav
63

Saya suka jawabannya oleh Simon Lee. Saya sebenarnya tidak mencoba metode itu tetapi sepertinya itu akan mengubah ukuran semua sel dalam daftar. Saya berharap untuk perubahan sel yang disadap. Saya agak melakukannya seperti Simon tetapi dengan sedikit perbedaan. Ini akan mengubah tampilan sel ketika dipilih. Dan itu memang bernyawa. Hanya cara lain untuk melakukannya.

Buat int untuk menyimpan nilai untuk indeks sel yang dipilih saat ini:

int currentSelection;

Kemudian:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    int row = [indexPath row];
    selectedNumber = row;
    [tableView beginUpdates];
    [tableView endUpdates];
}

Kemudian:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    if ([indexPath row] == currentSelection) {
        return  80;
    }
    else return 40;


}

Saya yakin Anda dapat membuat perubahan serupa di tableView: cellForRowAtIndexPath: untuk mengubah jenis sel atau bahkan memuat file xib untuk sel.

Seperti ini, pemilihan saat ini akan mulai dari 0. Anda perlu melakukan penyesuaian jika Anda tidak ingin sel pertama dari daftar (pada indeks 0) terlihat dipilih secara default.

Mark A.
sumber
2
Periksa kode yang dilampirkan pada posting saya, saya melakukan ini dengan tepat, menggandakan tinggi sel ketika dipilih. :)
Simon Lee
8
"Aku tidak benar-benar mencoba metode itu tetapi sepertinya itu akan mengubah ukuran semua sel dalam daftar" - maka kamu tidak terlihat terlalu keras.
jamie
13
Pilihan saat ini sudah disimpan di tableView.indexPathForSelectedRow.
Nick
22

Tambahkan properti untuk melacak sel yang dipilih

@property (nonatomic) int currentSelection;

Setel ke nilai sentinel di (misalnya) viewDidLoad , untuk memastikan bahwa UITableViewdimulai pada posisi 'normal'

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //sentinel
    self.currentSelection = -1;
}

Di heightForRowAtIndexPath Anda dapat mengatur ketinggian yang Anda inginkan untuk sel yang dipilih

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    int rowHeight;
    if ([indexPath row] == self.currentSelection) {
        rowHeight = self.newCellHeight;
    } else rowHeight = 57.0f;
    return rowHeight;
}

Di didSelectRowAtIndexPath Anda menyimpan pilihan saat ini dan menyimpan ketinggian dinamis, jika diperlukan

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // do things with your cell here

        // set selection
        self.currentSelection = indexPath.row;
        // save height for full text label
        self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}

Dalam didDeselectRowAtIndexPathmengatur indeks seleksi kembali ke nilai sentinel dan menghidupkan sel kembali ke bentuk normal

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {       
        // do things with your cell here

        // sentinel
        self.currentSelection = -1;

        // animate
        [tableView beginUpdates];
        [tableView endUpdates];
    }
}
Kegembiraan
sumber
Terima kasih terima kasih terima kasih! Saya menambahkan sedikit kode untuk membuat sel dapat diaktifkan. Saya menambahkan kode di bawah ini.
Septronic
14

reloadData tidak bagus karena tidak ada animasi ...

Inilah yang saya coba saat ini:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

Hampir berfungsi dengan benar. Hampir. Saya meningkatkan ketinggian sel, dan kadang-kadang ada sedikit "cegukan" pada tampilan tabel saat sel diganti, seolah-olah beberapa posisi bergulir dalam tampilan tabel dipertahankan, sel baru (yang merupakan sel pertama di tabel) berakhir dengan offset-nya terlalu tinggi, dan scrollview memantul untuk memposisikannya kembali.

lawrence
sumber
Secara pribadi saya menemukan bahwa menggunakan metode ini tetapi dengan UITableViewRowAnimationNone memberikan hasil yang lebih halus tetapi masih belum sempurna.
Ron Srebro
11

Saya tidak tahu apa semua hal ini tentang memanggil beginUpdates / endUpdates berturut-turut adalah, Anda bisa menggunakannya -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Ini adalah contoh proyek .

axiixc
sumber
Luar biasa ini tidak meregangkan tampilan teks saya di sel tata letak otomatis saya. Tetapi memang harus memiliki flicker ke animasi karena opsi Tidak ada animasi terlihat berkilau ketika memperbarui ukuran sel.
h3dkandi
10

Saya memutuskan dengan reloadRowsAtIndexPaths .

Saya menyimpan di didSelectRowAtIndexPathindexPath sel yang dipilih dan meneleponreloadRowsAtIndexPaths di akhir (Anda dapat mengirim NSMutableArray untuk daftar elemen yang ingin Anda muat ulang).

Di heightForRowAtIndexPath Anda dapat memeriksa apakah indexPath ada dalam daftar atau tidak dari selIndexPath dan kirim tinggi.

Anda dapat memeriksa contoh dasar ini: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Ini adalah solusi sederhana.

saya menambahkan semacam kode jika membantu Anda

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
    if ([indexPath isEqual:_expandIndexPath])
        return 80;

    return 40;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Celda";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell.textLabel setText:@"wopwop"];

    return cell;
}

#pragma mark -
#pragma mark Tableview Delegate Methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *modifiedRows = [NSMutableArray array];
    // Deselect cell
    [tableView deselectRowAtIndexPath:indexPath animated:TRUE];
    _expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];

    // This will animate updating the row sizes
    [tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}
fermin
sumber
3

Coba ini untuk meluaskan baris berurutan:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
    return 100;

return 44;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
    [modifiedRows addObject:self.expandIndexPath];
    self.expandIndexPath = nil;
} else {
    if (self.expandedIndexPath)
        [modifiedRows addObject:self.expandIndexPath];

    self.expandIndexPath = indexPath;
    [modifiedRows addObject:indexPath];
}

// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];

// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
    cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];

return cell;
}
Nagarjun
sumber
3
BOOL flag;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    flag = !flag;
    [tableView beginUpdates];
    [tableView reloadRowsAtIndexPaths:@[indexPath] 
                     withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView endUpdates];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return YES == flag ? 20 : 40;
}
Roman Solodyashkin
sumber
2

hanya sebuah catatan untuk seseorang seperti saya yang mencari tambah "Detail Selengkapnya" di sel khusus.

[tableView beginUpdates];
[tableView endUpdates];

Melakukan pekerjaan yang sangat baik, tetapi jangan lupa untuk "memotong" tampilan sel. Dari Interface Builder pilih Cell Anda -> Content View -> dari Property Inspector pilih " Clip subview "

Anthony Marchenko
sumber
2

Inilah versi yang lebih pendek dari jawaban Simons untuk Swift 3. Juga memungkinkan untuk beralih pilihan sel

var cellIsSelected: IndexPath?


  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
  }


  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if cellIsSelected == indexPath {
      return 250
    }
    return 65
  }
aBikis
sumber
2

Swift 4 dan Di Atas

tambahkan kode di bawah ini ke dalam metode delegasi baris didelect tableview Anda

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()
midhun p
sumber
1

Versi Swift dari jawaban Simon Lee.

// MARK: - Variables 
  var isCcBccSelected = false // To toggle Bcc.



    // MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

    // Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
    if self.cellTypes[indexPath.row] == CellType.Bcc {
        if (isCcBccSelected) {
            return 44
        } else {
            return 0
        }
    }

    return 44.0
}

Kemudian di didSelectRowAtIndexPath ()

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    self.tableView.deselectRowAtIndexPath(indexPath, animated: true)

    // To Get the Focus of CC, so that we can expand Bcc
    if self.cellTypes[indexPath.row] == CellType.Cc {

        if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {

            if cell.tag == 1 {
                cell.recipientTypeLabel.text = "Cc:"
                cell.recipientTextField.userInteractionEnabled = true
                cell.recipientTextField.becomeFirstResponder()

                isCcBccSelected = true

                tableView.beginUpdates()
                tableView.endUpdates()
            }
        }
    }
}
ioopl
sumber
1

Iya itu mungkin.

UITableView memiliki metode delegasi didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [UIView animateWithDuration:.6
                          delay:0
         usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
          initialSpringVelocity:0
                        options:UIViewAnimationOptionBeginFromCurrentState animations:^{

                            cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
                            NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
                            [violatedTableView beginUpdates];
                            [violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
                            [violatedTableView endUpdates];
                        }
                     completion:^(BOOL finished) {
    }];
}

Tetapi dalam kasus Anda jika pengguna menggulir dan memilih sel yang berbeda maka Anda harus memiliki sel yang dipilih terakhir untuk mengecilkan dan memperluas reloadRowsAtIndexPaths:panggilan sel yang saat ini dipilih heightForRowAtIndexPath:sehingga menangani sesuai.

Koushik
sumber
0

Berikut ini adalah kode UITableViewsubkelas kustom saya , yang diperluas UITextViewdi sel tabel, tanpa memuat ulang (dan kehilangan fokus keyboard):

- (void)textViewDidChange:(UITextView *)textView {
    CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
    // Check, if text height changed
    if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
        [self beginUpdates];

        // Calculate difference in height
        CGFloat difference = textHeight - self.previousTextHeight;

        // Update currently editing cell's height
        CGRect editingCellFrame = self.editingCell.frame;
        editingCellFrame.size.height += difference;
        self.editingCell.frame = editingCellFrame;

        // Update UITableView contentSize
        self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);

        // Scroll to bottom if cell is at the end of the table
        if (self.editingNoteInEndOfTable) {
            self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
        } else {
            // Update all next to editing cells
            NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
            for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
                UITableViewCell *cell = self.visibleCells[i];
                CGRect cellFrame = cell.frame;
                cellFrame.origin.y += difference;
                cell.frame = cellFrame;
            }
        }
        [self endUpdates];
    }
    self.previousTextHeight = textHeight;
}
Vitaliy Gozhenko
sumber
0

Saya menggunakan jawaban luar biasa @ Joy, dan bekerja dengan baik dengan ios 8.4 dan XCode 7.1.1.

Jika Anda ingin membuat sel Anda dapat diaktifkan, saya mengubah -tableViewDidSelect ke berikut ini:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell, 
//cell is expanded. If tapped again on the same cell, 
//cell is collapsed. 
    if (self.currentSelection==indexPath.row) {
        self.currentSelection = -1;
    }else{
        self.currentSelection = indexPath.row;
    }
        // animate
        [tableView beginUpdates];
        [tableView endUpdates];

}

Saya harap semua ini membantu Anda.

Septronic
sumber
0

Periksa metode ini setelah iOS 7 dan yang lebih baru.

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

Perbaikan telah dilakukan untuk ini di iOS 8. Kita dapat mengaturnya sebagai properti dari tampilan tabel itu sendiri.

Govind
sumber
0

Versi cepat dari jawaban Simon Lee :

tableView.beginUpdates()
tableView.endUpdates()

Ingatlah bahwa Anda harus memodifikasi properti ketinggian SEBELUM endUpdates() .

Tamás Sengel
sumber
0

Input -

tableView.beginUpdates () tableView.endUpdates () fungsi-fungsi ini tidak akan memanggil

func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}

Tetapi, jika Anda melakukannya, tableView.reloadRows (at: [selectedIndexPath! As IndexPath], dengan: .none)

Ini akan memanggil func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} fungsi ini.

sRoy
sumber
-1

Saya baru saja menyelesaikan masalah ini dengan sedikit retasan:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;

- (void)onTimer {
    cellHeight++;
    [tableView reloadData];
    if (cellHeight < s_CellHeightEditing)
        heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}

- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
        if (isInEdit) {
            return cellHeight;
        }
        cellHeight = s_CellHeight;
        return s_CellHeight;
}

Ketika saya perlu memperluas tinggi sel saya mengatur isInEdit = YESdan memanggil metode [self onTimer]dan menjiwai pertumbuhan sel sampai mencapai nilai s_CellHeightEditing :-)

Dzamir
sumber
Dalam simulator berfungsi dengan baik, tetapi di iPhone perangkat kerasnya lambat. Dengan keterlambatan waktu 0,05 dan dengan peningkatan tinggi sel 5 unit, ini jauh lebih baik, tetapi tidak seperti CoreAnimation
Dzamir
1
Konfirmasikan, sebelum posting .. waktu berikutnya.
ajay_nasa
-2

Dapatkan jalur indeks dari baris yang dipilih. Muat ulang tabel. Dalam metode heightForRowAtIndexPath dari UITableViewDelegate, atur ketinggian baris yang dipilih ke ketinggian berbeda dan untuk yang lain mengembalikan tinggi baris normal

hilangInTransit
sumber
2
-1, tidak bekerja. Memanggil [table reloadData]hasil dalam perubahan ketinggian terjadi secara instan, daripada menjiwai.
Mark Amery