五、放置目的地
拖拽源是資料的提供者,放置目的地就是資料的接收者。前面我們也實驗過,将自定義的拖拽源拖拽進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;
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;