Ответ 1
Взгляните на этот проект под названием DragDropCollectionView. Он реализует перетаскивание, а также анимацию.
Изменить: эту проблему можно разбить на две более мелкие подзадачи:
- Как оживить ячейки
- Как изменить порядок ячеек с помощью перетаскивания.
Вы должны объединить эти 2 решения в подклассу UICollectionView, чтобы получить основное решение.
Как оживить ячейки
Чтобы получить анимацию вибрирования, вам нужно добавить 2 разных анимационных эффекта:
- Переместить ячейки по вертикали i.e вверх и вниз bounce
- Поверните ячейки
- Наконец, добавьте случайное интервальное время для каждой ячейки, чтобы оно выглядело так, что ячейки не анимируются равномерно
Вот код:
@interface DragDropCollectionView ()
@property (assign, nonatomic) BOOL isWiggling;
@end
@implementation DragDropCollectionView
//Start and Stop methods for wiggle
- (void) startWiggle {
for (UICollectionViewCell *cell in self.visibleCells) {
[self addWiggleAnimationToCell:cell];
}
self.isWiggling = true;
}
- (void)stopWiggle {
for (UICollectionViewCell *cell in self.visibleCells) {
[cell.layer removeAllAnimations];
}
self.isWiggling = false;
}
//- (UICollectionViewCell *)dequ
- (UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(nonnull NSIndexPath *)indexPath{
UICollectionViewCell *cell = [super dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
if (self.isWiggling) {
[self addWiggleAnimationToCell:cell];
} else {
[cell.layer removeAllAnimations];
}
return [[UICollectionViewCell alloc] init];
}
//Animations
- (void)addWiggleAnimationToCell:(UICollectionViewCell *)cell {
[CATransaction begin];
[CATransaction setDisableActions:false];
[cell.layer addAnimation:[self rotationAnimation] forKey:@"rotation"];
[cell.layer addAnimation:[self bounceAnimation] forKey:@"bounce"];
[CATransaction commit];
}
- (CAKeyframeAnimation *)rotationAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
CGFloat angle = 0.04;
NSTimeInterval duration = 0.1;
double variance = 0.025;
animation.values = @[@(angle), @(-1 * angle)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:duration withVariance: variance];
animation.repeatCount = INFINITY;
return animation;
}
- (CAKeyframeAnimation *)bounceAnimation {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
CGFloat bounce = 3.0;
NSTimeInterval duration = 0.12;
double variance = 0.025;
animation.values = @[@(bounce), @(-1 * bounce)];
animation.autoreverses = YES;
animation.duration = [self randomizeInterval:duration withVariance: variance];
animation.repeatCount = INFINITY;
return animation;
}
- (NSTimeInterval)randomizeInterval:(NSTimeInterval)interval withVariance:(double)variance {
double randomDecimal = (arc4random() % 1000 - 500.0) / 500.0;
return interval + variance * randomDecimal;
}
Как изменить порядок ячеек с помощью перетаскивания
Итак, идея такова: вы не перемещаете фактическую ячейку вокруг, а скорее перемещаете UIImageView с UIImage содержимого ячейки.
Алгоритм более или менее подобен этому. Ive разбил его на 3 раздела, gestureRecognizerBegan, Changed and Ended
gestureRecognizerBegan:
- Когда начнется распознавание gestureRecognizer, определите, было ли длительное нажатие действительно на ячейке (а не на пустом пространстве).
- Получить UIImage ячейки (см. мой метод "getRasterizedImageOfCell" )
- Скрыть ячейку (i.e alpha = 0), создать UIImageView с точным фреймом ячейки, чтобы пользователь не понимал, что вы действительно скрыли ячейку, а вы действительно используете изображение.
gestureRecognizerChanged:
- Обновите центр UIImageView, чтобы он перемещался пальцем.
- Если пользователь перестает двигать пальцами, то есть он нависает над ячейкой, которую он хочет заменить, теперь вам нужно поменять ячейки. (Посмотрите на мою функцию "shouldSwapCells", этот метод возвращает bool того, должны ли ячейки меняться или нет)
- Переместите ячейку, которую вы перетаскиваете, в новую indexPath. (Посмотрите на мой метод "swapDraggedCell" ). UICollectionView имеет встроенный метод "moveItemAtIndexPath: toIndexPath", Im не уверен, что UITableView имеет то же самое.
gestureRecognizerEnd:
- "Отбросьте UIImageView обратно в ячейку
- измените ячейку alpha с 0.0 на 1.0 и удалите UIImageView из представления.
Вот код:
@interface DragDropCollectionView ()
@property (strong, nonatomic) NSIndexPath *draggedCellIndexPath;
@property (strong, nonatomic) UIImageView *draggingImageView;
@property (assign, nonatomic) CGPoint touchOffsetFromCenterOfCell;
@property (strong, nonatomic) UILongPressGestureRecognizer *longPressRecognizer;
@end
@implementation DragDropCollectionView
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer {
CGPoint touchLocation = [longPressRecognizer locationInView:self];
switch (longPressRecognizer.state) {
case UIGestureRecognizerStateBegan: {
self.draggedCellIndexPath = [self indexPathForItemAtPoint:touchLocation];
if (self.draggedCellIndexPath != nil) {
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
self.draggingImageView = [[UIImageView alloc] initWithImage:[self rasterizedImageCopyOfCell:draggedCell]];
self.draggingImageView.center = draggedCell.center;
[self addSubview:self.draggingImageView];
draggedCell.alpha = 0.0;
self.touchOffsetFromCenterOfCell = CGPointMake(draggedCell.center.x - touchLocation.x, draggedCell.center.y - touchLocation.y);
[UIView animateWithDuration:0.4 animations:^{
self.draggingImageView.transform = CGAffineTransformMakeScale(1.3, 1.3);
self.draggingImageView.alpha = 0.8;
}];
}
break;
}
case UIGestureRecognizerStateChanged: {
if (self.draggedCellIndexPath != nil) {
self.draggingImageView.center = CGPointMake(touchLocation.x + self.touchOffsetFromCenterOfCell.x, touchLocation.y + self.touchOffsetFromCenterOfCell.y);
}
float pingInterval = 0.3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(pingInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSIndexPath *newIndexPath = [self indexPathToSwapCellWithAtPreviousTouchLocation:touchLocation];
if (newIndexPath) {
[self swapDraggedCellWithCellAtIndexPath:newIndexPath];
}
});
break;
}
case UIGestureRecognizerStateEnded: {
if (self.draggedCellIndexPath != nil ) {
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
[UIView animateWithDuration:0.4 animations:^{
self.draggingImageView.transform = CGAffineTransformIdentity;
self.draggingImageView.alpha = 1.0;
if (draggedCell != nil) {
self.draggingImageView.center = draggedCell.center;
}
} completion:^(BOOL finished) {
[self.draggingImageView removeFromSuperview];
self.draggingImageView = nil;
if (draggedCell != nil) {
draggedCell.alpha = 1.0;
self.draggedCellIndexPath = nil;
}
}];
}
}
default:
break;
}
}
- (UIImage *)rasterizedImageCopyOfCell:(UICollectionViewCell *)cell {
UIGraphicsBeginImageContextWithOptions(cell.bounds.size, false, 0.0);
[cell.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
return image;
}
- (NSIndexPath *)indexPathToSwapCellWithAtPreviousTouchLocation:(CGPoint)previousTouchLocation {
CGPoint currentTouchLocation = [self.longPressRecognizer locationInView:self];
if (!isnan(currentTouchLocation.x) && !isnan(currentTouchLocation.y)) {
if ([self distanceBetweenPoints:currentTouchLocation secondPoint:previousTouchLocation] < 20.0) {
NSIndexPath *newIndexPath = [self indexPathForItemAtPoint:currentTouchLocation];
return newIndexPath;
}
}
return nil;
}
- (CGFloat)distanceBetweenPoints:(CGPoint)firstPoint secondPoint:(CGPoint)secondPoint {
CGFloat xDistance = firstPoint.x - secondPoint.x;
CGFloat yDistance = firstPoint.y - secondPoint.y;
return sqrtf(xDistance * xDistance + yDistance * yDistance);
}
- (void)swapDraggedCellWithCellAtIndexPath:(NSIndexPath *)newIndexPath {
[self moveItemAtIndexPath:self.draggedCellIndexPath toIndexPath:newIndexPath];
UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:newIndexPath];
draggedCell.alpha = 0.0;
self.draggedCellIndexPath = newIndexPath;
}
Надеюсь, что это поможет:)