Seçildiğinde bir UITableViewCell'de yükseklik değişikliğini canlandırabilir misiniz?


393

UITableViewİPhone uygulamamda bir kullanıyorum ve bir gruba ait kişilerin bir listesi var. Kullanıcı belirli bir kişiyi tıklattığında (böylece hücreyi seçerek), hücrenin, o kişinin özelliklerini düzenlemek için birkaç UI denetimi görüntülemek için yüksekliği büyür.

Mümkün mü?

Yanıtlar:


879

UITableViewÜzerinde çalıştığım bir yan etkisi olarak GERÇEKTEN BASİT bir çözüm buldum .....

Hücre yüksekliğini orijinal yüksekliğini normal olarak bildiren bir değişkende saklayın tableView: heightForRowAtIndexPath:, daha sonra bir yükseklik değişikliğini canlandırmak istediğinizde, değişkenin değerini değiştirin ve bunu çağırın ...

[tableView beginUpdates];
[tableView endUpdates];

Tam bir yeniden yükleme yapmadığını göreceksiniz, ancak UITableViewhücrelerin yeni yükseklik değerini kapmak için hücreleri yeniden çizmesi gerektiğini bilmek yeterli ... ve tahmin etmek ne? Sizin için değişikliği canlandırır. Tatlı.

Blogumda daha ayrıntılı bir açıklama ve tam kod örnekleri var ... Animasyon UITableView Hücre Yüksekliği Değişikliği


6
Bu harika. Ancak tablonun animasyon hızını kontrol etmenin herhangi bir yolu var mı?
Josh Kahane

7
Bu işe yarar ama bir hücre daha büyük yaparsanız 44 ila 74 diyelim ve daha sonra tekrar 44 daha küçük yapmak ayırıcı çizgi tamamen garip davranır. Bazıları bunu doğrulayabilir mi?
plaetzchen

46
Bu tuhaf bir çözüm, ancak Apple'ın WWDC 2010 "Mastering Table View" oturumunda da önerdiği şey bu. Ben sadece yaklaşık 2 saat araştırma geçirdim çünkü bu belgelere ekleme hakkında bir hata raporu dosyalayacağım.
bpapa

5
Bu çözümü denedim ve sadece bazen çalışıyor, bir hücreye bastığınızda% 50 çalışma olasılığı var. Aynı hataya sahip olan var mı? Çünkü iOS7?
Joan Cardona

7
Şimdi resmi belgelerde de yazılmıştır: 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

Simon Lee'nin cevabını seviyorum. Aslında bu yöntemi denemedim ama listedeki tüm hücrelerin boyutunu değiştirecek gibi görünüyor. Sadece dokunulan hücrenin değişmesini umuyordum. Ben Simon gibi yaptım ama biraz farkla. Bu seçildiğinde bir hücrenin görünümünü değiştirir. Ve canlandırıyor. Bunu yapmanın başka bir yolu.

Seçili olan hücre dizini için bir değeri tutmak üzere bir int oluşturun:

int currentSelection;

Sonra:

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

Sonra:

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

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


}

Tablo türünü değiştirmek veya hücre için bir xib dosyası yüklemek için tableView: cellForRowAtIndexPath: benzer değişiklikler yapabilirsiniz eminim.

Bunun gibi, currentSelection 0'da başlayacaktır. Listenin ilk hücresinin (dizin 0'da) varsayılan olarak seçili görünmesini istemiyorsanız ayarlamalar yapmanız gerekir.


2
Gönderima ekli kodu kontrol edin, tam olarak bunu yaptım, seçildiğinde bir hücrenin yüksekliğini iki katına çıkardım. :)
Simon Lee

8
"Aslında bu yöntemi denemedim ama listedeki tüm hücrelerin boyutunu değiştirecek gibi görünüyor" - o zaman çok sert görünmüyordun.
jamie

13
Geçerli seçim zaten tableView.indexPathForSelectedRow içinde saklanır.
Nick

22

Seçili hücreyi takip etmek için bir özellik ekleyin

@property (nonatomic) int currentSelection;

Başlangıçların 'normal' konumda viewDidLoadolduğundan emin olmak için (örneğin) içindeki bir sentinel değerine ayarlayınUITableView

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

    //sentinel
    self.currentSelection = -1;
}

İçinde heightForRowAtIndexPathseçilen hücre için istediğiniz yüksekliği ayarlayabilirsiniz

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

In didSelectRowAtIndexPathsize Geçerli seçimi kaydetmek ve dinamik bir yüksekliğe tasarrufu, gerekirse

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

Gelen didDeselectRowAtIndexPathsentinel değerine seleksiyon indeksi geri ayarlanır ve normal forma hücre geri animasyon

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

        // sentinel
        self.currentSelection = -1;

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

Teşekkürler teşekkürler teşekkürler! Hücreyi açıp kapamak için biraz kod ekledim. Aşağıdaki kodu ekledim.
Septronic

14

reloadData iyi değil çünkü animasyon yok ...

Şu anda denediğim şey bu:

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

Neredeyse doğru çalışıyor. Neredeyse. Hücrenin yüksekliğini artırıyorum ve bazen hücre görünümünde tablo görünümünde küçük bir "hıçkırık" var, sanki tablo görünümünde bir kaydırma konumu korunuyormuş gibi, yeni hücre (ilk hücre ), ofseti çok yüksek olacak şekilde sona erer ve kaydırma görünümü yeniden konumlandırmak için seker.


Şahsen ben bu yöntemi kullanarak ama UITableViewRowAnimationNone ile daha pürüzsüz ama yine de mükemmel bir sonuç sağlar bulundu.
Ron Srebro

11

StartUpdates / endUpdates'i art arda çağırmakla ilgili tüm bu şeylerin ne olduğunu bilmiyorum, sadece kullanabilirsiniz -[UITableView reloadRowsAtIndexPaths:withAnimation:]. İşte bir örnek proje .


Mükemmel, otomatik düzen hücremdeki metin görünümlerimi uzatmıyor. Ancak, animasyonun titremesi gerekir, çünkü hücre boyutunu güncellerken Animasyon Yok seçeneği glitchy görünür.
h3dkandi

10

Karar verdim reloadRowsAtIndexPaths.

Ben didSelectRowAtIndexPathseçilen hücrenin indexPath kaydedin ve reloadRowsAtIndexPathssonunda (yeniden yüklemek istediğiniz öğelerin listesi için NSMutableArray gönderebilirsiniz).

İçinde heightForRowAtIndexPath indexPath'in expandIndexPath hücresinin listede olup olmadığını kontrol edebilir ve yüksekliği gönderebilirsiniz.

Bu temel örneği kontrol edebilirsiniz: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Basit bir çözüm.

sana yardım edersem bir çeşit kod eklerim

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


3

Bunu dizinsel satırı genişletmek için deneyin:

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

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

2

benim gibi birisi için özel bir hücreye "Daha Fazla Ayrıntı" eklemek için bir not.

[tableView beginUpdates];
[tableView endUpdates];

Mükemmel bir iş yaptı, ancak hücre görünümünü "kırpmayı" unutmayın. Arayüz Oluşturucu'dan Hücrenizi seçin -> İçerik Görünümü -> Özellik Denetçisinden " Klip alt görünümü " seçin


2

Heres, Swift 3 için Simons cevabının daha kısa bir versiyonudur. Ayrıca, hücre seçiminin değiştirilmesine de izin verir

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
  }

2

Swift 4 ve Üstü

tableview'ın didselect row delegate yöntemine aşağıdaki kodu ekleyin

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

1

Simon Lee'nin cevabının Hızlı Versiyonu.

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

Sonra didSelectRowAtIndexPath () içinde

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

1

Evet mümkün.

UITableView temsilci yöntemi var 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) {
    }];
}

Ancak sizin durumunuzda kullanıcı farklı bir hücreyi kaydırır ve seçerse, o zaman seçili olan hücre reloadRowsAtIndexPaths:çağrılarını küçültmek ve genişletmek için son seçilen hücreye sahip olmanız gerekir heightForRowAtIndexPath:.


0

İşte yeniden yükleme (ve kayıp klavye odak) olmadan tablo hücresinde UITableViewgenişleyen özel alt sınıf benim kod UITextView:

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

0

@ Joy'un harika cevabını kullandım ve ios 8.4 ve XCode 7.1.1 ile mükemmel çalıştı.

Hücrenizin geçiş yapabilmesini istiyorsanız, -tableViewDidSelect öğesini aşağıdaki gibi değiştirdim:

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

}

Umarım bunlardan herhangi biri size yardımcı olmuştur.


0

İOS 7 ve sonraki sürümlerden sonra bu yöntemi kontrol edin.

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

İOS 8'de bu konuda iyileştirmeler yapıldı. Tablo görünümünün kendisinin özelliği olarak ayarlayabiliriz.



0

Girişler -

tableView.beginUpdates () tableView.endUpdates () bu işlevler çağrılamaz

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

Ama eğer yaparsan, tableView.reloadRows (at: [selectedIndexPath! Olarak IndexPath olarak], ile: .none)

O arayacak > UITableViewCell {} - (indexPath: UITableView, cellForRowAt indexPath _ tableView) fonk tableView bu fonksiyonu.


-1

Ben sadece bu sorunu biraz hack ile çözdüm:

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

Hücre yüksekliğini genişletmek gerektiğinde ben ayarlamak isInEdit = YESve yöntemi çağırmak [self onTimer]ve s_CellHeightEditing değerine ulaşana kadar hücre büyümesini canlandırır :-)


Simülatörde harika çalışıyor, ancak iPhone'da donanım laggy. 0,05 zamanlayıcı gecikmesi ve hücre yüksekliği 5 birim artışla, çok daha iyi, ancak CoreAnimation
Dzamir

1
Göndermeden önce onaylayın. Sonraki zaman.
ajay_nasa

-2

Seçilen satırın dizin yolunu alın. Tabloyu yeniden yükleyin. UITableViewDelegate'in heightForRowAtIndexPath yönteminde, seçilen satırın yüksekliğini farklı bir yüksekliğe ayarlayın ve diğerleri için normal satır yüksekliğini döndürün


2
-1, çalışmıyor. Sesli arama yapmak [table reloadData], canlandırma yapmak yerine yükseklik değişikliğinin anında gerçekleşmesine neden olur.
Mark Amery
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.