移动时保持 UICollectionView 中自定义单元格的大小
Keep size of a custom cell in a UICollectionView when moving
我有一个水平的集合视图,每个单元格都有自己的宽度。我使用默认的 UICollectionViewFlowLayout。重新排列时,单元格继承了它在此移动过程中覆盖的单元格的宽度,这是我不想要的。知道如何避免这种行为吗?
因此,如果您的 UICollectionView 具有不同大小的单元格,并且您希望在使用标准 UICollectionViewFlowLayout 移动时保持它们的大小,请使用此:
extension UICollectionViewFlowLayout {
@available(iOS 9.0, *)
open override func invalidationContext(forInteractivelyMovingItems targetIndexPaths: [IndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [IndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forInteractivelyMovingItems: targetIndexPaths, withTargetPosition: targetPosition, previousIndexPaths: previousIndexPaths, previousPosition: previousPosition)
//Check that the movement has actually happeneds
if previousIndexPaths.first!.item != targetIndexPaths.first!.item {
collectionView?.dataSource?.collectionView?(collectionView!, moveItemAt: previousIndexPaths.first!, to: targetIndexPaths.last!)
}
return context
}
open override func invalidationContextForEndingInteractiveMovementOfItems(toFinalIndexPaths indexPaths: [IndexPath], previousIndexPaths: [IndexPath], movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext {
return super.invalidationContextForEndingInteractiveMovementOfItems(toFinalIndexPaths: indexPaths, previousIndexPaths: previousIndexPaths, movementCancelled: movementCancelled)
}
@available(iOS 9.0, *) //If you'd like to apply some formatting as the dimming of the movable cell
open override func layoutAttributesForInteractivelyMovingItem(at indexPath: IndexPath, withTargetPosition position: CGPoint) -> UICollectionViewLayoutAttributes {
let attributes = super.layoutAttributesForInteractivelyMovingItem(at: indexPath, withTargetPosition: position)
attributes.alpha = 0.8
return attributes
}
}
在控制器的 moveItemAt 方法中,不要忘记检查压力状态是否已更改,例如:
override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
if #available(iOS 9.0, *) {
if self.longPressureGestureRecognizer.state == .ended {
return
}
//Update your data
data.moveItem(at: sourceIndexPath.row, to: destinationIndexPath.row)
}
}
我通过使用 collectionView:targetIndexPathForMoveFromItemAtIndexPath:toProposedIndexPath:
解决了这个问题,如果我的 row/section 数据的副本不存在,我会创建它,然后更新副本,就好像提议的移动是实际的一样移动。如果副本存在,我的 collectionView:layout:sizeForItemAtIndexPath:
会使用它。我会在 collectionView:moveItemAtIndexPath:toIndexPath:
中将副本设置为 nil,以便视图返回使用 "original" row/section 数据来调整大小。结果,所有尺寸在移动之前、期间和之后看起来都是正确的。
根据要求,这是我的代码:
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath
{
if (nil == _movingSections)
_movingSections = [_sections copy];
NSMutableArray *section = [_movingSections.sections objectAtIndex:originalIndexPath.section];
id obj = [section objectAtIndex:originalIndexPath.row];
[section removeObjectAtIndex:originalIndexPath.row];
section = [_movingSections.sections objectAtIndex:proposedIndexPath.section];
if (proposedIndexPath.row < [section count])
[section insertObject:obj atIndex:proposedIndexPath.row];
else
[section addObject:obj];
return proposedIndexPath;
}
我还更新了 collectionView:layout:sizeForItemAtIndexPath:
以在 _movingSections 非零时使用 _movingSections 而不是 _sections。在 collectionView:moveItemAtIndexPath:toIndexPath:
中,我将 _movingSections 设置为 nil。就是这样!
我应该补充一点,我最终放弃了这个方法,因为我有一些部分可以比设备宽度宽,在你移动某些东西之前它工作正常,此时 UICollectionViewFlowLayout 想要均匀地 space单元格,无论您提供的宽度如何。相反,我转而使用 UIScrollView,现在我的代码更简单了,而且我不再与 UICollectionViewFlowLayout 作斗争。我考虑过滚动我自己的 UICollectionViewLayout,但这并没有使我的代码更简单。
我有一个水平的集合视图,每个单元格都有自己的宽度。我使用默认的 UICollectionViewFlowLayout。重新排列时,单元格继承了它在此移动过程中覆盖的单元格的宽度,这是我不想要的。知道如何避免这种行为吗?
因此,如果您的 UICollectionView 具有不同大小的单元格,并且您希望在使用标准 UICollectionViewFlowLayout 移动时保持它们的大小,请使用此:
extension UICollectionViewFlowLayout {
@available(iOS 9.0, *)
open override func invalidationContext(forInteractivelyMovingItems targetIndexPaths: [IndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [IndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext {
let context = super.invalidationContext(forInteractivelyMovingItems: targetIndexPaths, withTargetPosition: targetPosition, previousIndexPaths: previousIndexPaths, previousPosition: previousPosition)
//Check that the movement has actually happeneds
if previousIndexPaths.first!.item != targetIndexPaths.first!.item {
collectionView?.dataSource?.collectionView?(collectionView!, moveItemAt: previousIndexPaths.first!, to: targetIndexPaths.last!)
}
return context
}
open override func invalidationContextForEndingInteractiveMovementOfItems(toFinalIndexPaths indexPaths: [IndexPath], previousIndexPaths: [IndexPath], movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext {
return super.invalidationContextForEndingInteractiveMovementOfItems(toFinalIndexPaths: indexPaths, previousIndexPaths: previousIndexPaths, movementCancelled: movementCancelled)
}
@available(iOS 9.0, *) //If you'd like to apply some formatting as the dimming of the movable cell
open override func layoutAttributesForInteractivelyMovingItem(at indexPath: IndexPath, withTargetPosition position: CGPoint) -> UICollectionViewLayoutAttributes {
let attributes = super.layoutAttributesForInteractivelyMovingItem(at: indexPath, withTargetPosition: position)
attributes.alpha = 0.8
return attributes
}
}
在控制器的 moveItemAt 方法中,不要忘记检查压力状态是否已更改,例如:
override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath,
to destinationIndexPath: IndexPath) {
if #available(iOS 9.0, *) {
if self.longPressureGestureRecognizer.state == .ended {
return
}
//Update your data
data.moveItem(at: sourceIndexPath.row, to: destinationIndexPath.row)
}
}
我通过使用 collectionView:targetIndexPathForMoveFromItemAtIndexPath:toProposedIndexPath:
解决了这个问题,如果我的 row/section 数据的副本不存在,我会创建它,然后更新副本,就好像提议的移动是实际的一样移动。如果副本存在,我的 collectionView:layout:sizeForItemAtIndexPath:
会使用它。我会在 collectionView:moveItemAtIndexPath:toIndexPath:
中将副本设置为 nil,以便视图返回使用 "original" row/section 数据来调整大小。结果,所有尺寸在移动之前、期间和之后看起来都是正确的。
根据要求,这是我的代码:
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath
{
if (nil == _movingSections)
_movingSections = [_sections copy];
NSMutableArray *section = [_movingSections.sections objectAtIndex:originalIndexPath.section];
id obj = [section objectAtIndex:originalIndexPath.row];
[section removeObjectAtIndex:originalIndexPath.row];
section = [_movingSections.sections objectAtIndex:proposedIndexPath.section];
if (proposedIndexPath.row < [section count])
[section insertObject:obj atIndex:proposedIndexPath.row];
else
[section addObject:obj];
return proposedIndexPath;
}
我还更新了 collectionView:layout:sizeForItemAtIndexPath:
以在 _movingSections 非零时使用 _movingSections 而不是 _sections。在 collectionView:moveItemAtIndexPath:toIndexPath:
中,我将 _movingSections 设置为 nil。就是这样!
我应该补充一点,我最终放弃了这个方法,因为我有一些部分可以比设备宽度宽,在你移动某些东西之前它工作正常,此时 UICollectionViewFlowLayout 想要均匀地 space单元格,无论您提供的宽度如何。相反,我转而使用 UIScrollView,现在我的代码更简单了,而且我不再与 UICollectionViewFlowLayout 作斗争。我考虑过滚动我自己的 UICollectionViewLayout,但这并没有使我的代码更简单。