我已经实现了一个系统来重新排序UITableViews中的单元格。一切都很好,除了我不能将单元格重新排序到iPhone屏幕上没有显示的位置。
所以我已经实现了一个条件来检查我是否需要滚动
NSArray *indexVisibles = [_tableView indexPathsForVisibleRows];
NSInteger indexForObject = [indexVisibles indexOfObject:indexPath];
if (indexForObject == NSNotFound){
[_tableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
我的问题是动画不甜而干净。
我认为检查细胞是否显示的操作对于我的系统来说非常巨大,并且当我移动细胞时会产生一个小延迟,但是,我不确定为什么当细胞隐藏时滚动是如此困难
我已将UITableViewScrollPositionTop
更改为UITableViewScrollPositionMiddle
,现在它更好但速度非常大,所以始终滚动到我的UITableView的顶部。
我想慢慢来。
其他失败尝试:
选项1:
[UIView animateWithDuration:0.2
animations:^{_tableView.contentOffset = CGPointMake(0.0, _tableView.contentOffset.y - 50);}
completion:^(BOOL finished){ }];
但这有两个问题:
答案 0 :(得分:11)
我用一个非常漂亮的解决方案解决了我的问题,所以我将通过三个简单的步骤解释如何做到这一点。
我使用https://github.com/hpique/HPReorderTableView的一些灵感并在我自己的资源库https://github.com/enrimr/EMRReorderTableCells中分享
:一种。管理gestureRecognition
<强> longPressGestureRecognized: 强>
- (IBAction)longPressGestureRecognized:(id)sender {
_reorderGestureRecognizer = (UILongPressGestureRecognizer *)sender;
CGPoint location = [_reorderGestureRecognizer locationInView:_tableView];
NSIndexPath *indexPath = [self getCellIndexPathWithPoint:location];
UIGestureRecognizerState state = _reorderGestureRecognizer.state;
switch (state) {
case UIGestureRecognizerStateBegan: {
NSIndexPath *indexPath = [_tableView indexPathForRowAtPoint:location];
if (indexPath == nil)
{
[self gestureRecognizerCancel:_reorderGestureRecognizer];
break;
}
// For scrolling while dragging
_scrollDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scrollTableWithCell:)];
[_scrollDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
// Check for the right indexes (between margins of offset
if (indexPath.row >=_elementsOffset && indexPath.row < [_elements count]+_elementsOffset){
if (indexPath) {
sourceIndexPath = indexPath;
id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset];
snapshot = [self createSnapshotForCellAtIndexPath:indexPath withPosition:location];
}
} else {
sourceIndexPath = nil;
snapshot = nil;
}
break;
}
case UIGestureRecognizerStateChanged: {
[self calculateScroll:_reorderGestureRecognizer];
if (sourceIndexPath != nil && indexPath.row >=_elementsOffset && indexPath.row < [_elements count]+_elementsOffset){
[self updateSnapshotWithPosition:location];
// Is destination valid and is it different from source?
if (indexPath && ![indexPath isEqual:sourceIndexPath]) {
if (indexPath.row - sourceIndexPath.row <= 1){
id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset];
id targetElement = [_elements objectAtIndex:indexPath.row-_elementsOffset];
sourceIndexPath = [self exchangeElement:sourceElement byElement:targetElement];
}
}
}
break;
}
case UIGestureRecognizerStateEnded:
{
// For scrolling while dragging
[_scrollDisplayLink invalidate];
_scrollDisplayLink = nil;
_scrollRate = 0;
// Check if it is the last element
if (sourceIndexPath != nil){
id element;
if (indexPath.row <=_elementsOffset){
element = [_elements firstObject];
} else if (indexPath.row > [_elements count]-1+_elementsOffset){
element = [_elements lastObject];
} else {
element = [_elements objectAtIndex:indexPath.row-_elementsOffset];
}
}
}
default: {
// Clean up.
[self deleteSnapshotForRowAtIndexPath:sourceIndexPath];
[appDelegate startTimer];
break;
}
}
}
<强> gestureRecognizerCancel: 强>
用于取消手势识别以完成重新排序操作。
-(void) gestureRecognizerCancel:(UIGestureRecognizer *) gestureRecognizer
{ // See: http://stackoverflow.com/a/4167471/143378
gestureRecognizer.enabled = NO;
gestureRecognizer.enabled = YES;
}
<强> scrollTableWithCell: 强>
当你在桌子的极限(上下)时,它被调用来进行滚动运动的方法
- (void)scrollTableWithCell:(NSTimer *)timer
{
UILongPressGestureRecognizer *gesture = _reorderGestureRecognizer;
const CGPoint location = [gesture locationInView:_tableView];
CGPoint currentOffset = _tableView.contentOffset;
CGPoint newOffset = CGPointMake(currentOffset.x, currentOffset.y + _scrollRate * 10);
if (newOffset.y < -_tableView.contentInset.top)
{
newOffset.y = -_tableView.contentInset.top;
}
else if (_tableView.contentSize.height + _tableView.contentInset.bottom < _tableView.frame.size.height)
{
newOffset = currentOffset;
}
else if (newOffset.y > (_tableView.contentSize.height + _tableView.contentInset.bottom) - _tableView.frame.size.height)
{
newOffset.y = (_tableView.contentSize.height + _tableView.contentInset.bottom) - _tableView.frame.size.height;
}
[_tableView setContentOffset:newOffset];
if (location.y >= 0 && location.y <= _tableView.contentSize.height + 50)
{
[self updateSnapshotWithPosition:location];
NSIndexPath *indexPath = [self getCellIndexPathWithPoint:location];
// CHeck if element is between offset limits.
if (![indexPath isEqual:sourceIndexPath] &&
indexPath.row >= _elementsOffset &&
indexPath.row - _elementsOffset < [_elements count] &&
sourceIndexPath.row >= _elementsOffset &&
sourceIndexPath.row - _elementsOffset < [_elements count])
{
id sourceElement = [_elements objectAtIndex:sourceIndexPath.row-_elementsOffset];
id targetElement = [_elements objectAtIndex:indexPath.row-_elementsOffset];
[self exchangeElement:sourceElement byElement:targetElement];
sourceIndexPath = indexPath;
}
}
}
<强> B中。快照管理
<强> createSnapshotForCellAtIndexPath:withPosition 强>
创建正在移动的单元格的快照(图像副本)的方法
-(UIView *)createSnapshotForCellAtIndexPath:(NSIndexPath *)indexPath withPosition:(CGPoint)location{
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:indexPath];
// Take a snapshot of the selected row using helper method.
snapshot = [self customSnapshoFromView:cell];
// Add the snapshot as subview, centered at cell's center...
__block CGPoint center = cell.center;
snapshot.center = center;
snapshot.alpha = 0.0;
[_tableView addSubview:snapshot];
[UIView animateWithDuration:0.25 animations:^{
// Offset for gesture location.
center.y = location.y;
snapshot.center = center;
snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshot.alpha = 0.98;
cell.alpha = 0.0;
} completion:^(BOOL finished) {
cell.hidden = YES;
}];
return snapshot;
}
<强> customSnapshoFromView: 强>
返回给定视图的自定义快照。 * /
- (UIView *)customSnapshoFromView:(UIView *)inputView {
// Make an image from the input view.
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
[inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Create an image view.
snapshot = [[UIImageView alloc] initWithImage:image];
snapshot.layer.masksToBounds = NO;
snapshot.layer.cornerRadius = 0.0;
snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0);
snapshot.layer.shadowRadius = 5.0;
snapshot.layer.shadowOpacity = 0.4;
return snapshot;
}
<强> updateSnapshotWithPosition: 强>
给定CGPoint,它会更改快照位置以显示您正在_tableView正确位置移动的单元格
-(void)updateSnapshotWithPosition:(CGPoint)location{
CGPoint center = snapshot.center;
center.y = location.y;
snapshot.center = center;
}
<强> deleteSnapshotForRowAtIndexPath: 强>
拖动完成后,您需要从_tableView
中删除快照-(void)deleteSnapshotForRowAtIndexPath:(NSIndexPath *)sourceIndexPath{
UITableViewCell *cell = [_tableView cellForRowAtIndexPath:sourceIndexPath];
cell.hidden = NO;
cell.alpha = 0.0;
[UIView animateWithDuration:0.25 animations:^{
snapshot.center = cell.center;
snapshot.transform = CGAffineTransformIdentity;
snapshot.alpha = 0.0;
cell.alpha = 1.0;
} completion:^(BOOL finished) {
[snapshot removeFromSuperview];
}];
}
<强> calculateScroll 强>
-(void)calculateScroll:(UIGestureRecognizer *)gestureRecognizer{
const CGPoint location = [gestureRecognizer locationInView:_tableView];
CGRect rect = _tableView.bounds;
// adjust rect for content inset as we will use it below for calculating scroll zones
rect.size.height -= _tableView.contentInset.top;
//[self updateCurrentLocation:gestureRecognizer];
// tell us if we should scroll and which direction
CGFloat scrollZoneHeight = rect.size.height / 6;
CGFloat bottomScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top + rect.size.height - scrollZoneHeight;
CGFloat topScrollBeginning = _tableView.contentOffset.y + _tableView.contentInset.top + scrollZoneHeight;
// we're in the bottom zone
if (location.y >= bottomScrollBeginning)
{
_scrollRate = (location.y - bottomScrollBeginning) / scrollZoneHeight;
}
// we're in the top zone
else if (location.y <= topScrollBeginning)
{
_scrollRate = (location.y - topScrollBeginning) / scrollZoneHeight;
}
else
{
_scrollRate = 0;
}
}
<强>℃。如何使用
在init方法中,将手势识别器分配给表格视图。将方法longPressGestureRecognized:
指定为操作,如下所示:
_reorderGestureRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(longPressGestureRecognized:)];
[_tableView addGestureRecognizer:_reorderGestureRecognizer];
声明使用上述代码所需的变量
@implementation YourClassName{
CADisplayLink *_scrollDisplayLink;
CGFloat _scrollRate;
UIView *snapshot; // A snapshot of the row user is moving.
NSIndexPath *sourceIndexPath; // Initial index path, where gesture begins.
}
这就是解决我遇到的问题所需的一切。
答案 1 :(得分:1)
我也在寻找一种通过长按它们来渲染tableview单元格的方法。 我找到了一个用于此目的的存储库。如果您不介意将第三方库添加到您的项目中,请查看它! :)
我个人将此代码用于我的应用程序Cheetah Note。他们的工作就像一个魅力!强烈推荐!
答案 2 :(得分:0)
使用选项一尝试滚动到拖动的单元格位置 - 当拖动的单元格位于顶部时为1:
NSArray *indexVisibles = [_tableView indexPathsForVisibleRows];
NSInteger indexForObject = [indexVisibles indexOfObject:indexPath];
if (indexForObject == 0){
[_tableView scrollToRowAtIndexPath:indexPathForCellBefore
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
}
要平滑滚动tableview,请使用以下代码段:
[UIView animateWithDuration: 1.0 animations: ^{
[_tableView scrollToRowAtIndexPath:indexPathForCellBefore atScrollPosition:UITableViewScrollPositionTop animated:NO];
} completion: ^(BOOL finished){
}];
当然,您需要在向下滚动时修改它。