[ios] 동적 셀 높이를 가진 UITableView의 reloadData ()로 인해 스크롤이 급증합니다.

나는 이것이 일반적인 문제라고 생각하고 그것에 대한 일반적인 해결책이 있는지 궁금합니다.

기본적으로 내 UITableView에는 모든 셀에 대한 동적 셀 높이가 있습니다. UITableView 및 I의 맨 위에 있지 않으면 위로 tableView.reloadData()스크롤이 급증합니다.

나는 이것이 데이터를 다시로드했기 때문에 스크롤 할 때 UITableView가 가시성에 도달하는 각 셀의 높이를 다시 계산한다는 사실 때문이라고 생각합니다. 이를 완화하는 방법 또는 특정 IndexPath에서 UITableView의 끝까지 만 데이터를 다시로드하는 방법은 무엇입니까?

또한 맨 위로 스크롤 할 때 점프하지 않아도 문제가 없습니다. UITableViewCell 높이가 이미 계산 되었기 때문일 수 있습니다.



답변

점프를 방지하기 위해 셀이로드 될 때 셀의 높이를 저장하고 정확한 값을 제공해야합니다 tableView:estimatedHeightForRowAtIndexPath.

빠른:

var cellHeights = [IndexPath: CGFloat]()

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

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? UITableView.automaticDimension
}

목표 C :

// declare cellHeightsDictionary
NSMutableDictionary *cellHeightsDictionary = @{}.mutableCopy;

// declare table dynamic row height and create correct constraints in cells
tableView.rowHeight = UITableViewAutomaticDimension;

// save height
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    [cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:indexPath];
}

// give exact height value
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
    if (height) return height.doubleValue;
    return UITableViewAutomaticDimension;
}


답변

수락 된 답변의 신속한 3 버전.

var cellHeights: [IndexPath : CGFloat] = [:]


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

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0
}


답변

점프는 예상 높이가 잘못 되었기 때문입니다. 예상 RowHeight가 실제 높이와 다를수록 테이블을 다시로드 할 때 특히 테이블이 더 아래로 스크롤 될 때 테이블이 더 많이 점프 할 수 있습니다. 테이블의 예상 크기가 실제 크기와 크게 다르기 때문에 테이블의 내용 크기와 오프셋을 조정해야하기 때문입니다. 따라서 추정 높이는 임의의 값이 아니라 높이가 될 것으로 생각되는 것과 비슷해야합니다. UITableViewAutomaticDimension
당신의 세포가 같은 유형인지 설정했을 때도 경험했습니다.

func viewDidLoad() {
     super.viewDidLoad()
     tableView.estimatedRowHeight = 100//close to your cell height
}

다른 섹션에 다양한 셀이 있다면 더 좋은 곳은

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
     //return different sizes for different cells if you need to
     return 100
}


답변

이 경우 @Igor 답변이 제대로 작동Swift-4합니다.

// declaration & initialization
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]  

다음과 같은 방법으로 UITableViewDelegate

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  // print("Cell height: \(cell.frame.size.height)")
  self.cellHeightsDictionary[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
  if let height =  self.cellHeightsDictionary[indexPath] {
    return height
  }
  return UITableView.automaticDimension
}


답변

위의 모든 해결 방법을 시도했지만 아무 효과가 없습니다.

몇 시간을 보내고 가능한 모든 좌절을 겪고 나서 이것을 고치는 방법을 알아 냈습니다. 이 솔루션은 구세주입니다! 매력처럼 일했다!

스위프트 4

let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)

코드를 깔끔하게 보이게하고 다시로드 할 때 마다이 모든 줄을 쓰지 않도록 확장 기능으로 추가했습니다.

extension UITableView {

    func reloadWithoutAnimation() {
        let lastScrollOffset = contentOffset
        beginUpdates()
        endUpdates()
        layer.removeAllAnimations()
        setContentOffset(lastScrollOffset, animated: false)
    }
}

드디어 ..

tableView.reloadWithoutAnimation()

또는 실제로 UITableViewCell awakeFromNib()메소드에 이러한 행을 추가 할 수 있습니다

layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale

그리고 정상적으로 reloadData()


답변

더 많은 방법으로 문제를 해결합니다.

뷰 컨트롤러의 경우 :

var cellHeights: [IndexPath : CGFloat] = [:]


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

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0
}

UITableView의 확장으로

extension UITableView {
  func reloadSectionWithouAnimation(section: Int) {
      UIView.performWithoutAnimation {
          let offset = self.contentOffset
          self.reloadSections(IndexSet(integer: section), with: .none)
          self.contentOffset = offset
      }
  }
}

결과는

tableView.reloadSectionWithouAnimation(section: indexPath.section)


답변

나는 오늘 이것을 만났고 관찰했다.

  1. 실제로 iOS 8 전용입니다.
  2. 재정의 cellForRowAtIndexPath는 도움이되지 않습니다.

수정은 실제로 매우 간단했습니다.

재정의 estimatedHeightForRowAtIndexPath하고 올바른 값을 반환하는지 확인하십시오.

이로 인해 UITableViews에서 모든 이상한 지터와 점프가 중단되었습니다.

참고 : 실제로 셀 크기를 알고 있습니다. 가능한 값은 두 가지뿐입니다. 셀이 실제로 가변 크기 인 경우 cell.bounds.size.heightfrom 을 캐시 할 수 있습니다tableView:willDisplayCell:forRowAtIndexPath: