Nice programing

iOS 8 자동 셀 높이-마지막 행으로 스크롤 할 수 없음

nicepro 2020. 12. 25. 22:58
반응형

iOS 8 자동 셀 높이-마지막 행으로 스크롤 할 수 없음


iOS 8의 새로운 자체 크기 조정 셀을 사용하고 있습니다. 시각적으로 잘 작동합니다. 각 셀은 적절한 크기를 갖습니다. 그러나 마지막 행 으로 스크롤 하려고 하면 테이블보기가 올바른 크기를 알지 못하는 것 같습니다. 이것은 버그입니까, 아니면 수정 사항이 있습니까?

문제를 재현하는 방법은 다음과 같습니다.

이 프로젝트 -TableViewCellWithAutoLayoutiOS8 ( 이 SO 답변 에서 참조)을 사용하여 예상대로 자동 크기 조정 셀을 얻었습니다.

그러나 다음과 같이 scrollToRowAtIndexPath 함수를 호출하는 경우 :

tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)

나는 마지막 줄에 도착하지 않습니다 .

다음과 같이 낮은 수준의 기능을 사용하려고해도 :

tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true)

결과는 예상과 다르며 끝까지 도달하지 않습니다. 여러 번 클릭하거나 잠시 기다리면 결국 올바른 위치로 이동합니다. tableView.contentSize.height가 올바르게 설정되지 않은 것 같습니다. 그래서 iOS는 마지막 셀이 어디에 있는지 "모릅니다".

도움을 주시면 감사하겠습니다.

감사


업데이트 : 2015 년 6 월 24 일

Apple은 iOS 9.0 SDK에서 이러한 버그의 대부분을 해결했습니다. 애니메이션없이 테이블보기의 상단 및 하단으로 reloadData스크롤하고 테이블보기 중간에서 스크롤 하는 동안 호출 하는 등 모든 문제가 iOS 9 베타 2부터 수정되었습니다 .

아직 수정되지 않은 나머지 문제는 다음과 같습니다.

  1. 큰 예상 행 높이를 사용하는 경우 애니메이션이있는 마지막 행으로 스크롤하면 테이블보기 셀이 사라집니다.
  2. 작은 예상 행 높이를 사용하는 경우 애니메이션이있는 마지막 행으로 스크롤하면 테이블보기가 너무 일찍 스크롤을 완료하여 일부 셀이 가시 영역 아래 (그리고 마지막 행은 여전히 ​​화면을 벗어남)에 남게됩니다.

애니메이션 스크롤과 관련된 이러한 문제에 대한 새로운 버그 보고서 (rdar : // 21539211)가 제출되었습니다.

원래 답변

이것은 테이블보기 행 높이 추정 기능이있는 Apple 버그이며,이 기능이 iOS 7에서 처음 도입 된 이후 존재했습니다. 저는이 문제에 대해 Apple UIKit 엔지니어 및 개발자 전도자와 직접 작업했습니다. 버그이지만 신뢰할 수있는 해결 방법이 없으며 (행 높이 추정을 비활성화하는 것보다 부족함) 특별히 수정하는 데 관심이없는 것 같습니다.

버그는 reloadData부분적으로 또는 완전히 아래로 스크롤 한 상태에서 호출하면 테이블 뷰 셀이 사라지는 것과 같은 다른 방식으로 나타납니다 (예 : contentOffset.y0보다 훨씬 큼).

분명히 iOS 8 자체 크기 조정 셀에서는 행 높이 추정이 매우 중요하므로 Apple은이 문제를 최대한 빨리 해결해야합니다.

이 문제는 2013 년 10 월 21 일에 Radar # 15283329로 다시 제출했습니다. Apple이 수정에 우선 순위를 부여 할 수 있도록 중복 버그 보고서를 제출하십시오.

이 간단한 샘플 프로젝트첨부 하여 문제를 보여줄 수 있습니다 . Apple의 자체 샘플 코드를 직접 기반으로합니다.


이것은 매우 성가신 버그 였지만 이유를 완전히 설명 할 수는 없지만 영구적 인 해결책을 찾은 것 같습니다.

약간의 (눈에 띄지 않는) 지연 후에 함수를 호출합니다.

let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

dispatch_after(time, dispatch_get_main_queue(), {
  tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true)
})

이것이 당신에게도 효과가 있는지 말해주십시오.


확실히 Apple의 버그입니다. 나는 또한이 문제가 있습니다. "scrollToRowAtIndexPath"메서드를 두 번 호출하여이 문제를 해결했습니다. 예제 코드는 다음과 같습니다.

        if array.count > 0 {
        let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0)
        self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        let delay = 0.1 * Double(NSEC_PER_SEC)
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

        dispatch_after(time, dispatch_get_main_queue(), {
            self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
        })
    }

Apple이 우리를 괴롭히는 많은 버그를 수정하기로 결정할 때까지 도움이 될 수있는 임시 해결 방법을 찾았습니다.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *text = [self findTextForIndexPath:indexPath];
    UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13];
    CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT)
                                                options:NSStringDrawingUsesLineFragmentOrigin
                                             attributes:@{NSFontAttributeName: font}
                                                context:nil];
    return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING;
}

이것은 완벽하지는 않지만 나를 위해 일했습니다. 이제 다음과 같이 전화 할 수 있습니다.

- (void)scrollToLastestSeenMessageAnimated:(BOOL)animated
{
    NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0];
    if (count > 0) {
        NSInteger lastPos = MAX(0, count-1);
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

켜고 viewDidLayoutSubviews바닥에서 올바른 위치 (또는 매우 가까운 예상 위치)를 찾습니다.

도움이 되었기를 바랍니다.


제 경우에는 예상 셀 높이를 프로그램에 제안하지 않음으로써 임시 해결 방법을 찾았습니다. 내 코드에서 다음 방법을 주석으로 처리했습니다.

- (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

그러나 셀이 서로 비교하여 많이 다를 경우 사용자가 스크롤 할 때 사용자 경험에 영향을 미칠 수 있습니다. 제 경우에는 지금까지 눈에 띄는 차이가 없습니다.

도움이 되었기를 바랍니다.


내 해결책은 스토리 보드의 크기를 추정치로 사용하는 것이 었습니다.

그래서이 대신 :

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

}

나는 다음과 같이했다.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 

MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath];

switch (messageType) {

    case MyMessageTypeText:
        return 45;
        break;

    case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText:
        return 96;
        break;

    default:
        break;
 }
}

채팅 테이블보기를 작성 중이므로 많은 셀, 특히 해당 텍스트 유형이 IB에있는 것보다 클 가능성이 높습니다. 특히 채팅 메시지가 매우 긴 경우 더욱 그렇습니다. 이것은 꽤 좋은 것 같습니다 ... 음 ... 추정하고 아래로 스크롤하면 꽤 가까워집니다. 스크롤링이 길어질수록 약간 더 나빠지는 것 같지만 예상 할 수 있습니다.


viewDidAppear가 문제를 해결할 수있는 후에 tableview reloadData를 호출하기 만하면됩니다.

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}

smileyborg의 대답 으로 iOS 8.x의 버그 이지만 지원하는 모든 플랫폼에서 수정해야합니다 ...

iOS9 이전의 문제를 해결하려면 아래 코드에서 dispatch_async 또는 dispatch_after없이 트릭을 수행하십시오. iOS 8.4 시뮬레이터에서 테스트되었습니다.

업데이트 : 스크롤되는 UIPageViewController에 의해 뷰 컨트롤러가 표시되면 layoutIfNeeded 호출 (전용)이 작동하지 않습니다. 따라서 layoutSubviews (또는 setNeedsLayout + layoutIfNeeded)를 대신 사용하십시오.

// For iOS 8 bug workaround.
// See https://stackoverflow.com/a/33515872/1474113
- (void)scrollToBottomForPreiOS9
{
    CGFloat originalY, scrolledY;
    do {
        // Lay out visible cells immediately for current contentOffset.
        // NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged.
        [self.tableView layoutSubviews];
        originalY = self.tableView.contentOffset.y;
        [self scrollToBottom];  // Call -scrollToRowAtIndexPath as usual.
        scrolledY = self.tableView.contentOffset.y;
    } while (scrolledY > originalY);
}

셀 높이가 다른 채팅 tableView를 만들 때 동일한 문제가 발생했습니다. viewDidAppear () 수명주기 메서드에서 아래 코드를 호출합니다.

// First figure out how many sections there are
let lastSectionIndex = self.tableView.numberOfSections - 1

// Then grab the number of rows in the last section
let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1

// Now just construct the index path
let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex)

// Make the last row visible
self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true)

그것이 당신에게도 효과가 있는지 알려주십시오.


스토리 보드 창에서 빈 영역을 클릭하여 모든보기를 선택 취소 한 다음 테이블보기가있는보기를 클릭 한 다음 Resolve Auto Layout Issue아이콘 을 클릭하고Reset to Suggested Constraints

여기에 이미지 설명 입력 여기에 이미지 설명 입력


이 간단한 코드를 사용하여 아래로 스크롤하십시오.

 var rows:NSInteger=self.tableName.numberOfRowsInSection(0)
        if(rows > 0)
        {
            let indexPath = NSIndexPath(forRow: rows-1, inSection: 0)
            tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition:  UITableViewScrollPosition.Bottom, animated: true)
        }
            }

참조 URL : https://stackoverflow.com/questions/25686490/ios-8-auto-cell-height-cant-scroll-to-last-row

반응형