在UILongpress手势中,UICollectionView滚动(UICollectionView scroll when in UILongpress gesture)

编程入门 行业动态 更新时间:2024-10-23 07:39:35
在UILongpress手势中,UICollectionView滚动(UICollectionView scroll when in UILongpress gesture)

更新的问题。 当视图滚动时,我很难在集合视图中重新排序项目。 这样做的原因是重新排序项目的代码在UILongpress手势的UIGestureRecognizerStateChanged内。 因此,当视图滚动并且用户的手指在屏幕边缘处静止时,不会调用UILongpress手势中的重新排序代码。 如果我只是插入重新排序的代码:

// Is destination valid and is it different from source? if (indexPath && ![indexPath isEqual:sourceIndexPath]) { // ... update data source. [self.imageArray exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row]; // ... move the rows. [self.collectionView moveItemAtIndexPath:sourceIndexPath toIndexPath:indexPath]; // ... and update source so it is in sync with UI changes. sourceIndexPath = indexPath;

在autoscrollTimerFired方法中,项目的索引路径将不再正确,因为集合视图已滚动到用户静态手指下的新位置。

完整代码如下。 建议好吗?

- (void)longPressGestureRecognized:(id)sender { UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender; UIGestureRecognizerState state = longPress.state; CGPoint location = [longPress locationInView:self.collectionView]; NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location]; static UIView *snapshot = nil; ///< A snapshot of the row user is moving. static NSIndexPath *sourceIndexPath = nil; ///< Initial index path, where gesture begins. switch (state) { case UIGestureRecognizerStateBegan: { if (indexPath) { [self.deleteButton removeFromSuperview]; self.isDeleteActive = NO; sourceIndexPath = indexPath; UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: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; [self.collectionView addSubview:snapshot]; [UIView animateWithDuration:0.25 animations:^{ // Offset for gesture location. center.x = location.x; snapshot.center = center; snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05); snapshot.alpha = 0.98; cell.alpha = 0.0; } completion:^(BOOL finished) { cell.hidden = YES; }]; } break; } case UIGestureRecognizerStateChanged: { CGPoint center = snapshot.center; center.x = location.x; snapshot.center = center; [self maybeAutoscrollForSnapshot:snapshot]; // Is destination valid and is it different from source? if (indexPath && ![indexPath isEqual:sourceIndexPath]) { // ... update data source. [self.imageArray exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row]; // ... move the rows. [self.collectionView moveItemAtIndexPath:sourceIndexPath toIndexPath:indexPath]; // ... and update source so it is in sync with UI changes. sourceIndexPath = indexPath; } break; } default: { // Clean up. if(_autoscrollTimer) { [_autoscrollTimer invalidate]; _autoscrollTimer = nil; } UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: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) { sourceIndexPath = nil; [snapshot removeFromSuperview]; snapshot = nil; }]; break; } } } - (void)maybeAutoscrollForSnapshot:(UIImageView *)snapshot { _autoscrollDistance = 0; if (CGRectGetMaxX(snapshot.frame) < self.collectionView.collectionViewLayout.collectionViewContentSize.width) { // only autoscroll if the content is larger than the view if (self.collectionView.collectionViewLayout.collectionViewContentSize.width > self.view.frame.size.width) { float distanceFromTop = snapshot.center.x - CGRectGetMinX(self.collectionView.bounds); float distanceFromBottom = CGRectGetMaxX(self.collectionView.bounds) - snapshot.center.x; //NSLog(@"dist from left %f", distanceFromTop); if (distanceFromTop < kAutoScrollingThreshold) { _autoscrollDistance = [self autoscrollDistanceForProximityToEdge:distanceFromTop] * -1; // if scrolling up distance is negative //NSLog(@"left dist %f", _autoscrollDistance); } else if (distanceFromBottom < kAutoScrollingThreshold) { _autoscrollDistance = [self autoscrollDistanceForProximityToEdge:distanceFromBottom]; //NSLog(@"right dist %f", _autoscrollDistance); } } } // if no autoscrolling, stop and clear timer if (_autoscrollDistance == 0) { [_autoscrollTimer invalidate]; _autoscrollTimer = nil; } // otherwise create and start timer (if we don't already have a timer going) else if (_autoscrollTimer == nil) { _autoscrollTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:@selector(autoscrollTimerFired:) userInfo:snapshot repeats:YES]; } } - (float)autoscrollDistanceForProximityToEdge:(float)proximity { // the scroll distance grows as the proximity to the edge decreases, so that moving the thumb // further over results in faster scrolling. return ceilf((kAutoScrollingThreshold - proximity) / 5.0); } - (void)legalizeAutoscrollDistance { // makes sure the autoscroll distance won't result in scrolling past the content of the scroll view float minimumLegalDistance = (self.collectionView.contentOffset.x + self.collectionView.contentInset.left) * -1; float maximumLegalDistance = self.collectionView.collectionViewLayout.collectionViewContentSize.width - (self.view.frame.size.width + self.collectionView.contentOffset.x); //NSLog(@"min dist %f", minimumLegalDistance); //NSLog(@"max dist %f", maximumLegalDistance); _autoscrollDistance = MAX(_autoscrollDistance, minimumLegalDistance); _autoscrollDistance = MIN(_autoscrollDistance, maximumLegalDistance); //NSLog(@"autoscroll distance %f", _autoscrollDistance); } - (void)autoscrollTimerFired:(NSTimer*)timer { // NSLog(@"autoscrolling: %.2f",_autoscrollDistance); [self legalizeAutoscrollDistance]; // autoscroll by changing content offset CGPoint contentOffset = [self.collectionView contentOffset]; contentOffset.x += _autoscrollDistance; //NSLog(@"%f",contentOffset.x); [self.collectionView setContentOffset:contentOffset]; // adjust thumb position so it appears to stay still UIImageView *snapshot = (UIImageView *)[timer userInfo]; snapshot.center = CGPointMake(snapshot.center.x + _autoscrollDistance, snapshot.center.y); }

Updated question. I'm having difficulty re-ordering the items within a collection view when the view is scrolling. The reason for this is that the code for reordering the items is within UIGestureRecognizerStateChanged of a UILongpress gesture. So, when the view scrolls and the user's finger is static at the edge of the screen, the reordering code in the UILongpress gesture isn't called. If I was to simply insert the reordering code:

// Is destination valid and is it different from source? if (indexPath && ![indexPath isEqual:sourceIndexPath]) { // ... update data source. [self.imageArray exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row]; // ... move the rows. [self.collectionView moveItemAtIndexPath:sourceIndexPath toIndexPath:indexPath]; // ... and update source so it is in sync with UI changes. sourceIndexPath = indexPath;

into the autoscrollTimerFired method, the indexpath of the item would no longer be correct as the collection view has scrolled to a new position under the user's static finger.

Full code below. Suggestions please?

- (void)longPressGestureRecognized:(id)sender { UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender; UIGestureRecognizerState state = longPress.state; CGPoint location = [longPress locationInView:self.collectionView]; NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:location]; static UIView *snapshot = nil; ///< A snapshot of the row user is moving. static NSIndexPath *sourceIndexPath = nil; ///< Initial index path, where gesture begins. switch (state) { case UIGestureRecognizerStateBegan: { if (indexPath) { [self.deleteButton removeFromSuperview]; self.isDeleteActive = NO; sourceIndexPath = indexPath; UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: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; [self.collectionView addSubview:snapshot]; [UIView animateWithDuration:0.25 animations:^{ // Offset for gesture location. center.x = location.x; snapshot.center = center; snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05); snapshot.alpha = 0.98; cell.alpha = 0.0; } completion:^(BOOL finished) { cell.hidden = YES; }]; } break; } case UIGestureRecognizerStateChanged: { CGPoint center = snapshot.center; center.x = location.x; snapshot.center = center; [self maybeAutoscrollForSnapshot:snapshot]; // Is destination valid and is it different from source? if (indexPath && ![indexPath isEqual:sourceIndexPath]) { // ... update data source. [self.imageArray exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row]; // ... move the rows. [self.collectionView moveItemAtIndexPath:sourceIndexPath toIndexPath:indexPath]; // ... and update source so it is in sync with UI changes. sourceIndexPath = indexPath; } break; } default: { // Clean up. if(_autoscrollTimer) { [_autoscrollTimer invalidate]; _autoscrollTimer = nil; } UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath: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) { sourceIndexPath = nil; [snapshot removeFromSuperview]; snapshot = nil; }]; break; } } } - (void)maybeAutoscrollForSnapshot:(UIImageView *)snapshot { _autoscrollDistance = 0; if (CGRectGetMaxX(snapshot.frame) < self.collectionView.collectionViewLayout.collectionViewContentSize.width) { // only autoscroll if the content is larger than the view if (self.collectionView.collectionViewLayout.collectionViewContentSize.width > self.view.frame.size.width) { float distanceFromTop = snapshot.center.x - CGRectGetMinX(self.collectionView.bounds); float distanceFromBottom = CGRectGetMaxX(self.collectionView.bounds) - snapshot.center.x; //NSLog(@"dist from left %f", distanceFromTop); if (distanceFromTop < kAutoScrollingThreshold) { _autoscrollDistance = [self autoscrollDistanceForProximityToEdge:distanceFromTop] * -1; // if scrolling up distance is negative //NSLog(@"left dist %f", _autoscrollDistance); } else if (distanceFromBottom < kAutoScrollingThreshold) { _autoscrollDistance = [self autoscrollDistanceForProximityToEdge:distanceFromBottom]; //NSLog(@"right dist %f", _autoscrollDistance); } } } // if no autoscrolling, stop and clear timer if (_autoscrollDistance == 0) { [_autoscrollTimer invalidate]; _autoscrollTimer = nil; } // otherwise create and start timer (if we don't already have a timer going) else if (_autoscrollTimer == nil) { _autoscrollTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:@selector(autoscrollTimerFired:) userInfo:snapshot repeats:YES]; } } - (float)autoscrollDistanceForProximityToEdge:(float)proximity { // the scroll distance grows as the proximity to the edge decreases, so that moving the thumb // further over results in faster scrolling. return ceilf((kAutoScrollingThreshold - proximity) / 5.0); } - (void)legalizeAutoscrollDistance { // makes sure the autoscroll distance won't result in scrolling past the content of the scroll view float minimumLegalDistance = (self.collectionView.contentOffset.x + self.collectionView.contentInset.left) * -1; float maximumLegalDistance = self.collectionView.collectionViewLayout.collectionViewContentSize.width - (self.view.frame.size.width + self.collectionView.contentOffset.x); //NSLog(@"min dist %f", minimumLegalDistance); //NSLog(@"max dist %f", maximumLegalDistance); _autoscrollDistance = MAX(_autoscrollDistance, minimumLegalDistance); _autoscrollDistance = MIN(_autoscrollDistance, maximumLegalDistance); //NSLog(@"autoscroll distance %f", _autoscrollDistance); } - (void)autoscrollTimerFired:(NSTimer*)timer { // NSLog(@"autoscrolling: %.2f",_autoscrollDistance); [self legalizeAutoscrollDistance]; // autoscroll by changing content offset CGPoint contentOffset = [self.collectionView contentOffset]; contentOffset.x += _autoscrollDistance; //NSLog(@"%f",contentOffset.x); [self.collectionView setContentOffset:contentOffset]; // adjust thumb position so it appears to stay still UIImageView *snapshot = (UIImageView *)[timer userInfo]; snapshot.center = CGPointMake(snapshot.center.x + _autoscrollDistance, snapshot.center.y); }

最满意答案

当您调用maybeAutoscrollForSnapshot函数时,将发件人( UILongPressGestureRecognizer )保存在UIGestureRecognizerStateChanged ,然后让计时器使用您保存的此手势识别器对象调用longPressGestureRecognized。 手势识别器将具有相对于UIWindow的相同位置,但是当您计算indexPath它应该是正确的,因为scrollView具有滚动。

作为旁注,当你到达边缘时,你真的需要让视图滚动吗? 我实现了相同的东西,但没有autoscroll部分,它工作正常。

你有这么长的列表,如果它自动滚动会有帮助吗?

Save the sender(UILongPressGestureRecognizer) in the UIGestureRecognizerStateChanged when you call the maybeAutoscrollForSnapshot function, and then have the timer to call longPressGestureRecognized with this gesture recognizer object that you saved. The gesture recognizer will have the same location relative to UIWindow, but when you calculate the indexPath it should be the correct one as the scrollView has scroll.

As a side note, do you really need to have the view scroll when you get to the edges? I implemented the same thing but without the autoscroll part, and it works fine.

Do you have such long lists that it would help if it autoscroll?

更多推荐

本文发布于:2023-04-29 09:51:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1335907.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:手势   UILongpress   UICollectionView   gesture   scroll

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!