UITableViewAutomaticDimension을 사용하여 UITableViewCell을 제자리에서 업데이트 한 후 불규칙한 스크롤
사용자가 제출 한 게시물에 대한 피드보기가있는 앱을 만들고 있습니다. 이보기에는 UITableView
사용자 정의 UITableViewCell
구현이 있습니다. 이 셀 안에는 UITableView
주석을 표시하기위한 다른 것이 있습니다 . 요점은 다음과 같습니다.
Feed TableView
PostCell
Comments (TableView)
CommentCell
PostCell
Comments (TableView)
CommentCell
CommentCell
CommentCell
CommentCell
CommentCell
초기 피드는 미리보기를 위해 3 개의 댓글과 함께 다운로드되지만 댓글이 더 있거나 사용자가 댓글을 추가 또는 삭제하는 경우 PostCell
내부 CommentCells
댓글 테이블을 추가하거나 제거하여 피드 테이블보기 내부에서 업데이트하고 싶습니다. 의 PostCell
. 나는 현재 다음 도우미를 사용하여이를 수행하고 있습니다.
// (PostCell.swift) Handle showing/hiding comments
func animateAddOrDeleteComments(startRow: Int, endRow: Int, operation: CellOperation) {
let table = self.superview?.superview as UITableView
// "table" is outer feed table
// self is the PostCell that is updating it's comments
// self.comments is UITableView for displaying comments inside of the PostCell
table.beginUpdates()
self.comments.beginUpdates()
// This function handles inserting/removing/reloading a range of comments
// so we build out an array of index paths for each row that needs updating
var indexPaths = [NSIndexPath]()
for var index = startRow; index <= endRow; index++ {
indexPaths.append(NSIndexPath(forRow: index, inSection: 0))
}
switch operation {
case .INSERT:
self.comments.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
case .DELETE:
self.comments.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
case .RELOAD:
self.comments.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
}
self.comments.endUpdates()
table.endUpdates()
// trigger a call to updateConstraints so that we can update the height constraint
// of the comments table to fit all of the comments
self.setNeedsUpdateConstraints()
}
override func updateConstraints() {
super.updateConstraints()
self.commentsHeight.constant = self.comments.sizeThatFits(UILayoutFittingCompressedSize).height
}
이것은 업데이트를 잘 수행합니다. 게시물은 PostCell
예상대로 내부에 추가되거나 제거 된 댓글로 업데이트 됩니다. PostCells
피드 테이블에서 자동 크기 조정 을 사용하고 있습니다. 의 주석 테이블이 PostCell
확장되어 모든 주석이 표시되지만 애니메이션이 약간 불안정하고 셀 업데이트 애니메이션이 발생하는 동안 테이블 종류가 12 픽셀 정도 위아래로 스크롤됩니다.
크기 조정 중 점프하는 것은 약간 짜증나지만 내 주요 문제는 나중에 발생합니다. 이제 피드에서 아래로 스크롤하면 이전과 같이 스크롤이 부드럽지만 댓글을 추가 한 후 크기를 조정 한 셀 위로 스크롤하면 피드가 피드의 맨 위에 도달하기 전에 몇 번 뒤로 건너 뜁니다. 다음 iOS8
과 같이 피드에 대한 자동 크기 조정 셀을 설정 했습니다.
// (FeedController.swift)
// tableView is the feed table containing PostCells
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.estimatedRowHeight = 560
을 제거하면 estimatedRowHeight
셀 높이가 변경 될 때마다 표가 맨 위로 스크롤됩니다. 나는 지금 이것에 꽤 집착하고 있고 새로운 iOS 개발자로서 당신이 가질 수있는 팁을 사용할 수 있다고 느낍니다.
다음은 이러한 종류의 문제를 해결하기 위해 찾은 최상의 솔루션입니다 (스크롤링 문제 + reloadRows + iOS 8 UITableViewAutomaticDimension);
tableView가 셀을 표시하므로 사전에 모든 높이를 유지하고 사전에서 업데이트하여 구성됩니다.
그런 다음 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
메서드에 저장된 높이를 반환합니다 .
다음과 같이 구현해야합니다.
목표 -C
- (void)viewDidLoad {
[super viewDidLoad];
self.heightAtIndexPath = [NSMutableDictionary new];
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber *height = [self.heightAtIndexPath objectForKey:indexPath];
if(height) {
return height.floatValue;
} else {
return UITableViewAutomaticDimension;
}
}
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSNumber *height = @(cell.frame.size.height);
[self.heightAtIndexPath setObject:height forKey:indexPath];
}
스위프트 3
@IBOutlet var tableView : UITableView?
var heightAtIndexPath = NSMutableDictionary()
override func viewDidLoad() {
super.viewDidLoad()
tableView?.rowHeight = UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber {
return CGFloat(height.floatValue)
} else {
return UITableViewAutomaticDimension
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let height = NSNumber(value: Float(cell.frame.size.height))
heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying)
}
우리도 같은 문제가있었습니다. 이는 SDK가 잘못된 높이를 강제로 적용하여 위로 스크롤 할 때 셀 점프를 유발하는 잘못된 셀 높이 추정에서 비롯됩니다. 셀을 구축 한 방법에 따라이 문제를 해결하는 가장 좋은 방법은 UITableViewDelegate
방법 을 구현하는 것입니다.- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
당신의 추정치가 셀 높이의 실제 값에 매우 가깝다면, 이것은 점프와 멍청함을 거의 제거 할 것입니다. 구현 방법은 다음과 같습니다. 논리를 얻을 수 있습니다.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
// This method will get your cell identifier based on your data
NSString *cellType = [self reuseIdentifierForIndexPath:indexPath];
if ([cellType isEqualToString:kFirstCellIdentifier])
return kFirstCellHeight;
else if ([cellType isEqualToString:kSecondCellIdentifier])
return kSecondCellHeight;
else if ([cellType isEqualToString:kThirdCellIdentifier])
return kThirdCellHeight;
else {
return UITableViewAutomaticDimension;
}
}
Swift 2 지원 추가
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
// This method will get your cell identifier based on your data
let cellType = reuseIdentifierForIndexPath(indexPath)
if cellType == kFirstCellIdentifier
return kFirstCellHeight
else if cellType == kSecondCellIdentifier
return kSecondCellHeight
else if cellType == kThirdCellIdentifier
return kThirdCellHeight
else
return UITableViewAutomaticDimension
}
dosdos 답변은 Swift 2 에서 나를 위해 일했습니다.
ivar 선언
var heightAtIndexPath = NSMutableDictionary()
func에서 viewDidLoad ()
func viewDidLoad() {
.... your code
self.tableView.rowHeight = UITableViewAutomaticDimension
}
그런 다음 다음 두 가지 방법을 추가하십시오.
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height = self.heightAtIndexPath.objectForKey(indexPath)
if ((height) != nil) {
return CGFloat(height!.floatValue)
} else {
return UITableViewAutomaticDimension
}
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let height = cell.frame.size.height
self.heightAtIndexPath.setObject(height, forKey: indexPath)
}
SWIFT 3 :
var heightAtIndexPath = [IndexPath: CGFloat]()
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return self.heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.heightAtIndexPath[indexPath] = cell.frame.size.height
}
나도 같은 문제에 직면했다. 해결 방법을 찾았지만 멍청이를 완전히 고치는 것은 아닙니다. 그러나 이전의 고르지 않은 스크롤에 비해 훨씬 나은 것 같습니다.
In your UITableView
delegate method :cellForRowAtIndexPath:
, try using the following two methods to update the constraints before returning the cell. (Swift language)
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
EDIT: You may also have to play around with the tableView.estimatedRowHeight
value to get a smoother scrolling.
Following @dosdos answer.
I also found interesting to implement: tableView(tableView: didEndDisplayingCell: forRowAtIndexPath:
Specially for my code, where the cell is changing Constraints dynamically while the cell is already displayed on screen. Updating the Dictionary like this helps the second time the cell is displayed.
var heightAtIndexPath = [NSIndexPath : NSNumber]()
....
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = UITableViewAutomaticDimension
....
extension TableViewViewController: UITableViewDelegate {
//MARK: - UITableViewDelegate
func tableView(tableView: UITableView,
estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height = heightAtIndexPath[indexPath]
if let height = height {
return CGFloat(height)
}
else {
return UITableViewAutomaticDimension
}
}
func tableView(tableView: UITableView,
willDisplayCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
let height: NSNumber = CGRectGetHeight(cell.frame)
heightAtIndexPath[indexPath] = height
}
func tableView(tableView: UITableView,
didEndDisplayingCell cell: UITableViewCell,
forRowAtIndexPath indexPath: NSIndexPath) {
let height: NSNumber = CGRectGetHeight(cell.frame)
heightAtIndexPath[indexPath] = height
}
}
@dosdos solution is working fine
but there is something you should added
following @dosdos answer
Swift 3/4
@IBOutlet var tableView : UITableView!
var heightAtIndexPath = NSMutableDictionary()
override func viewDidLoad() {
super.viewDidLoad()
tableView?.rowHeight = UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber {
return CGFloat(height.floatValue)
} else {
return UITableViewAutomaticDimension
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let height = NSNumber(value: Float(cell.frame.size.height))
heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying)
}
then use this lines when ever you want , for me I use it inside textDidChange
- first reload Tableview
- update constraint
finally move to top Tableview
tableView.reloadData() self.tableView.layoutIfNeeded() self.tableView.setContentOffset(CGPoint.zero, animated: true)
'Programing' 카테고리의 다른 글
Mongoose : 전체 사용자 목록 가져 오기 (0) | 2020.10.14 |
---|---|
ng 스타일을 사용하여 div 너비를 설정하는 방법 (0) | 2020.10.14 |
Angular 버전을 확인하는 방법은 무엇입니까? (0) | 2020.10.14 |
Uri에서 호스트 교체 (0) | 2020.10.14 |
Android 애플리케이션 ID를 얻는 방법은 무엇입니까? (0) | 2020.10.14 |