iOS: update flowlayout beforeDragging & add itemCanDrag

This commit is contained in:
王劲鹏 2022-08-31 20:52:36 +08:00 committed by osborn
parent 7632fa9a9d
commit a452cde882

View File

@ -17,6 +17,7 @@
// Created by pengfei.zhou on 2019/11/28. // Created by pengfei.zhou on 2019/11/28.
// //
#import <DoricCore/Doric.h>
#import "DoricFlowLayoutNode.h" #import "DoricFlowLayoutNode.h"
#import "DoricFlowLayoutItemNode.h" #import "DoricFlowLayoutItemNode.h"
#import "DoricExtensions.h" #import "DoricExtensions.h"
@ -44,6 +45,9 @@ @interface DoricFlowLayout : UICollectionViewLayout
@property(nonatomic, readonly) CGFloat rowSpace; @property(nonatomic, readonly) CGFloat rowSpace;
@property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSNumber *> *columnHeightInfo; @property(nonatomic, strong) NSMutableDictionary <NSNumber *, NSNumber *> *columnHeightInfo;
@property(nonatomic, weak) id <DoricFlowLayoutDelegate> delegate; @property(nonatomic, weak) id <DoricFlowLayoutDelegate> delegate;
@property(nonatomic, copy) NSArray<UICollectionViewLayoutAttributes*> *layoutAttributes;
@property(nonatomic, copy) NSArray<NSValue*> *unionRects;
@property(nonatomic, strong) NSArray *swapDisabled;
@end @end
@implementation DoricFlowLayout @implementation DoricFlowLayout
@ -75,63 +79,104 @@ - (void)prepareLayout {
for (int i = 0; i < self.columnCount; i++) { for (int i = 0; i < self.columnCount; i++) {
self.columnHeightInfo[@(i)] = @(0); self.columnHeightInfo[@(i)] = @(0);
} }
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
for (int i = 0; i < self.columnCount; i++) {
self.columnHeightInfo[@(i)] = @(0);
}
NSMutableArray *array = [NSMutableArray array]; NSMutableArray *array = [NSMutableArray array];
NSInteger count = [self.collectionView numberOfItemsInSection:0]; NSInteger count = [self.collectionView numberOfItemsInSection:0];
for (int i = 0; i < count; i++) {
UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
[array addObject:attrs];
}
return array;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
NSNumber *minYOfColumn = @(0); NSNumber *minYOfColumn = @(0);
NSArray<NSNumber *> *keys = self.columnHeightInfo.allKeys; NSArray<NSNumber *> *keys = self.columnHeightInfo.allKeys;
NSArray<NSNumber *> *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) { NSArray<NSNumber *> *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) {
return ([obj1 intValue] <= [obj2 intValue] ? NSOrderedAscending : NSOrderedDescending); return ([obj1 intValue] <= [obj2 intValue] ? NSOrderedAscending : NSOrderedDescending);
}]; }];
for (NSNumber *key in sortedKeys) { for (int i = 0; i < count; i++) {
if ([self.columnHeightInfo[key] floatValue] < [self.columnHeightInfo[minYOfColumn] floatValue]) { minYOfColumn = @(0);
minYOfColumn = key; NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0] ;
}
} UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat width = [self.delegate doricFlowLayoutItemWidthAtIndexPath:indexPath];
CGFloat height = [self.delegate doricFlowLayoutItemHeightAtIndexPath:indexPath];
CGFloat x = 0;
CGFloat y = [self.columnHeightInfo[minYOfColumn] floatValue];
if (y > 0) {
y += self.rowSpace;
}
if ([self.delegate doricFlowLayoutItemFullSpan:indexPath]) {
NSNumber *maxYColumn = @(0);
for (NSNumber *key in sortedKeys) { for (NSNumber *key in sortedKeys) {
if ([self.columnHeightInfo[key] floatValue] > [self.columnHeightInfo[maxYColumn] floatValue]) { if ([self.columnHeightInfo[key] floatValue] < [self.columnHeightInfo[minYOfColumn] floatValue]) {
maxYColumn = key; minYOfColumn = key;
} }
} }
CGFloat maxY = [self.columnHeightInfo[maxYColumn] floatValue];
y = maxY + self.rowSpace; CGFloat width = [self.delegate doricFlowLayoutItemWidthAtIndexPath:indexPath];
for (NSNumber *key in self.columnHeightInfo.allKeys) { CGFloat height = [self.delegate doricFlowLayoutItemHeightAtIndexPath:indexPath];
self.columnHeightInfo[key] = @(y + height); CGFloat x = 0;
CGFloat y = [self.columnHeightInfo[minYOfColumn] floatValue];
if (y > 0) {
y += self.rowSpace;
} }
} else {
CGFloat columnWidth = (self.collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount; if ([self.delegate doricFlowLayoutItemFullSpan:indexPath]) {
x = (columnWidth + self.columnSpace) * [minYOfColumn integerValue]; NSNumber *maxYColumn = @(0);
self.columnHeightInfo[minYOfColumn] = @(y + height); for (NSNumber *key in sortedKeys) {
if ([self.columnHeightInfo[key] floatValue] > [self.columnHeightInfo[maxYColumn] floatValue]) {
maxYColumn = key;
}
}
CGFloat maxY = [self.columnHeightInfo[maxYColumn] floatValue];
y = maxY + self.rowSpace;
for (NSNumber *key in self.columnHeightInfo.allKeys) {
self.columnHeightInfo[key] = @(y + height);
}
} else {
CGFloat columnWidth = (self.collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount;
x = (columnWidth + self.columnSpace) * [minYOfColumn integerValue];
self.columnHeightInfo[minYOfColumn] = @(y + height);
}
attrs.frame = CGRectMake(x, y, width, height);
[array addObject:attrs];
} }
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; self.layoutAttributes = array;
attrs.frame = CGRectMake(x, y, width, height);
NSMutableArray *mutableCopy = [NSMutableArray array];
long idx = 0;
NSInteger itemCounts = count;
while(idx < itemCounts){
CGRect rect1 = self.layoutAttributes[idx].frame;
idx = MIN(idx + count, itemCounts) - 1;
CGRect rect2 = self.layoutAttributes[idx].frame;
[mutableCopy addObject:[NSValue valueWithCGRect:CGRectUnion(rect1, rect2)]];
idx += 1;
}
self.unionRects = mutableCopy;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSInteger begin = 0;
NSInteger end = self.unionRects.count;
NSMutableArray<UICollectionViewLayoutAttributes*> *attrs = [NSMutableArray array];
for (int i = 0; i < end; i++) {
if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) {
begin = i * self.layoutAttributes.count;
break;
}
}
for (long i = self.unionRects.count - 1; i >= 0; i--) {
if (CGRectIntersectsRect(rect, [self.unionRects[i] CGRectValue])) {
end = MIN((i + 1) * self.layoutAttributes.count, self.layoutAttributes.count);
break;
}
}
for (NSInteger i = begin; i < end; i++) {
UICollectionViewLayoutAttributes *attr = self.layoutAttributes[i];
if (CGRectIntersectsRect(rect, attr.frame)) {
[attrs addObject:attr];
}
}
return attrs; return attrs;
} }
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.layoutAttributes[indexPath.row];
}
- (CGSize)collectionViewContentSize { - (CGSize)collectionViewContentSize {
CGFloat width = self.collectionView.width; CGFloat width = self.collectionView.width;
CGFloat height = 0; CGFloat height = 0;
@ -175,11 +220,12 @@ @interface DoricFlowLayoutNode () <UICollectionViewDataSource, UICollectionViewD
@property(nonatomic, assign) NSInteger loadAnchor; @property(nonatomic, assign) NSInteger loadAnchor;
@property(nonatomic, strong) UILongPressGestureRecognizer *longPress; @property(nonatomic, strong) UILongPressGestureRecognizer *longPress;
@property(nonatomic, strong) NSIndexPath *initialDragIndexPath;
@property(nonatomic, strong) NSIndexPath *currentDragIndexPath; @property(nonatomic, strong) NSIndexPath *currentDragIndexPath;
@property(nonatomic, copy) NSString *itemCanDragFuncId;
@property(nonatomic, copy) NSString *beforeDraggingFuncId; @property(nonatomic, copy) NSString *beforeDraggingFuncId;
@property(nonatomic, copy) NSString *onDraggingFuncId; @property(nonatomic, copy) NSString *onDraggingFuncId;
@property(nonatomic, copy) NSString *onDraggedFuncId; @property(nonatomic, copy) NSString *onDraggedFuncId;
@property(nonatomic, strong) NSArray *swapDisabled;
@end @end
@implementation DoricFlowLayoutNode @implementation DoricFlowLayoutNode
@ -221,29 +267,29 @@ - (void)longPressAction:(UILongPressGestureRecognizer *)sender {
NSIndexPath *indexPath = [self.view indexPathForItemAtPoint:locationInView]; NSIndexPath *indexPath = [self.view indexPathForItemAtPoint:locationInView];
if (sender.state == UIGestureRecognizerStateBegan) { if (sender.state == UIGestureRecognizerStateBegan) {
if (indexPath != nil) { if (indexPath != nil) {
self.initialDragIndexPath = indexPath; [self.view beginInteractiveMovementForItemAtIndexPath:indexPath];
self.currentDragIndexPath = indexPath; self.currentDragIndexPath = indexPath;
if (self.beforeDraggingFuncId != nil) { if (self.beforeDraggingFuncId != nil) {
[self callJSResponse:self.beforeDraggingFuncId, @(indexPath.row), nil]; DoricAsyncResult *asyncResult = [self callJSResponse:self.beforeDraggingFuncId, @(indexPath.row), nil];
JSValue *model = [asyncResult waitUntilResult];
if (model.isArray) {
self.swapDisabled = [model toArray];
}
} }
} }
} else if (sender.state == UIGestureRecognizerStateChanged) { } else if (sender.state == UIGestureRecognizerStateChanged) {
[self.view updateInteractiveMovementTargetPosition:[sender locationInView:self.view]];
if ((indexPath != nil) && (indexPath != self.currentDragIndexPath)) { if ((indexPath != nil) && (indexPath != self.currentDragIndexPath)) {
NSString *fromValue = self.itemViewIds[@(self.currentDragIndexPath.row)];
NSString *toValue = self.itemViewIds[@(indexPath.row)];
self.itemViewIds[@(self.currentDragIndexPath.row)] = toValue;
self.itemViewIds[@(indexPath.row)] = fromValue;
[self.view moveItemAtIndexPath:self.currentDragIndexPath toIndexPath:indexPath];
if (self.onDraggingFuncId != nil) { if (self.onDraggingFuncId != nil) {
[self callJSResponse:self.onDraggingFuncId, @(self.currentDragIndexPath.row), @(indexPath.row), nil]; [self callJSResponse:self.onDraggingFuncId, @(self.currentDragIndexPath.row), @(indexPath.row), nil];
} }
self.currentDragIndexPath = indexPath;
} }
} else if (sender.state == UIGestureRecognizerStateEnded) { } else if (sender.state == UIGestureRecognizerStateEnded) {
if (self.onDraggedFuncId != nil) { [self.view endInteractiveMovement];
[self callJSResponse:self.onDraggedFuncId, @(self.initialDragIndexPath.row), @(self.currentDragIndexPath.row), nil]; } else {
} [self.view cancelInteractiveMovement];
} }
} }
@ -294,6 +340,8 @@ - (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValu
} else if ([@"canDrag" isEqualToString:name]) { } else if ([@"canDrag" isEqualToString:name]) {
bool canDrag = [prop boolValue]; bool canDrag = [prop boolValue];
[self.longPress setEnabled:canDrag]; [self.longPress setEnabled:canDrag];
} else if ([@"itemCanDrag" isEqualToString:name]) {
self.itemCanDragFuncId = prop;
} else if ([@"beforeDragging" isEqualToString:name]) { } else if ([@"beforeDragging" isEqualToString:name]) {
self.beforeDraggingFuncId = prop; self.beforeDraggingFuncId = prop;
} else if ([@"onDragging" isEqualToString:name]) { } else if ([@"onDragging" isEqualToString:name]) {
@ -449,6 +497,17 @@ - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collection
return cell; return cell;
} }
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)currentIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath {
if (self.swapDisabled != nil) {
for (int i = 0; i != self.swapDisabled.count; i++) {
if (proposedIndexPath.row == [self.swapDisabled[i] intValue]) {
return currentIndexPath;
}
}
}
return proposedIndexPath;
}
- (CGFloat)doricFlowLayoutItemHeightAtIndexPath:(NSIndexPath *)indexPath { - (CGFloat)doricFlowLayoutItemHeightAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger position = (NSUInteger) indexPath.row; NSUInteger position = (NSUInteger) indexPath.row;
NSValue *value = self.itemSizeInfo[@(position)]; NSValue *value = self.itemSizeInfo[@(position)];
@ -593,6 +652,7 @@ - (void)reset {
self.loadMoreViewId = nil; self.loadMoreViewId = nil;
self.onScrollFuncId = nil; self.onScrollFuncId = nil;
self.onScrollEndFuncId = nil; self.onScrollEndFuncId = nil;
self.itemCanDragFuncId = nil;
self.beforeDraggingFuncId = nil; self.beforeDraggingFuncId = nil;
self.onDraggingFuncId = nil; self.onDraggingFuncId = nil;
self.onDraggedFuncId = nil; self.onDraggedFuncId = nil;
@ -614,4 +674,25 @@ - (void)reload {
[self.view reloadData]; [self.view reloadData];
}); });
} }
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
if (self.itemCanDragFuncId != nil) {
DoricAsyncResult *asyncResult = [self callJSResponse:self.itemCanDragFuncId, @(indexPath.row), nil];
JSValue *model = [asyncResult waitUntilResult];
if (model.isBoolean) {
return [model toBool];
}
}
return true;
}
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSString *fromValue = self.itemViewIds[@(sourceIndexPath.row)];
NSString *toValue = self.itemViewIds[@(destinationIndexPath.row)];
self.itemViewIds[@(sourceIndexPath.row)] = toValue;
self.itemViewIds[@(destinationIndexPath.row)] = fromValue;
if (self.onDraggedFuncId != nil) {
[self callJSResponse:self.onDraggedFuncId, @(sourceIndexPath.row), @(destinationIndexPath.row), nil];
}
}
@end @end