상단에 행을 삽입 할 때 uitableview를 정적으로 유지
맨 위에 행을 삽입하는 tableView가 있습니다.
이 작업을 수행하는 동안 현재보기가 완전히 가만히 있기를 원하므로 위로 스크롤하면 행이 나타납니다.
기본 UIScrollview의 현재 위치를 저장하고 행이 삽입 된 후 위치를 재설정하려고 시도했지만 동일한 위치에서 다시 끝날지라도 위아래로 흔들림이 발생합니다.
이것을 달성하는 좋은 방법이 있습니까?
업데이트 : beginUpdate를 사용하고 나서 insertRowsAtIndexPath, endUpdates를 사용하고 있습니다. reloadData 호출이 없습니다.
scrollToRowAtIndexPath는 현재 셀의 맨 위로 이동합니다 (행을 추가하기 전에 저장 됨).
내가 시도한 다른 접근 방식은 정확히 올바른 속도로 끝나지만 저더가 있습니다.
save tableView currentOffset. (Underlying scrollView method)
Add rows (beginUpdates,insert...,endUpdates)
reloadData ( to force a recalulation of the scrollview size )
Recalculate the correct new offset from the bottom of the scrollview
setContentOffset (Underlying scrollview method)
문제는 reloadData로 인해 scrollview / tableview가 잠시 스크롤을 시작한 다음 setContentOffset이 올바른 위치로 반환합니다.
디스플레이를 시작하지 않고 새로운 크기를 해결하기 위해 tableView를 얻는 방법이 있습니까?
beginAnimation commitAnimation에서 모든 것을 래핑하는 것도 큰 도움이되지 않습니다.
업데이트 2 : 이것은 명확하게 수행 할 수 있습니다. 업데이트를 위해 아래로 내리면 공식 트위터 앱을 참조하십시오.
실제로 모든 행 높이를 합산 할 필요가 없습니다. 테이블을 다시로드 한 후 새로운 contentSize가 이미이를 나타냅니다. 따라서해야 할 일은 contentSize 높이의 델타를 계산하고 현재 오프셋에 추가하는 것입니다.
...
CGSize beforeContentSize = self.tableView.contentSize;
[self.tableView reloadData];
CGSize afterContentSize = self.tableView.contentSize;
CGPoint afterContentOffset = self.tableView.contentOffset;
CGPoint newContentOffset = CGPointMake(afterContentOffset.x, afterContentOffset.y + afterContentSize.height - beforeContentSize.height);
self.tableView.contentOffset = newContentOffset;
...
-(void) updateTableWithNewRowCount : (int) rowCount
{
//Save the tableview content offset
CGPoint tableViewOffset = [self.tableView contentOffset];
//Turn of animations for the update block
//to get the effect of adding rows on top of TableView
[UIView setAnimationsEnabled:NO];
[self.tableView beginUpdates];
NSMutableArray *rowsInsertIndexPath = [[NSMutableArray alloc] init];
int heightForNewRows = 0;
for (NSInteger i = 0; i < rowCount; i++) {
NSIndexPath *tempIndexPath = [NSIndexPath indexPathForRow:i inSection:SECTION_TO_INSERT];
[rowsInsertIndexPath addObject:tempIndexPath];
heightForNewRows = heightForNewRows + [self heightForCellAtIndexPath:tempIndexPath];
}
[self.tableView insertRowsAtIndexPaths:rowsInsertIndexPath withRowAnimation:UITableViewRowAnimationNone];
tableViewOffset.y += heightForNewRows;
[self.tableView endUpdates];
[UIView setAnimationsEnabled:YES];
[self.tableView setContentOffset:tableViewOffset animated:NO];
}
-(int) heightForCellAtIndexPath: (NSIndexPath *) indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
int cellHeight = cell.frame.size.height;
return cellHeight;
}
상단에 삽입 할 새 행의 행 수를 전달하기 만하면됩니다.
@Dean의 이미지 캐시 사용 방법은 너무 엉망이며 UI의 응답 성을 파괴한다고 생각합니다.
한 가지 적절한 방법은 UITableView 하위 클래스를 사용하고 -setContentSize : 를 재정의하는 것입니다.이 방법을 사용하면 contentOffset 을 설정하여 테이블 뷰가 얼마나 아래로 내려 오는지 계산하고 오프셋 할 수 있습니다.
다음은 모든 삽입이 테이블 뷰 상단에서 발생하는 가장 간단한 상황을 처리하는 가장 간단한 샘플 코드입니다.
@implementation MyTableView
- (void)setContentSize:(CGSize)contentSize {
// I don't want move the table view during its initial loading of content.
if (!CGSizeEqualToSize(self.contentSize, CGSizeZero)) {
if (contentSize.height > self.contentSize.height) {
CGPoint offset = self.contentOffset;
offset.y += (contentSize.height - self.contentSize.height);
self.contentOffset = offset;
}
}
[super setContentSize:contentSize];
}
@end
같은 문제가 있었고 해결책을 찾았습니다.
save tableView currentOffset. (Underlying scrollView method)
//Add rows (beginUpdates,insert...,endUpdates) // don't do this!
reloadData ( to force a recalulation of the scrollview size )
add newly inserted row heights to contentOffset.y here, using tableView:heightForRowAtIndexPath:
setContentOffset (Underlying scrollview method)
이렇게 :
- (CGFloat) firstRowHeight
{
return [self tableView:[self tableView] heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
...
CGPoint offset = [[self tableView] contentOffset];
[self tableView] reloadData];
offset.y += [self firstRowHeight];
if (offset.y > [[self tableView] contentSize].height) {
offset.y = 0;
}
[[self tableView] setContentOffset:offset];
...
결함없이 완벽하게 작동합니다.
핵심 데이터 샘플 프로젝트로 몇 가지 테스트를 수행하고 새 셀이 보이는 맨 위에있는 셀 위에 추가되는 동안 그대로 두었습니다. 이 코드는 화면에 빈 공간이있는 테이블을 조정해야하지만 일단 화면이 채워지면 제대로 작동합니다.
static CGPoint delayOffset = {0.0};
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller {
if ( animateChanges )
[self.tableView beginUpdates];
delayOffset = self.tableView.contentOffset; // get the current scroll setting
}
셀 삽입 지점에 추가했습니다. 셀 삭제를 위해 상대 뺄셈을 할 수 있습니다.
case NSFetchedResultsChangeInsert:
delayOffset.y += self.tableView.rowHeight; // add for each new row
if ( animateChanges )
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
break;
그리고 마지막으로
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
if ( animateChanges )
{
[self.tableView setContentOffset:delayOffset animated:YES];
[self.tableView endUpdates];
}
else
{
[self.tableView reloadData];
[self.tableView setContentOffset:delayOffset animated:NO];
}
}
animateChanges = NO를 사용하면 셀이 추가 될 때 움직이는 것을 볼 수 없습니다.
animateChanges = YES로 테스트 할 때 "저더"가있었습니다. 셀 삽입 애니메이션은 애니메이션 테이블 스크롤과 같은 속도가 없었던 것 같습니다. 끝의 결과는 시작된 위치에서 정확히 보이는 셀로 끝날 수 있지만 전체 테이블은 2 또는 3 픽셀 이동 한 다음 뒤로 이동합니다.
애니메이션 속도를 동일하게 만들 수 있다면 그대로있는 것처럼 보일 수 있습니다.
그러나 이전 애니메이션이 끝나기 전에 행을 추가하기 위해 버튼을 누르면 갑자기 애니메이션이 중지되고 다음 애니메이션이 시작되어 갑자기 위치가 변경됩니다.
@학장,
애니메이션을 방지하기 위해 이와 같이 코드를 변경할 수 있습니다.
[tableView beginUpdates];
[UIView setAnimationsEnabled:NO];
// ...
[tableView endUpdates];
[tableView setContentOffset:newOffset animated:NO];
[UIView setAnimationsEnabled:YES];
모두가 코드 예제 복사 및 붙여 넣기를 좋아하므로 다음은 Andrey Z.의 답변을 구현 한 것입니다.
이것은 내 delegateDidFinishUpdating:(MyDataSourceDelegate*)delegate방법입니다
if (self.contentOffset.y <= 0)
{
[self beginUpdates];
[self insertRowsAtIndexPaths:insertedIndexPaths withRowAnimation:insertAnimation];
[self endUpdates];
}
else
{
CGPoint newContentOffset = self.contentOffset;
[self reloadData];
for (NSIndexPath *indexPath in insertedIndexPaths)
newContentOffset.y += [self.delegate tableView:self heightForRowAtIndexPath:indexPath];
[self setContentOffset:newContentOffset];
NSLog(@"New data at top of table view");
}
NSLog하단에 신선한 데이터가 거기에 표시된보기를 표시하기 위해 전화로 대체 될 수있다.
사용자 지정 그룹화로 인해 -reloadData 호출간에 행 수가 다를 수있는 섹션이 많고 행 높이가 다른 상황에 직면했습니다. 그래서 여기에 AndreyZ를 기반으로 한 솔루션이 있습니다. -reloadData 전후의 UIScrollView의 contentHeight 속성이며 더 보편적 인 것처럼 보입니다.
CGFloat contentHeight = self.tableView.contentSize.height;
CGPoint offset = self.tableView.contentOffset;
[self.tableView reloadData];
offset.y += (self.tableView.contentSize.height - contentHeight);
if (offset.y > [self.tableView contentSize].height)
offset.y = 0;
[self.tableView setContentOffset:offset];
추가 조건을 추가하고 싶습니다. iOS11 이상의 코드 인 경우 아래와 같이해야합니다.
iOS 11에서 테이블보기는 기본적으로 예상 높이를 사용합니다. 이는 contentSize가 초기에 예상 된 값과 동일 함을 의미합니다. contentSize를 사용해야하는 경우 3 개의 예상 높이 속성을 0으로 설정하여 예상 높이를 비활성화 할 수 있습니다.
tableView.estimatedRowHeight = 0 tableView.estimatedSectionHeaderHeight = 0 tableView.estimatedSectionFooterHeight = 0
How are you adding the rows to the table?
If you're changing the data source and then calling reloadData, that may result in the table being scrolled to the top again.
However, if you use the beginUpdates, insertRowsAtIndexPaths:withRowAnimation:, endUpdates methods, you should be able to insert rows without having to call reloadData thus keeping the table in its original position.
Don't forget to modify your data source before calling endUpdates or else you'll end up with an internal inconsistency exception.
You don't need to do so much difficult operations, furthermore these manipulations wouldn't work perfectly. The simple solution is to rotate table view, and then rotate cells into it.
tableView.transform = CGAffineTransformMakeRotation(M_PI);
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.transform = CGAffineTransformMakeRotation(M_PI);
}
Use [tableView setScrollIndicatorInsets:UIEdgeInsetsMake(0, 0, 0, 310)] to set relative position to scroll indicator. It will be on the right side after you table view rotation.
Just a heads up it does not seem possible to do this if you return estimated heights for the tableview.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath ;
If you implement this method and return a rough height your tableview will jump about when reloading as it appears to use these heights when setting the offsets.
To get it working use one of the above answers (I went with @Mayank Yadav answer), don't implement the estimatedHeight method and cache the cell heights (remembering to adjust the cache when you insert additional cells at the top).
Simple solution to disable animations
func addNewRows(indexPaths: [NSIndexPath]) {
let addBlock = { () -> Void in
self.tableView.beginUpdates()
self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .None)
self.tableView.endUpdates()
}
tableView.contentOffset.y >= tableView.height() ? UIView.performWithoutAnimation(addBlock) : addBlock()
}
I solved this in the end by rendering the current tableview into a UIImage and then putting a temporary UIImageView over the tableview whilst it animates.
The following code will generate the image
// Save the current tableView as an UIImage
CSize pageSize = [[self tableView] frame].size;
UIGraphicsBeginImageContextWithOptions(pageSize, YES, 0.0); // 0.0 means scale appropriate for device ( retina or no )
CGContextRef resizedContext = UIGraphicsGetCurrentContext();
CGPoint offset = [[self tableView] contentOffset];
CGContextTranslateCTM(resizedContext,-(offset.x),-(offset.y));
[[[self tableView ]layer] renderInContext:resizedContext];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You need to keep track of how much the tableview will have grown by whilst inserting rows and make sure you scroll the tableview back to the exact same position.
Based on Andrey Z's answer, here is a live example working perfect for me...
int numberOfRowsBeforeUpdate = [controller.tableView numberOfRowsInSection:0];
CGPoint currentOffset = controller.tableView.contentOffset;
if(numberOfRowsBeforeUpdate>0)
{
[controller.tableView reloadData];
int numberOfRowsAfterUpdate = [controller.tableView numberOfRowsInSection:0];
float rowHeight = [controller getTableViewCellHeight]; //custom method in my controller
float offset = (numberOfRowsAfterUpdate-numberOfRowsBeforeUpdate)*rowHeight;
if(offset>0)
{
currentOffset.y = currentOffset.y+offset;
[controller.tableView setContentOffset:currentOffset];
}
}
else
[controller.tableView reloadData];
Late to the party but this works even when cell have dynamic heights (a.k.a. UITableViewAutomaticDimension), no need to iterate over cells to calculate their size, but works only when items are added at the very beginning of the tableView and there is no header, with a little bit of math it's probably possible to adapt this to every situation:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
self.getMoreMessages()
}
}
private func getMoreMessages(){
var initialOffset = self.tableView.contentOffset.y
self.tableView.reloadData()
//@numberOfCellsAdded: number of items added at top of the table
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numberOfCellsAdded, inSection: 0), atScrollPosition: .Top, animated: false)
self.tableView.contentOffset.y += initialOffset
}
AmitP answers, Swift 3 version
let beforeContentSize = self.tableView.contentSize
self.tableView.reloadData()
let afterContentSize = self.tableView.contentSize
let afterContentOffset = self.tableView.contentOffset
let newContentOffset = CGPoint(x: afterContentOffset.x, y: afterContentOffset.y + afterContentSize.height - beforeContentSize.height)
self.tableView.contentOffset = newContentOffset
How about using scrollToRowAtIndexPath:atScrollPosition:animated:? You should be able to just add an element to your data source, set the row with the above mentioned method and reload the table...
참고URL : https://stackoverflow.com/questions/4279730/keep-uitableview-static-when-inserting-rows-at-the-top
'Program Club' 카테고리의 다른 글
| Ubuntu를 사용하여 Docker 컨테이너 내에 pip 패키지를 설치할 수 없습니다. (0) | 2020.12.03 |
|---|---|
| 문자열 인덱스가있는 Python 배열 (0) | 2020.12.03 |
| Html.Hidden과 Html.HiddenFor의 차이점은 무엇입니까? (0) | 2020.12.03 |
| Android Marshmallow : Espresso로 권한을 테스트 하시겠습니까? (0) | 2020.12.02 |
| iPhone SDK에 새 글꼴을 포함하고 사용하는 방법은 무엇입니까? (0) | 2020.12.02 |