선택하면 UITableViewCell의 높이 변경에 애니메이션을 적용 할 수 있습니까?


393

UITableViewiPhone 앱에서를 사용하고 있으며 그룹에 속한 사람들의 목록이 있습니다. 사용자가 특정 사람을 클릭하면 (따라서 셀 선택) 셀의 높이가 커져 해당 사람의 속성을 편집하기위한 여러 UI 컨트롤이 표시되도록하고 싶습니다.

이것이 가능한가?

답변:


879

나는 내가 작업하고있는 부작용으로 이것에 대한 정말 간단한 해결책을 찾았 UITableView습니다 .....

을 통해 원래 높이를 정상적으로보고하는 변수에 셀 높이를 저장 한 tableView: heightForRowAtIndexPath:다음 높이 변경에 애니메이션을 적용하려면 변수 값을 변경하고 이것을 호출하십시오.

[tableView beginUpdates];
[tableView endUpdates];

전체 재로드를 수행하지는 않지만 UITableView셀을 다시 그려서 셀의 새 높이 값을 가져와야한다는 것을 알기에 충분합니다 . 그것은 당신을 위해 변화를 애니메이션합니다. 단.

내 블로그에 더 자세한 설명과 전체 코드 샘플이 있습니다 ... Animate UITableView 셀 높이 변경


6
훌륭합니다. 그러나 테이블의 애니메이션 속도를 제어 할 수있는 방법이 있습니까?
Josh Kahane

7
이것은 작동하지만 셀을 더 크게 만들면 44에서 74로 말한 다음 44로 다시 작게하면 구분선이 완전히 이상하게 작동합니다. 어떤 사람이 이것을 확인할 수 있습니까?
plaetzchen

46
이것은 기괴한 해결책이지만 WWDC 2010 "Mastering Table View"세션에서도 Apple이 권장하는 것입니다. 연구에 약 2 시간을 보냈기 때문에 문서에 추가하는 것에 대한 버그 보고서를 제출할 것입니다.
bpapa

5
나는이 솔루션을 시도했고 때로는 셀을 누를 때 50 %의 확률로 작동합니다. 누구나 같은 버그가 있습니까? iOS7 때문인가요?
Joan Cardona

7
이제 공식 문서에도 작성되었습니다 : 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의 답변을 좋아합니다. 실제로 해당 방법을 시도하지는 않았지만 목록의 모든 셀 크기가 변경되는 것처럼 보입니다. 나는 도청 된 세포의 변화를 바라고있었습니다. 나는 약간 시몬처럼 그것을 약간 차이가 있었어요. 셀 선택시 셀 모양이 변경됩니다. 그리고 그것은 움직입니다. 또 다른 방법입니다.

현재 선택된 셀 인덱스의 값을 보유 할 int를 작성하십시오.

int currentSelection;

그때:

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

그때:

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

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


}

tableView : cellForRowAtIndexPath :에서 유사한 유형을 변경하여 셀 유형을 변경하거나 셀의 xib 파일을로드 할 수 있다고 확신합니다.

이와 같이 currentSelection은 0에서 시작합니다. 목록의 첫 번째 셀 (인덱스 0)이 기본적으로 선택되어 표시되지 않게하려면 조정해야합니다.


2
내 게시물에 첨부 된 코드를 확인하십시오. 정확하게이 작업을 수행했을 때 셀 높이를 두 배로 선택했습니다. :)
Simon Lee

8
"실제로는이 방법을 시도하지 않았지만 목록에있는 모든 셀의 크기가 변경되는 것처럼 보입니다."
jamie

13
현재 선택은 이미 tableView.indexPathForSelectedRow에 저장되어 있습니다.
Nick

22

선택한 셀을 추적하는 속성 추가

@property (nonatomic) int currentSelection;

(예를 들어)에서 센티넬 값으로 설정 viewDidLoad하여 UITableView시작이 '정상'위치에서 시작 되도록하십시오.

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

    //sentinel
    self.currentSelection = -1;
}

에서 heightForRowAtIndexPath당신이 높이를 설정할 수 있습니다 당신이 선택한 셀에 대해 원하는

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

에서 didSelectRowAtIndexPath당신의 현재 선택 사항을 저장하고 동적 높이 저장, 경우 필요

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

에서는 didDeselectRowAtIndexPath센티널 값 선택 인덱스를 다시 설정하고 정상 형태로 되돌아 셀 애니메이션

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

        // sentinel
        self.currentSelection = -1;

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

감사합니다 감사합니다 셀을 토글 할 수 있도록 약간의 코드를 추가했습니다. 아래 코드를 추가했습니다.
Septronic

14

애니메이션이 없기 때문에 reloadData가 좋지 않습니다 ...

이것이 현재 시도중인 것입니다.

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

거의 제대로 작동합니다. 거의. 셀 높이를 높이고 있으며 테이블 뷰의 일부 스크롤 위치가 유지되는 것처럼 셀이 교체 될 때 테이블 뷰에 약간의 "딸꾹질"이 있습니다. 새로운 셀 (첫 번째 셀) 표)의 오프셋이 너무 높으면 스크롤 뷰가 튀어 위치를 변경합니다.


개인적 으로이 방법을 사용하지만 UITableViewRowAnimationNone과 함께 사용하면 더 매끄럽지 만 완벽한 결과를 얻을 수 없다는 것을 알았습니다.
Ron Srebro

11

beginUpdates / endUpdates를 연속적으로 호출하는 데 필요한 모든 내용이 무엇인지 모르겠습니다 -[UITableView reloadRowsAtIndexPaths:withAnimation:]. 다음은 예제 프로젝트 입니다.


이 기능은 자동 레이아웃 셀에서 텍스트 뷰를 늘리지 않습니다. 그러나 셀 크기를 업데이트 할 때 없음 애니메이션 옵션이 글리치처럼 보이기 때문에 애니메이션이 깜박 여야합니다.
h3dkandi

10

나는로 해결했다 reloadRowsAtIndexPaths.

didSelectRowAtIndexPath선택한 셀의 indexPath에 저장 reloadRowsAtIndexPaths하고 마지막에 호출 합니다 (다시로드 할 요소 목록에 NSMutableArray를 보낼 수 있음).

에서 heightForRowAtIndexPathindexPath가 목록에 있는지 여부를 expandIndexPath 셀과 보내기 높이가 아닌지 확인할 수 있습니다.

이 기본 예제를 확인할 수 있습니다 : https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam 간단한 해결책입니다.

도움이된다면 일종의 코드를 추가하십시오.

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

이것은 색인 행을 확장하기위한 것입니다.

@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

나 같은 사람이 사용자 정의 셀에 "자세한 정보"를 추가하도록 메모하십시오.

[tableView beginUpdates];
[tableView endUpdates];

훌륭한 작업을 수행했지만 셀 뷰를 "자르는"것을 잊지 마십시오. Interface Builder에서 셀-> 컨텐츠 뷰-> 속성 관리자에서 " 클립 하위 뷰 "를 선택하십시오.


2

Swift 3에 대한 더 짧은 버전의 Simons 답변입니다. 또한 셀 선택을 토글 할 수 있습니다

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

스위프트 4 이상

아래 코드를 테이블 뷰의 didselect 행 위임 메소드에 추가하십시오.

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

1

사이먼 리의 대답의 스위프트 버전.

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

그런 다음 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

네 가능합니다.

UITableView 위임 방법이 있습니다 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) {
    }];
}

그러나 사용자가 다른 셀을 스크롤하여 선택하면 현재 선택한 셀 reloadRowsAtIndexPaths:호출 을 축소하고 확장하려면 마지막으로 선택한 셀이 있어야 heightForRowAtIndexPath:하므로 적절하게 처리하십시오.


0

다음은 다시로드하지 않고 키보드 포커스를 잃지 않고 테이블 셀에서 UITableView확장되는 사용자 정의 하위 클래스 코드입니다 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의 멋진 답변을 사용했으며 ios 8.4 및 XCode 7.1.1과 완벽하게 작동했습니다.

셀을 토글 가능하게 만들려면 -tableViewDidSelect를 다음과 같이 변경하십시오.

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

}

이 중 하나가 도움이 되었기를 바랍니다.


0

iOS 7 이후 버전에서이 방법을 확인하십시오.

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

iOS 8에서이 기능이 개선되었습니다. 테이블 뷰 자체의 속성으로 설정할 수 있습니다.



0

입력-

tableView.beginUpdates () tableView.endUpdates () 이 함수는 호출하지 않습니다

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

그러나 그렇게하면 tableView.reloadRows (at : [selectedIndexPath! as IndexPath], .none)

그것은 호출 >있는 UITableViewCell {} - (IndexPath : jQuery과, cellForRowAt indexPath _있는 tableView) FUNC의있는 tableView를 이 기능을 사용하지 않음.


-1

방금 약간의 해킹 으로이 문제를 해결했습니다.

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

셀 높이를 확장해야 할 때 isInEdit = YES메소드를 설정 하고 호출 [self onTimer]하면 s_CellHeightEditing 값에 도달 할 때까지 셀 성장에 애니메이션을 적용합니다 :-)


시뮬레이터에서는 훌륭하게 작동하지만 iPhone에서는 하드웨어가 느립니다. 0.05 타이머 지연과 cellHeight 5 단위 증가로 훨씬 나아지지만 CoreAnimation과 같은 것은 없습니다.
Dzamir

1
다음에 게시하기 전에 확인하십시오.
ajay_nasa

-2

선택된 행의 인덱스 경로를 가져옵니다. 테이블을 다시로드하십시오. UITableViewDelegate의 heightForRowAtIndexPath 메서드에서 선택한 행의 높이를 다른 높이로 설정하고 다른 행의 경우 일반 행 높이를 반환


2
-1, 작동하지 않습니다. 호출 [table reloadData]하면 애니메이션이 아닌 높이 변화가 즉시 발생합니다.
Mark Amery
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.