Bạn có thể tạo hiệu ứng thay đổi chiều cao trên UITableViewCell khi được chọn không?


393

Tôi đang sử dụng một UITableViewứng dụng trong iPhone của mình và tôi có một danh sách những người thuộc về một nhóm. Tôi muốn nó để khi người dùng nhấp vào một người cụ thể (do đó chọn ô), ô sẽ tăng chiều cao để hiển thị một số điều khiển UI để chỉnh sửa các thuộc tính của người đó.

Điều này có thể không?

Câu trả lời:


879

Tôi đã tìm thấy một giải pháp SIMPLE THỰC SỰ cho vấn đề này như là một tác dụng phụ cho một UITableViewtôi đang làm việc trên .....

Lưu trữ chiều cao của ô trong một biến báo cáo chiều cao ban đầu thông thường thông qua tableView: heightForRowAtIndexPath:, sau đó khi bạn muốn tạo hiệu ứng thay đổi chiều cao, chỉ cần thay đổi giá trị của biến và gọi đây là ...

[tableView beginUpdates];
[tableView endUpdates];

Bạn sẽ thấy nó không thực hiện tải lại đầy đủ nhưng đủ UITableViewđể biết nó phải vẽ lại các ô, lấy giá trị chiều cao mới cho ô .... và đoán xem? Nó ANIMATE sự thay đổi cho bạn. Ngọt.

Tôi có một lời giải thích chi tiết hơn và các mẫu mã đầy đủ trên blog của mình ... Animate UITableView Cell Change Change


6
Điều này thật tuyệt vời. Nhưng có cách nào chúng ta có thể kiểm soát tốc độ hoạt hình của bảng không?
Josh Kahane

7
Điều này hoạt động nhưng nếu tôi làm cho một ô lớn hơn, hãy nói từ 44 đến 74 và sau đó làm cho nó nhỏ hơn một lần nữa thành 44, đường phân cách hoạt động hoàn toàn kỳ lạ. Ai đó có thể xác nhận điều này?
plaetzchen

46
Đây là một giải pháp kỳ quái, nhưng đó là những gì Apple khuyến nghị trong phiên "Làm chủ bảng xem" của WWDC 2010. Tôi sẽ nộp báo cáo lỗi về việc thêm tài liệu này vào tài liệu vì tôi mới dành khoảng 2 giờ để nghiên cứu.
bpapa

5
Tôi đã thử giải pháp này và đôi khi nó chỉ hoạt động, khi bạn nhấn một ô có xác suất 50% để hoạt động. Bất cứ ai cũng có cùng một lỗi? Có phải vì iOS7?
Joan Cardona

7
Bây giờ nó cũng được viết trong tài liệu chính thức: 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/l
Jaroslav

63

Tôi thích câu trả lời của Simon Lee. Tôi đã không thực sự thử phương pháp đó nhưng có vẻ như nó sẽ thay đổi kích thước của tất cả các ô trong danh sách. Tôi đã hy vọng cho một sự thay đổi của chỉ tế bào được khai thác. Tôi đã làm nó giống như Simon nhưng chỉ với một chút khác biệt. Điều này sẽ thay đổi giao diện của một ô khi nó được chọn. Và nó làm động. Chỉ là một cách khác để làm điều đó.

Tạo một int để giữ một giá trị cho chỉ mục ô được chọn hiện tại:

int currentSelection;

Sau đó:

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

Sau đó:

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

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


}

Tôi chắc chắn rằng bạn có thể thực hiện các thay đổi tương tự trong bảngView: cellForRowAtIndexPath: để thay đổi loại ô hoặc thậm chí tải tệp xib cho ô.

Giống như thế này, currentSelection sẽ bắt đầu từ 0. Bạn sẽ cần điều chỉnh nếu bạn không muốn ô đầu tiên của danh sách (ở chỉ số 0) được chọn theo mặc định.


2
Kiểm tra mã đính kèm với bài viết của tôi, tôi đã làm chính xác điều này, tăng gấp đôi chiều cao cho một ô khi được chọn. :)
Simon Lee

8
"Tôi đã không thực sự thử phương pháp đó nhưng có vẻ như nó sẽ thay đổi kích thước của tất cả các ô trong danh sách" - sau đó bạn trông không khó lắm.
jamie

13
Lựa chọn hiện tại đã được lưu trữ trong tableView.indexPathForSelectedRow.
Nick

22

Thêm một thuộc tính để theo dõi ô đã chọn

@property (nonatomic) int currentSelection;

Đặt nó thành một giá trị sentinel trong (ví dụ) viewDidLoad, để đảm bảo rằng UITableViewbắt đầu ở vị trí 'bình thường'

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

    //sentinel
    self.currentSelection = -1;
}

Trong heightForRowAtIndexPathbạn có thể đặt chiều cao bạn muốn cho ô đã chọn

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

Trong didSelectRowAtIndexPathbạn lưu lựa chọn hiện tại và lưu chiều cao động, nếu cần

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

Trong didDeselectRowAtIndexPaththiết lập chỉ mục lựa chọn trở về giá trị sentinel và làm động ô trở lại dạng bình thường

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

        // sentinel
        self.currentSelection = -1;

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

Cảm ơn bạn, cảm ơn bạn, cảm ơn bạn! Tôi đã thêm một chút mã để làm cho ô có thể chuyển đổi. Tôi đã thêm mã dưới đây.
Septronic

14

reloadData là không tốt vì không có hình ảnh động ...

Đây là những gì tôi đang cố gắng:

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

Nó gần như hoạt động đúng. Hầu hết. Tôi đang tăng chiều cao của ô và đôi khi có một chút "trục trặc" trong chế độ xem bảng khi ô được thay thế, như thể một vị trí cuộn trong chế độ xem bảng đang được giữ nguyên, ô mới (là ô đầu tiên trong bảng) kết thúc với độ lệch của nó quá cao và scrollview bị trả về để định vị lại nó.


Cá nhân tôi thấy rằng sử dụng phương pháp này nhưng với UITableViewRowAnimationNone cung cấp một kết quả mượt mà hơn nhưng vẫn không hoàn hảo.
Ron Srebro

11

Tôi không biết tất cả những thứ này về việc gọi startUpdates / endUpdates liên tiếp là gì, bạn chỉ có thể sử dụng -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Đây là một dự án ví dụ .


Tuyệt vời, điều này không kéo dài lượt xem văn bản của tôi trong ô bố trí tự động của tôi. Nhưng nó phải có một nhấp nháy cho hình ảnh động bởi vì tùy chọn Không có hình động nào trông rối mắt khi cập nhật kích thước ô.
h3dkandi

10

Tôi giải quyết với reloadRowsAtIndexPaths.

Tôi lưu vào didSelectRowAtIndexPathindexPath của ô được chọn và gọi reloadRowsAtIndexPathsở cuối (bạn có thể gửi NSMutableArray cho danh sách các phần tử mà bạn muốn tải lại).

Trong đó, heightForRowAtIndexPathbạn có thể kiểm tra xem indexPath có trong danh sách hay không của chiều rộng và gửi chiều cao của ô.

Bạn có thể kiểm tra ví dụ cơ bản này: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam Đó là một giải pháp đơn giản.

tôi thêm một loại mã nếu giúp bạn

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

Hãy thử điều này là để mở rộng hàng theo chỉ số:

@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

chỉ là một ghi chú cho người như tôi đang tìm kiếm để thêm "Chi tiết khác" trên ô tùy chỉnh.

[tableView beginUpdates];
[tableView endUpdates];

Đã làm một công việc tuyệt vời, nhưng đừng quên "cắt" chế độ xem tế bào. Từ Trình tạo giao diện, chọn Ô của bạn -> Chế độ xem nội dung -> từ Trình kiểm tra thuộc tính, chọn " Clip phụ "


2

Đây là phiên bản rút gọn của câu trả lời Simons cho Swift 3. Cũng cho phép chuyển đổi lựa chọn của ô

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 trở lên

thêm mã dưới đây vào phương thức ủy nhiệm hàng của bạn của viewview

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

1

Phiên bản Swift của câu trả lời của 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
}

Sau đó, trong 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()
            }
        }
    }
}

1

Vâng nó có thể.

UITableView có phương thức đại biểu 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) {
    }];
}

Nhưng trong trường hợp của bạn nếu người dùng cuộn và chọn một ô khác thì bạn cần phải có ô được chọn cuối cùng để thu nhỏ và mở rộng các reloadRowsAtIndexPaths:cuộc gọi ô hiện được chọn heightForRowAtIndexPath:để xử lý phù hợp.


0

Đây là mã UITableViewlớp con tùy chỉnh của tôi , mở rộng UITextViewtại ô bảng, mà không tải lại (và mất tiêu điểm bàn phím):

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

Tôi đã sử dụng câu trả lời tuyệt vời của @ Joy và nó hoạt động hoàn hảo với ios 8.4 và XCode 7.1.1.

Trong trường hợp bạn đang muốn làm cho tế bào của bạn có thể chuyển đổi được, tôi đã thay đổi -tableViewDidSelect thành như sau:

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

}

Tôi hy vọng bất kỳ điều này đã giúp bạn.


0

Kiểm tra phương pháp này sau iOS 7 trở lên.

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

Những cải tiến đã được thực hiện cho điều này trong iOS 8. Chúng ta có thể đặt nó làm thuộc tính của chế độ xem bảng.



0

Đầu vào -

tableView.beginUpdates () tableView.endUpdates () các hàm này sẽ không gọi

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

Nhưng, nếu bạn làm như vậy, tableView.reloadRows (tại: [chọn IndexPath! As IndexPath], với: .none)

Nó sẽ gọi func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {} chức năng này.


-1

Tôi chỉ giải quyết vấn đề này với một chút hack:

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

Khi tôi cần mở rộng chiều cao ô, tôi đặt isInEdit = YESvà gọi phương thức [self onTimer]và nó sẽ kích hoạt sự tăng trưởng của ô cho đến khi đạt đến giá trị s_CellHeightEditing :-)


Trong trình giả lập hoạt động tuyệt vời, nhưng trong phần cứng iPhone bị lag. Với độ trễ hẹn giờ 0,05 và với mức tăng 5 đơn vị tế bào, sẽ tốt hơn nhiều, nhưng không có gì giống như CoreAnimation
Dzamir

1
Xác nhận, trước khi đăng bài..tiếp theo thời gian.
ajay_nasa

-2

Lấy đường dẫn của hàng được chọn. Tải lại bảng. Trong phương thức heightForRowAtIndexPath của UITableViewDelegate, đặt chiều cao của hàng được chọn thành một chiều cao khác và cho các chiều cao khác trả về chiều cao hàng bình thường


2
-1, không hoạt động. Gọi [table reloadData]kết quả trong thay đổi chiều cao xảy ra ngay lập tức, thay vì hoạt hình.
Đánh dấu Amery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.