UFO ET IT

-reloadData를 호출 한 후 UITableView contentoffset을 유지하는 방법

ufoet 2020. 12. 13. 10:01
반응형

-reloadData를 호출 한 후 UITableView contentoffset을 유지하는 방법


CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO];    //unuseful

//    __block UITableView *tableBlock = _table;
//    [self performBlock:^(id sender) {
//        [tableBlock setContentOffset:offset];
//    } afterDelay:2];

나는 이후에 호출되는 대리자 메서드를 모른다는 것을 압니다 reloadData. 그리고 afterDelay:2어떤 종류의 해킹을 사용 하는 것이 너무 짧거나 너무 길 수 있으므로 어떻게 구현할 수 있습니까?


나는 최근에 작업 한 reloadData- reloadData변경되지 않습니다 contentOffset또는 테이블 뷰를 스크롤합니다. 오프셋이 새로운 데이터 양보다 적 으면 실제로 동일하게 유지됩니다.


내 cellForRowAtIndexPath 메서드에서 셀 크기 조정을 엉망으로 만들기 때문에 문제가 발생했습니다. reloadData를 수행 한 후 크기 조정 정보가 꺼져 있음을 알았으므로 콘텐츠 오프셋을 다시 설정하기 직전에 레이아웃에 강제로 적용해야한다는 것을 깨달았습니다.

CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];

reloadDatatableView를 호출 해도 콘텐츠 오프셋이 변경되지 않습니다. 그러나 UITableViewAutomaticDimensioniOS 8에서 도입 된 것을 사용한다면 문제가있을 수 있습니다.

사용하는 동안 UITableViewAutomaticDimension대리인 방법을 작성하는 하나 개의 요구를 tableView: estimatedHeightForRowAtIndexPath:반환 UITableViewAutomaticDimension과 함께 tableView: heightForRowAtIndexPath:하는 동일한를 반환합니다.

저에게는 이것을 사용하는 동안 iOS 8에서 문제가 발생했습니다. estimatedHeightForRowAtIndexPath:을 사용했지만 메서드 메서드가 부정확 한 값을 반환 했기 때문 UITableViewAutomaticDimension입니다. iOS 9 장치에는 문제가 없었기 때문에 iOS 8에서는 문제였습니다.

사전을 사용하여 셀 높이 값을 저장하고 반환하여이 문제를 해결했습니다. 이것이 내가 한 일입니다.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = @(cell.frame.size.height);

    [self.cellHeightsDictionary setObject:height forKey:key];
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = [self.cellHeightsDictionary objectForKey:key];

    if (height)
    {
        return height.doubleValue;
    }

    return UITableViewAutomaticDimension;
}

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

높이가 있는지 확인하는 것은 페이지가 처음로드 될 때입니다.


@Skywalker 답변의 Swift 4 변형 :

fileprivate var heightDictionary: [Int : CGFloat] = [:]

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    heightDictionary[indexPath.row] = cell.frame.size.height
}

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    let height = heightDictionary[indexPath.row]
    return height ?? UITableViewAutomaticDimension
}

다른 솔루션 (MessageKit에서 가져옴) :

대신이 메서드를 호출해야합니다 reloadData. 이것은 특정 경우에 적합 할 수 있습니다.

public func reloadDataAndKeepOffset() {
    // stop scrolling
    setContentOffset(contentOffset, animated: false)

    // calculate the offset and reloadData
    let beforeContentSize = contentSize
    reloadData()
    layoutIfNeeded()
    let afterContentSize = contentSize

    // reset the contentOffset after data is updated
    let newOffset = CGPoint(
        x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
        y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
    setContentOffset(newOffset, animated: false)
}

기본적으로 reloadData는 contentOffset을 유지합니다. 그러나 부정확 한 expectedRowHeight 값이있는 경우 업데이트 될 수 있습니다.


제 경우에는 행 높이를 자동으로 선택 취소하고 자동 문제가 해결되었습니다.

재 장전 문제 수정


estimatedHeightForRowAtIndexPath방법 을 구현 하고 예상치가 맞지 않으면이 상황에 들어갈 수 있습니다.

이를 해결하기 위해 다음과 같이 tableView의 모든 셀 높이보다 큰 큰 높이를 반환 할 수 있습니다.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return 800.f; // if 800 is bigger than every possible value of your cell height.
}

dataSource 배열의 시작 부분에 데이터를 삽입하는 경우 다음 contentOffset과 같이 변경해야합니다 . Swift 3+

func prepareReloadData() {
    let previousContentHeight = tableView.contentSize.height
    let previousContentOffset = tableView.contentOffset.y
    tableView.reloadData()
    let currentContentOffset = tableView.contentSize.height - previousContentHeight + previousContentOffset
    tableView.contentOffset = CGPoint(x: 0, y: currentContentOffset)
}

나는 같은 문제가 있었지만 여기에서 제안 된 답변 중 어느 것도 작동하지 않았습니다. 해결 방법은 다음과 같습니다. 다음 UITableViewlayoutSubviews같은 하위 클래스 및 재정의 메서드 :

override func layoutSubviews() {
    let offset = contentOffset
    super.layoutSubviews()
    contentOffset = offset
}

@Skywalker's answer showed best workaround for estimated height of cells problem. But sometimes problem lyes in a different place.
Sometimes the problem lyes in contentInsets of table view. If you make reload data while tableView is not visible on the screen you can face with wrong offset after the table view appears on the screen.
It happens because UIViewController can control insets if his scrollView when the scrollView is appearing to allow lying of scrollView below transparent navigationBar and statusBar.
I've faced with this behaviour in iOS 9.1


This is working 100%

change the tableView.reloadData() 

into

tableView.reloadRows(at: tableView!.indexPathsForVisibleRows!, with: .none)

For it works fine

[tView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
             atScrollPosition:UITableViewScrollPositionTop
                     animated:NO];

참고URL : https://stackoverflow.com/questions/8640409/how-to-keep-uitableview-contentoffset-after-calling-reloaddata

반응형