五、放置目的地
拖拽源是数据的提供者,放置目的地就是数据的接收者。前面我们也实验过,将自定义的拖拽源拖拽进UITextField后,文本框中会自动填充我们提供的文本数据。同样,对于任何自定义的UIView视图,我们也可以让其成为放置目的地,需要完成如下3步:
1.创建一个UIDropInteraction行为对象。
2.设置UIDropInteraction对象的代理并实现协议方法。
3.将其添加到自定义的视图中。
例如,我们将自定义的UILabel组件用来显示拖拽的文案:
//添加视图
- (void)viewDidLoad {
[super viewDidLoad];
//有关拖拽源的代码 前面已经列举过 这里不再重复
[self.view addSubview:self.dragView];
[self.view addSubview:self.dropLabel];
}
-(UILabel *)dropLabel{
if (!_dropLabel) {
_dropLabel = [[UILabel alloc]initWithFrame:CGRectMake(10, 300, 300, 30)];
_dropLabel.backgroundColor = [UIColor greenColor];
_dropLabel.userInteractionEnabled = YES;
[_dropLabel addInteraction:self.dropInteraction];
}
return _dropLabel;
}
//放置目的地行为对象
-(UIDropInteraction*)dropInteraction{
if (!_dropInteraction) {
_dropInteraction = [[UIDropInteraction alloc]initWithDelegate:self];
}
return _dropInteraction;
}
//这个方法返回是否响应此放置目的地的放置请求
-(BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session{
return YES;
}
//设置以何种方式响应拖放会话行为
-(UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session{
return [[UIDropProposal alloc]initWithDropOperation:UIDropOperationCopy];
}
//已经应用拖放行为后执行的操作
-(void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session{
[session loadObjectsOfClass:[NSString class] completion:^(NSArray<__kindof id<NSItemProviderReading>> * _Nonnull objects) {
self.dropLabel.text = objects.firstObject;
}];
}
上面的代码将我们自定义的拖拽源提供的Hello World拖放进了UILabel组件中。
六、关于UIDropInteraction类
与UIDragInteraction类类似,这个类的作用是让组件有相应放置操作的能力。其中属性如下:
//初始化方法
- (instancetype)initWithDelegate:(id<UIDropInteractionDelegate>)delegate;
//代理对象
@property (nonatomic, nullable, readonly, weak) id<UIDropInteractionDelegate> delegate;
//是否允许多个交互行为
@property (nonatomic, assign) BOOL allowsSimultaneousDropSessions;
七、UIDropInteractionDelegate协议
UIDropInteractionDelegate协议中所定义的方法全部是可选实现的,其用来处理用户放置交互行为。
//放置行为即将响应时触发的方法 返回值确定是否响应此次行为
- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session;
//当上面的协议方法返回YES时会接着调用这个函数
- (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnter:(id<UIDropSession>)session;
//将要处理数据时回调的方法
/*
当数据源数据添加时,这个方法也会被重新调用
这个函数需要返回一个处理行为方式UIDropProposal对象,这个我们后面再说
*/
- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session;
//放置行为相应结束的时候会调用此方法
- (void)dropInteraction:(UIDropInteraction *)interaction sessionDidExit:(id<UIDropSession>)session;
//这个方法当用户进行放置时会调用,可以从session中获取被传递的数据
- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session;
//放置动画完成后会调用这个方法
- (void)dropInteraction:(UIDropInteraction *)interaction concludeDrop:(id<UIDropSession>)session;
//整个拖放行为结束后会调用
- (void)dropInteraction:(UIDropInteraction *)interaction sessionDidEnd:(id<UIDropSession>)session;
//下面这些方法用来自定义放置动画
//设置放置预览动画
- (nullable UITargetedDragPreview *)dropInteraction:(UIDropInteraction *)interaction previewForDroppingItem:(UIDragItem *)item withDefault:(UITargetedDragPreview *)defaultPreview;
//这个函数每当有一个拖拽数据项放入时都会调用一次 可以进行动画
- (void)dropInteraction:(UIDropInteraction *)interaction item:(UIDragItem *)item willAnimateDropWithAnimator:(id<UIDragAnimating>)animator;
需要注意,UIDropProposal类用来进行处理回执,属性方法解析如下:
//初始化方法
/*
typedef NS_ENUM(NSUInteger, UIDropOperation) {
//取消这次行为
UIDropOperationCancel = 0,
//拒绝行为
UIDropOperationForbidden = 1,
//接收拷贝数据
UIDropOperationCopy = 2,
//接收移动数据
UIDropOperationMove = 3,
}
*/
- (instancetype)initWithDropOperation:(UIDropOperation)operation;
//处理方式
@property (nonatomic, readonly) UIDropOperation operation;
//精准定位
@property (nonatomic, getter=isPrecise) BOOL precise;
//设置是否展示完整的预览尺寸
@property (nonatomic) BOOL prefersFullSizePreview;
八、拖拽数据载体UIDragItem类
UIDragItem类用来承载要传递的数据。其通过NSItemProvider类来进行构建,传递的数据类型是有严格规定的,必须遵守一定的协议,系统的NSString,NSAttributeString,NSURL,UIColor和UIImage是默认支持的,你可以直接传递这些数据。
UIDragItem中提供的属性方法:
//初始化方法
- (instancetype)initWithItemProvider:(NSItemProvider *)itemProvider;
//数据提供者实例
@property (nonatomic, readonly) __kindof NSItemProvider *itemProvider;
//用来传递一些额外的关联信息
@property (nonatomic, strong, nullable) id localObject;
//用来自定义每个item添加时的预览动画
@property (nonatomic, copy, nullable) UIDragPreview * _Nullable (^previewProvider)(void);
九、UIDropSession与UIDragSession
在与拖拽交互相关的接口中,这两个是面向协议编程的绝佳范例,首先在UIKit框架中只定义了这两个协议,而并没有相关的实现类,在拖拽行为的相关回调接口中,很多id类型的参数都遵守了这个协议,我们无需知道是哪个类实现的,直接进行使用即可:
UIDropSession:
//继承于UIDragDropSession(提供基础数据), NSProgressReporting(提供数据读取进度)
@protocol UIDropSession <UIDragDropSession, NSProgressReporting>
//原始的dragSesstion会话 如果是跨应用的 则为nil
@property (nonatomic, readonly, nullable) id<UIDragSession> localDragSession;
//设置进度风格
/*
typedef NS_ENUM(NSUInteger, UIDropSessionProgressIndicatorStyle) {
UIDropSessionProgressIndicatorStyleNone, // 无
UIDropSessionProgressIndicatorStyleDefault, // 默认的
} API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos);
*/
@property (nonatomic) UIDropSessionProgressIndicatorStyle progressIndicatorStyle;
//进行数据的加载
- (NSProgress *)loadObjectsOfClass:(Class<NSItemProviderReading>)aClass completion:(void(^)(NSArray<__kindof id<NSItemProviderReading>> *objects))completion;
@end
UIDragSession:
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(watchos, tvos) @protocol UIDragSession <UIDragDropSession>
//设置要传递的额外信息 只有在同个APP内可见
@property (nonatomic, strong, nullable) id localContext;
@end
UIDragDropSession:
//传递的数据数组
@property (nonatomic, readonly) NSArray<UIDragItem *> *items;
//当前操作行为的坐标
- (CGPoint)locationInView:(UIView *)view;
//此次行为是否允许移动操作
@property (nonatomic, readonly) BOOL allowsMoveOperation;
//是否支持应用程序层面的拖拽
@property (nonatomic, readonly, getter=isRestrictedToDraggingApplication) BOOL restrictedToDraggingApplication;
//验证传递的数据是否支持某个数据类型协议
- (BOOL)hasItemsConformingToTypeIdentifiers:(NSArray<NSString *> *)typeIdentifiers;
//验证传递的数据是否可以加载某个类
- (BOOL)canLoadObjectsOfClass:(Class<NSItemProviderReading>)aClass;