programing

UITableViewCell 클릭시 확장

yoursource 2021. 1. 16. 10:51
반응형

UITableViewCell 클릭시 확장


사용자 정의 UITableViewCell이 있다고 가정 해 보겠습니다.

따라서 셀에서 사용자 지정 버튼을 클릭 할 때마다 어느 정도 확장되어야하며 (40 높이 더 말할 수 있습니다 ...) 동일한 사용자 지정 버튼을 다시 클릭하면 이전 높이로 축소됩니다.

개발자 님 안내해주세요 ..이 작업을 어떻게 수행 할 수 있습니까?


heightForRowAtIndexPath를 구현하여 올바른 높이를 계산합니다. 그런 다음 버튼 코드에서 테이블이 beginUpdates와 endUpdates를 사용하여 각 셀의 높이를 재평가하도록합니다.

[self.tableView beginUpdates];
[self.tableView endUpdates];

tableview 셀의 높이에 대한 변경 사항은 heightForRowAtIndexPath로 자동으로 계산되며 변경 사항도 애니메이션됩니다.

실제로이 작업을 수행하는 셀의 버튼 대신에서 셀을 선택하도록 만들 수도 있습니다 didSelectRowAtIndexPath.


나는 그것이 완벽하게 옳다는 것을 고려하여 받아 들여진 대답과 모순되는 여기에서 아무것도 말하지 않을 것입니다. 그러나이를 수행하는 방법에 대해 자세히 설명하겠습니다. 이 모든 내용을 읽고 싶지 않고 작업중인 프로젝트에서 소스 코드를 사용하는 데 더 관심이 있다면 예제 프로젝트를 GitHub에 업로드했습니다 .

기본 아이디어는 -tableView: heightForRowAtIndexPath:현재 셀을 확장해야하는지 여부를 결정 하는 메서드 내부에 조건을 갖는 것 입니다. 이것은 테이블에서 시작 / 종료 업데이트를 호출하여 트리거됩니다 -tableView: didSelectRowAtIndexPath:.이 예제에서는 한 번에 하나의 셀을 확장 할 수있는 테이블보기를 만드는 방법을 보여줍니다.

가장 먼저해야 할 일은 NSIndexPath 객체에 대한 참조를 선언하는 것입니다. 원하는대로이 작업을 수행 할 수 있지만 다음과 같은 속성 선언을 사용하는 것이 좋습니다.

@property (strong, nonatomic) NSIndexPath *expandedIndexPath;

참고 : viewDidLoad 또는 기타 유사한 메서드 내에서이 인덱스 경로를 만들 필요가 없습니다. 인덱스가 처음에 nil이라는 사실은 테이블에 처음에 확장 된 행이 없다는 것을 의미 할뿐입니다. 선택한 행이 확장 된 상태에서 테이블을 시작하려면 다음과 같이 viewDidLoad 메서드를 추가 할 수 있습니다.

NSInteger row = 1;
NSInteger section = 2;
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section];

다음 단계는 UITableViewDelegate 메서드 -tableView: didSelectRowAtIndexPath:로 이동하여 사용자 선택에 따라 확장 된 셀 인덱스를 변경하는 논리를 추가하는 것입니다. 여기서 아이디어는 expandedIndexPath변수 내부에 저장된 인덱스 경로에 대해 방금 선택한 인덱스 경로를 확인하는 것 입니다. 두 항목이 일치하면 사용자가 확장 된 셀을 선택 취소하려고한다는 것을 알고 있으며이 경우 변수를 nil로 설정합니다. 그렇지 않으면 expandedIndexPath방금 선택한 인덱스로 변수를 설정합니다 . 이것은 테이블 뷰가 전환 애니메이션을 자동으로 처리 할 수 ​​있도록 beginUpdates / endUpdates 호출 사이에 모두 수행됩니다.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView beginUpdates]; // tell the table you're about to start making changes

    // If the index path of the currently expanded cell is the same as the index that
    // has just been tapped set the expanded index to nil so that there aren't any
    // expanded cells, otherwise, set the expanded index to the index that has just
    // been selected.
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
        self.expandedIndexPath = nil;
    } else {
        self.expandedIndexPath = indexPath;
    }

    [tableView endUpdates]; // tell the table you're done making your changes
}

그런 다음 마지막 단계는 다른 UITableViewDelegate 메서드에 -tableView: heightForRowAtIndexPath:있습니다. 이 메서드는 beginUpdates테이블에서 업데이트가 필요하다고 판단한 각 인덱스 경로에 대해 한 번씩 트리거 후에 호출됩니다 . 여기에서 expandedIndexPath현재 재평가중인 인덱스 경로와 비교합니다 .

두 인덱스 경로가 같으면 확장하려는 셀이고 그렇지 않으면 높이가 정상이어야합니다. 값 100과 44를 사용했지만 필요에 맞는 것을 사용할 수 있습니다.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Compares the index path for the current cell to the index path stored in the expanded
    // index path variable. If the two match, return a height of 100 points, otherwise return
    // a height of 44 points.
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
        return 100.0; // Expanded height
    }
    return 44.0; // Normal height
}

이를 위해 오픈 소스 라이브러리를 만들었습니다. 코드와 voilà 에서 축소 및 확장 델리게이트를 구현하기 만하면됩니다 ! 모든 그림과 애니메이션을 수행 할 수도 있습니다. 이것을 확인 하십시오 .

여기에 이미지 설명 입력


나는 당신이 말하는 것과 정확히 일치하는 재사용 가능한 구성 요소를 만들었습니다. 사용하기가 매우 쉽고 데모 프로젝트가 있습니다.

GitHub의 GCRetractableSectionController .


[tableView beginUpdates]을 사용하는 대신 메서드 내부에서 메서드를 [tableView endUpdates]사용하고 있습니다.[tableView reloadRowsAtIndexPath:... withRowAnimation:...]didSelectRowAtIndexPath

을 확장 할 때 표시해야하는 요소에 문제가 있었기 때문에 UITableViewCell시작 및 종료 업데이트 메서드를 사용할 때 이것을 선호 합니다. 또 다른 점은 Top, Bottom, Left, Right ...와 같은 애니메이션 중에서 선택할 수 있다는 것입니다.


이것은 Mick의 대답이지만 Swift 4의 경우 (IndexPath는 nil이 Swift를 충돌시키기 때문에 빈 IndexPath와 함께 제공되는 NSIndexPath를 대체합니다. 또한를 사용하여 IndexPath의 두 인스턴스를 비교할 수 있습니다 ==)

extendedIndexPath 속성을 선언합니다.

var expandedIndexPath = IndexPath()

선택적 viewDidLoad 부분.

expandedIndexPath = IndexPath(row: 1, section: 2)

그런 다음 didSelectRow 부분입니다.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.beginUpdates()

    if indexPath == expandedIndexPath {
        expandedIndexPath = IndexPath()
    } else {
        expandedIndexPath = indexPath
    }

    tableView.endUpdates()
}

그런 다음 heightForRow 부분.

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if indexPath == expandedIndexPath {
        return 100
    }

    return 44
}

나는 Gcamp의 소스 코드를 사용하고 나만의 버전을 만들었습니다.

1) loadView 메서드에서 섹션의 확장 또는 비 확장 상태를 저장할 가변 배열을 초기화합니다. 확장 된 상태를 별도의 배열에 저장하는 것이 중요합니다. 이는 테이블보기가 스크롤되는 동안 파괴되지 않습니다 (예를 들어 headerView에 저장하면 다시 그려지고 확장 된 날씨를 잊어 버립니다). 제 경우에는 _sectionStatuses 배열입니다.

- (void)loadView
{
     // At the beginning all sections are expanded
    _sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections];
    for (int i = 0; i < self.tableView.numberOfSections; i++) {
        _sectionStates[i] = [NSNumber numberWithBool:YES];
    }
}

2) 확장 버튼이있는 섹션에 대한 사용자 지정 headerView를 만듭니다. 위임 패턴을 사용하여 headerView의 버튼에서 TableViewController로 작업을 위임합니다. Gcamp의 소스 코드에서 적절한 이미지를 찾을 수 있습니다.

3) 행을 제거하거나 추가하는 조치를 작성하십시오. 여기 _foldersArray는 모든 데이터를 포함하는 내 구조입니다. 내 섹션의 headerView-MCExpandableAccountHeaderView는 자신의 섹션 번호를 알고 있습니다. 각 섹션의 헤더보기를 만들 때 전송합니다. 어떤 섹션이 현재 확장 또는 확장되었는지 알아야하므로이 방법으로 전송하는 것이 중요합니다.

- (void)expandClicked:(MCAccountHeaderView *)sender
{
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender;

// Finding a section, where a button was tapped
NSInteger section = expandableAccountHeaderView.section;

// Number of rows, that must be in a section when it is expanded
NSUInteger contentCount = [_foldersArray[section - 1][@"folders"] count];

// Change a saved status of a section
BOOL expanded = [_sectionStates[section] boolValue];
expanded = ! expanded;
expandableAccountHeaderView.expanded = expanded;
_sectionStates[section] = [NSNumber numberWithBool:expanded];

// Animation in a table
[self.tableView beginUpdates];

NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < contentCount; i++) {
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section];
    [modifiedIndexPaths addObject:indexPath];
}

if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade];

[self.tableView endUpdates];

// Scroll to the top of current expanded section
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

4) 확장 여부에 따라 섹션에서 올바른 수 또는 행을 반환하는 것도 중요합니다.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
     BOOL expanded = [_sectionStates[section] boolValue];

     return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0;   
}

나를 위해 다음을 사용하는 것이 좋습니다.

  1. UITableViewDelegate에서

    func tableView (_ tableView : UITableView, didSelectRowAt indexPath : IndexPath) {

        print("Did select row: \(indexPath.row).")
    
        tableView.beginUpdates()
        tableView.endUpdates()
    }
    
  2. 선택 가능 / 확장 가능한 UITableViewCell

    override func setSelected (_ selected : Bool, animated : Bool) {super.setSelected (selected, animated : animated)

       configStyle(selected)
    }
    
  3. 중대한! tableView.rowHeight이다 .automatic와있는 UITableViewCell 자동 높이 계산을 가능하게하는 제약 조건이다, 즉 그것의 높이 제약 조건을 명확하게 사용되는 추가 위 / 아래 또는 높이 제한 또는 라벨 고유 콘텐츠 크기에 제약 같이 정의된다.


initialize iSelectedIndex = -1; and declare
UITableView *urTableView;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{

return 10;    //Section count

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

return 3; //row count

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if(cell == nil)
{
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];

}

[cell.textLabel setText:[NSString stringWithFormat:@"sec:%d,row:%d",indexPath.section,indexPath.row]];

return cell;

}


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

// adding a label with the tap gesture to the header in each section

headerLabel = [[UILabel alloc]init]; 

headerLabel.tag = section;

headerLabel.userInteractionEnabled = YES;

headerLabel.backgroundColor = [UIColor greenColor];

headerLabel.text = [NSString stringWithFormat:@"Header No.%d",section];

headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(gestureTapped:)];

[headerLabel addGestureRecognizer:tapGesture];

return headerLabel;

}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{

return 50.0; //adjust the height as you need

}

- (void)gestureTapped:(UITapGestureRecognizer *)sender{

UIView *theSuperview = self.view; // whatever view contains 

CGPoint touchPointInSuperview = [sender locationInView:theSuperview];

UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil];

if([touchedView isKindOfClass:[UILabel class]])
{

    if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand

        iSelectedIndex = touchedView.tag;

    }else{   // if the header is already expanded , need to collapse

        iSelectedIndex = -1;

    }

    [urTableView beginUpdates];

    [urTableView endUpdates];

}

}

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

// Show or hide cell

float height = 0.0;

if (indexPath.section == iSelectedIndex) {

    height = 44.0; // Show the cell - adjust the height as you need

}

return height;

}

0x7fffffff의 답변에 추가하려면 didSelectRowAtIndexPath 내의 if 문에 추가 조건이 필요하다는 것을 알았습니다 .

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{

   [tableView beginUpdates];

   if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) {
       self.expandedIndexPath = nil;
   } else {
       self.expandedIndexPath = indexPath;
   }

   [tableView endUpdates];

}

버튼을 탭하여 셀을 확장하고 특정 레이블을 설정하는 방법에 대한 중간 기사이어 다음 numbersOfLine을 사용하여 애니메이션을 수행 할 수있었습니다.

tableView.beginUpdates()
tableView.performBatchUpdates({
  cell.description.numberOfLines = !expanded ? 0 : 3
}, completion: nil)
tableView.endUpdates()

알림 performBatchUpdates은 iOS 11⬆️에서만 사용할 수 있습니다.

참조 URL : https://stackoverflow.com/questions/4635338/uitableviewcell-expand-on-click

반응형