Programing

UITableViewAutomaticDimension을 사용하여 UITableViewCell을 제자리에서 업데이트 한 후 불규칙한 스크롤

lottogame 2020. 10. 14. 07:21
반응형

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

  1. first reload Tableview
  2. update constraint
  3. finally move to top Tableview

    tableView.reloadData()
    self.tableView.layoutIfNeeded()
    self.tableView.setContentOffset(CGPoint.zero, animated: true)
    

참고URL : https://stackoverflow.com/questions/27996438/jerky-scrolling-after-updating-uitableviewcell-in-place-with-uitableviewautomati

반응형