동적 셀 높이가 있는 UITableView의 reloadData()는 점프 스크롤을 유발합니다.
저는 이것이 공통적인 문제일 수도 있다고 생각하고 공통적인 해결책이 있는지 궁금합니다.
기본적으로, 내 UITableView에는 모든 셀에 대해 동적 셀 높이가 있습니다.및 IUI의 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
}
그 점프는 높이가 잘못 추정되었기 때문입니다.예상되는 Row 행 많 수 을 록 가 상 될 때 아래쪽으로 때 더 할 수 .높이가 실제 높이와 다를수록 테이블이 다시 로드될 때 더 많이 점프할 수 있습니다. 특히 테이블을 아래로 스크롤할수록 더 많이 점프할 수 있습니다.이는 테이블의 예상 크기가 실제 크기와 크게 달라 테이블의 내용 크기 및 오프셋을 조정해야 하기 때문입니다.따라서 예상되는 높이는 임의의 값이 아니라 여러분이 생각하는 높이에 가까워야 합니다.나는 또한 내가 설정했을 때 경험했습니다.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 reloadSectionWithoutAnimation(section: Int) {
UIView.performWithoutAnimation {
let offset = self.contentOffset
self.reloadSections(IndexSet(integer: section), with: .none)
self.contentOffset = offset
}
}
}
결과는
tableView.reloadSectionWithoutAnimation(section: indexPath.section)
저는 오늘 이것을 우연히 만나 다음을 관찰했습니다.
- iOS 8 뿐입니다.
- 오이드
cellForRowAtIndexPath
도움이 되지 않습니다.
해결책은 사실 매우 간단했습니다.
재정의estimatedHeightForRowAtIndexPath
올바른 값을 반환하는지 확인합니다.
이것으로, 내 UITableViews에서 이상한 지렛대와 뛰어다니는 모든 것이 멈췄습니다.
참고: 저는 사실 제 세포의 크기를 알고 있습니다.가능한 값은 두 개뿐입니다.셀이 정말로 가변 크기라면, 당신은 캐시할 수 있습니다.cell.bounds.size.height
tableView:willDisplayCell:forRowAtIndexPath:
는 로제다음사특정행만다있수다로습니를 사용하여 특정 로드할 수 .reloadRowsAtIndexPaths
ex:
tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)
그러나 일반적으로 다음과 같이 테이블 셀 높이 변경을 애니메이션으로 만들 수도 있습니다.
tableView.beginUpdates()
tableView.endUpdates()
추정치 재정의행 높이(위치값이 높은 IndexPath 메서드(예: 300f)
이것으로 문제가 해결될 것입니다 :)
다음은 좀 더 짧은 버전입니다.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.cellHeightsDictionary[indexPath] ?? UITableViewAutomaticDimension
}
iOS11에 도입된 버그가 있습니다.
그때가 당신이 할 때입니다.reload
contentOffSet
예기치 않게 변경됩니다.은 은실.contentOffset
다시 로드한 후에는 변경되지 않아야 합니다.계산 착오로 인해 발생하는 경향이 있습니다.UITableViewAutomaticDimension
은 당신의 돈을 .contentOffSet
다시 로드가 완료된 후 저장된 값으로 되돌립니다.
func reloadTableOnMain(with offset: CGPoint = CGPoint.zero){
DispatchQueue.main.async { [weak self] () in
self?.tableView.reloadData()
self?.tableView.layoutIfNeeded()
self?.tableView.contentOffset = offset
}
}
어떻게 사용하나요?
someFunctionThatMakesChangesToYourDatasource()
let offset = tableview.contentOffset
reloadTableOnMain(with: offset)
이 답변은 여기서 도출되었습니다.
Swift4에서 저에게 도움이 된 것은 다음과 같습니다.
extension UITableView {
func reloadWithoutAnimation() {
let lastScrollOffset = contentOffset
reloadData()
layoutIfNeeded()
setContentOffset(lastScrollOffset, animated: false)
}
}
이 문제를 해결하기 위한 접근법 중 하나는
CATransaction.begin()
UIView.setAnimationsEnabled(false)
CATransaction.setCompletionBlock {
UIView.setAnimationsEnabled(true)
}
tableView.reloadSections([indexPath.section], with: .none)
CATransaction.commit()
이 해결책들 중 어느 것도 저에게 효과가 없었습니다.스위프트 4와 X코드 10.1을 사용해 본 결과는 다음과 같습니다.
viewDidLoad()에서 테이블 동적 행 높이를 선언하고 셀에 올바른 제약 조건을 만듭니다...
tableView.rowHeight = UITableView.automaticDimension
또한 ViewDidLoad()에서 모든 테이블을 등록합니다. 다음과 같이 테이블 뷰에 셀 니브를 표시합니다.
tableView.register(UINib(nibName: "YourTableViewCell", bundle: nil), forCellReuseIdentifier: "YourTableViewCell")
tableView.register(UINib(nibName: "YourSecondTableViewCell", bundle: nil), forCellReuseIdentifier: "YourSecondTableViewCell")
tableView.register(UINib(nibName: "YourThirdTableViewCell", bundle: nil), forCellReuseIdentifier: "YourThirdTableViewCell")
표의 View heightForRowAt에서 indexPath.row에서 각 셀의 높이와 동일한 반환 높이...
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
let cell = Bundle.main.loadNibNamed("YourTableViewCell", owner: self, options: nil)?.first as! YourTableViewCell
return cell.layer.frame.height
} else if indexPath.row == 1 {
let cell = Bundle.main.loadNibNamed("YourSecondTableViewCell", owner: self, options: nil)?.first as! YourSecondTableViewCell
return cell.layer.frame.height
} else {
let cell = Bundle.main.loadNibNamed("YourThirdTableViewCell", owner: self, options: nil)?.first as! YourThirdTableViewCell
return cell.layer.frame.height
}
}
이제 표의 각 셀에 대한 예상 행 높이를 지정합니다. 추정 보기행 높이:가능한 한 정확하게...
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0 {
return 400 // or whatever YourTableViewCell's height is
} else if indexPath.row == 1 {
return 231 // or whatever YourSecondTableViewCell's height is
} else {
return 216 // or whatever YourThirdTableViewCell's height is
}
}
그게 효과가 있을 겁니다
tableView.reloadData()를 호출할 때 contentOffset을 저장하고 설정할 필요가 없었습니다.
저는 두 개의 다른 세포 높이를 가지고 있습니다.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
견적을 추가한 후높이ForRowAt, 점프는 더 이상 없었습니다.
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
나에게 작업 해결책은
UIView.setAnimationsEnabled(false)
tableView.performBatchUpdates { [weak self] in
self?.tableView.reloadRows(at: [indexPath], with: .none)
} completion: { [weak self] _ in
UIView.setAnimationsEnabled(true)
self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) // remove if you don't need to scroll
}
확장 가능한 세포를 가지고 있습니다.
통화시에 전화해 .cell.layoutSubviews()
방으로 돌전기에가아감에서 셀을 에.func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?
iOS8에서 알려진 버그입니다.
다음을 사용할 수 있습니다.ViewDidLoad()
tableView.estimatedRowHeight = 0 // if have just tableViewCells <br/>
// use this if you have tableview Header/footer <br/>
tableView.estimatedSectionFooterHeight = 0 <br/>
tableView.estimatedSectionHeaderHeight = 0
이러한 점프 동작이 있었고 처음에는 정확한 예상 헤더 높이를 설정하여 완화할 수 있었지만(헤더 뷰가 하나밖에 없었기 때문에) 점프가 헤더 내부에서 발생하기 시작하여 더 이상 전체 테이블에 영향을 주지 않았습니다.
여기에 있는 답변을 따라 애니메이션과 관련이 있다는 단서를 얻었습니다. 그래서 테이블 뷰가 스택 뷰 안에 있다는 것을 알게 되었고, 때때로 우리는 전화를 했습니다.stackView.layoutIfNeeded()
애니메이션 블록 안에.저의 마지막 해결책은 레이아웃이 "필요하지 않은 경우"에도 해당 상황에서 시각적 동작을 수행하기 때문에 "필요한 경우"가 아닌 한 이 상담이 발생하지 않도록 하는 것이었습니다.
저도 같은 문제가 있었습니다.애니메이션 없이 페이지를 만들고 데이터를 다시 로드했지만 스크롤이 점프하는 것을 방지하는 데 도움이 되지 않았습니다.저는 다른 크기의 IP폰을 가지고 있습니다, iphone8에서는 스크롤이 뛰지 않았지만 iphone7+에서는 뛰었습니다.
viewDidLoad 기능에 다음과 같은 변경 사항을 적용했습니다.
self.myTableView.estimatedRowHeight = 0.0
self.myTableView.estimatedSectionFooterHeight = 0
self.myTableView.estimatedSectionHeaderHeight = 0
그리고 내 문제는 해결되었습니다.당신에게도 도움이 되길 바랍니다.
저는 "hightForRowAt"에서 작동했습니다.
extension APICallURLSessionViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
print("Inside heightForRowAt")
return 130.50
}
}
사실은 당신이 사용한다면 찾았습니다.reloadRows
점프 문제를 일으킵니다.그렇다면 당신은 사용해봐야 합니다.reloadSections
다음과 같이:
UIView.performWithoutAnimation {
tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
}
언급URL : https://stackoverflow.com/questions/28244475/reloaddata-of-uitableview-with-dynamic-cell-heights-causes-jumpy-scrolling
'programing' 카테고리의 다른 글
git 특정 하위 폴더에 있는 파일을 제외한 특정 유형의 모든 파일을 무시합니다. (0) | 2023.05.23 |
---|---|
mongodb를 내장형 데이터베이스로 사용할 수 있습니까? (0) | 2023.05.23 |
Bash를 사용하여 명령 출력을 열로 분할하시겠습니까? (0) | 2023.05.23 |
오류 /node_hostname/node-hostname:명령 실패 (0) | 2023.05.23 |
grep에서 반환되는 결과 수를 제한하려면 어떻게 해야 합니까? (0) | 2023.05.23 |