章节
Drag & Drop除了基础APIs外,还为tableView,collectionView、textView提供了高级APIs,所以本系列分为以下几个部分:
- 基础视图(本篇)
- UITableView
- UICollectionView
- UISpringLoadedInteraction
概述
Drag & Drop是iOS 11中新增的API,允许App内或不同App之间通过手势Drag(拖拽)、Drop(投放)来交换数据。
1-1
由于iPad的分屏多任务支持,可以使用Drag、Drop的全部特性,iPhone只能在App内部使用。
API结构
Drag、Drop基础部分:
- UIDragInteraction、UIDropInteraction
- UIDragSession、UIDropSession
- UIDragPreview、UIDragPreviewParameters
- UIDragItem、NSItemProvider
tableView:
- UITableViewDragDelegate, UITableViewDropDelegate, UITableViewDropCoordinator, UITableViewDropItem, UITableViewDropPlaceholderContext
collectionView:
- UICollectionViewDragDelegate, UICollectionViewDropDelegate, UICollectionViewDropCoordinator, UICollectionViewDropItem, UICollectionViewDropPlaceholderContext
示例
本篇将通过一个demo App与系统的照片App之间通过Drag、Drop来传递图片,解释部分基础API的使用。
- 首先,新建一个Xcode project,在ViewController中添加一个UIimageview,设置一个初始image。
2. 实现Drag功能
为了能够将UIimageview中的image拖拽到相册中,需要实现Drag功能,能够响应Drag手势。
2.1 确定Drag目标,本Demo中使用UIimageview。使用UIDragInteraction来实现,在viewDidLoad
中添加
[self customEnableDraggingOnView:self.imageView dragInteractionDelegate:self];
/**
add dragging support for specify view
*/
- (void)customEnableDraggingOnView:(UIView*)view dragInteractionDelegate:(id<UIDragInteractionDelegate>)delegate {
if (!view) {
return;
}
UIDragInteraction *dragInteraction = [[UIDragInteraction alloc] initWithDelegate:delegate];
[view addInteraction:dragInteraction];
}
2.2 添加interaction后,当系统识别到针对imageView的Drag手势后,会调用UIDragInteractionDelegate的方法:
/*
* 需要在此回调中提供拖拽的数据封装,因为虽然拖拽的是imageView,但是实际上需要传递的数据应该为imageView中的image。
*/
- (NSArray<UIDragItem *> *)dragInteraction:(UIDragInteraction *)interaction itemsForBeginningSession:(id<UIDragSession>)session {
if (self.imageView.image == nil) {
return nil;
}
/*
* item provider为Drag、Drop之间传输数据
* Drag、Drop支持的数据类型:
CNContact
CNMutableContact
CSLocalizedString
MKMapItem
NSAttributedString
NSMutableString
NSString
NSTextStorage
NSURL
UIColor
UIImage
*/
NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithObject:self.imageView.image];
UIDragItem *item = [[UIDragItem alloc] initWithItemProvider:itemProvider];
return @[item];
}
2.3 在Drag开始后,如图1-1所示,手指处会显示preview预览效果,默认preview视图为Drag目标本身,本例中为imageView。如果需要自定义preview,则需要实现代理方法- (nullable UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id<UIDragSession>)session
,那什么时候需要自定义preview呢?举个栗子,假如2.1中将Drag interaction添加到self.view上,那么默认Drag preview就是self.view,显然这并不符合要求,就需要将preview改成self.imageView。
- (nullable UITargetedDragPreview *)dragInteraction:(UIDragInteraction *)interaction previewForLiftingItem:(UIDragItem *)item session:(id<UIDragSession>)session {
UIDragPreviewParameters *pama = [[UIDragPreviewParameters alloc] init];
pama.visiblePath = [UIBezierPath bezierPathWithRect:self.imageView.frame];
pama.backgroundColor = [UIColor redColor];
UITargetedDragPreview *preview = [[UITargetedDragPreview alloc] initWithView:self.otherImageView parameters:pama];
return preview;
// return nil;
}
此处,已经实现了Drag,iPad模拟器运行,将demo与照片App分屏,按住imageView Drag到照片App里,松手Drop,可以发现imageView的image就保持到照片App里了。
3. 实现Drop功能
为了能够接受从其他App Drag到本App里的数据,需要使用Drop API。
3.1 确定接收数据的目标,本例为2.1中添加的imageView
[self customEnableDroppingOnView:self.imageView dragInteractionDelegate:self];
/**
add dropping support for specify view
*/
- (void)customEnableDroppingOnView:(UIView*)view dragInteractionDelegate:(id<UIDropInteractionDelegate>)delegate {
if (!view) {
return;
}
UIDropInteraction *dropInteraction = [[UIDropInteraction alloc] initWithDelegate:delegate];
[view addInteraction:dropInteraction];
}
3.2 当Drag目标到Drop interaction所附着的view坐标范围内,会调用- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session
方法:
/*
* 此方法决定是否响应Drop session
* 本例中只接受单个的image Drop
*/
- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session {
NSLog(@"---->>>>>>> canHandleSession");
// only support image
return [session hasItemsConformingToTypeIdentifiers:@[(__bridge_transfer NSString*)kUTTypeImage]] && session.items.count==1;
}
3.3 定义数据传输方式
/*
当
- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session
返回YES后,就会调用此代理,
UIDropProposal决定数据传输的方式,一般来说,
1️⃣ 外部App drag到本App,使用copy来复制数据;
2️⃣ App内部drag时,只移动数据
*/
- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session {
// localDragSession 不为nil时,表示App内部Drag、Drop
if (session.localDragSession != nil) {
//NSLog(@"---->>>>>>> UIDropOperationMove");
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationMove];
}
// 外部Drag 目标
else {
CGPoint loc = [session locationInView:self.view];
if (CGRectContainsPoint(self.imageView.frame, loc)) {
//NSLog(@"---->>>>>>> UIDropOperationCopy");
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
}
else {
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationForbidden];
}
}
return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCancel];
}
3.4 Drop发生时
/*
在Drop interaction的View上Drop内容时调用,具体数据的数据、UI表现在此方法中定义。
*/
- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session {
// App内Drag、Drop,只移动imageView的位置
if (session.localDragSession != nil) {
//self.imageView.center = [session locationInView:self.view];
}
else {
// dragItem来自外部App时,需要加载具体的数据
// 本例中即为从照片App中拷贝Drag图片到本App,然后使用imageView显示
for (UIDragItem *item in session.items) {
__weak __typeof(self) weakSelf = self;
[item.itemProvider loadObjectOfClass:[UIImage class] completionHandler:^(id<NSItemProviderReading> _Nullable object, NSError * _Nullable error) {
if (error) {
NSLog(@"----->>>>>> load data err: %@", [error localizedDescription]);
return ;
}
if (!object) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
__strong __typeof(self) self = weakSelf;
self.imageView.image = (UIImage*)object;
});
}];
}
}
}
3.5 Drop功能完成,运行App,从照片App Drag图片到imageView上,然后Drop图片,可以发现,imageView就会显示所Drag的图片了。
Next:
- Drag and Drop with Collection and Table View
参考资料
文档
视频
- Introducing Drag and Drop
- Mastering Drag and Drop
- Drag and Drop with Collection and Table View
- Data Delivery with Drag and Drop
代码
Tips
文章难免会有错误、理解错位的地方,请不吝指教。🙏
网友评论