您的位置:首页 > 运维架构

OS X系统 Drag & Drop

2016-01-28 18:57 1021 查看
Drag & Drop(拖拽)提供了在应用与OS X系统,不同应用之间,应用内部多种场景下 资源,文件,数据可视化交换的极致的一种用户体验。

我们把可以拖拽的视图(view)或窗口(window)称为 拖放源(Drag Sources),接收拖放的视图或窗口称为 拖放目标(Drag Destination)。



拖放开始时会出现代表拖放源Drag Sources的图标顺着鼠标轨迹运动,直到拖放目标Drag Destination 接收了这个拖放请求,就完成了一次成功的拖放。如果拖放目标不能响应这个拖放请求,代表拖放源Drag Sources的图标会以动画形式弹回到拖放源以前的位置。

拖放源和拖放目标之间数据交换是通过使用系统的剪贴板(NSPasteboard)保存数据来完成的。



整个拖放过程中,涉及到拖放源和拖放目标之间一系列的交互处理流程。


拖放开始

用户鼠标点击NSView/NSWindow触发MouseDown事件,调用beginDraggingSessionWithItems方法开始建立一个拖放的session,开始启动拖放过程。

beginDraggingSessionWithItems需要3个参数,按顺序依次为拖放数据items,鼠标的NSEvent,拖放源代理。

(void)mouseDown:(NSEvent *)theEvent

{

NSMutableArray *draggingItems = [NSMutableArray array];

NSPasteboardItem *pasteboardItem = [NSPasteboardItem new];

DragImageItem *dataProvider = [[DragImageItem alloc]init];

NSData *data = [self.image TIFFRepresentation];

dataProvider.data = data;

[pasteboardItem setDataProvider:dataProvider forTypes:@[kPasteboardTypeName]];

NSDraggingItem *draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteboardItem];

[draggingItems addObject:draggingItem];

[self beginDraggingSessionWithItems:draggingItems event:theEvent source:self.dragSourceDelegate];

}


拖放数据定义

拖放源跟拖放目标方约定数据的type类型,拖放源的数据按type类型存储,拖放接收方注册根据类型来获取数据。


NSPasteboardItem

使用NSPasteboardItem定义拖放携带的基本数据信息。NSPasteboardItem提供了3种基本的定义数据的方法和一个使用代理提供数据的方式。

1.基本的数据定义方法

可以定义NSData,NSString,id类型的数据

- (BOOL)setData:(NSData *)data forType:(NSString *)type;

- (BOOL)setString:(NSString *)string forType:(NSString *)type;

- (BOOL)setPropertyList:(id)propertyList forType:(NSString *)type;

2.使用代理方式提供数据

- (BOOL)setDataProvider:(id )dataProvider forTypes:(NSArray *)types;

实现代理类中定义获取数据的协议方法,实际上实现部分仍然是调用NSPasteboardItem的基本方法把数据存储起来。

(void)pasteboard:(nullable NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSString *)type;

{

[item setData:self.data forType:type];

}


NSDraggingItem

使用NSDraggingItem包装NSPasteboardItem

NSDraggingItem *draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:item];


拖放可视化定义

定义拖放过程中的跟随鼠标移动的图像。

draggingFrame中定义了拖放图像的位置,当有多个拖放对象一起拖放时,每个拖放图像的位置是不一样的。

imageComponentsProvider block块中定义了NSDraggingImageComponent对象, 可以在drawingHandler使用Cocoa绘图方法绘制出代表拖放的图像。block最后返回数据对象,方便表示多个不同的拖放源。

NSDraggingItem *draggingItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteboardItem];

draggingItem.draggingFrame = NSMakeRect(0, 0, 16, 16);

draggingItem.imageComponentsProvider = {

NSDraggingImageComponent *component = [NSDraggingImageComponent draggingImageComponentWithKey:NSDraggingImageComponentIconKey];

component.frame = NSMakeRect(0, 0, 16, 16);

component.contents = [NSImage imageWithSize:NSMakeSize(16, 16) flipped:NO drawingHandler:NSRect
rect {

NSImage *image = ....

[image drawInRect:rect];

return YES;

}];

return @[component];

};


拖放源协议NSDraggingSource

返回允许的拖放操作,代理必须实现的方法

(NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context;

可选3个方法分别是拖放开始,拖放移动,拖放结束时的位置。

@optional

- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint;

- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint;

- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation;


拖放内容


接收拖放

拖放接收方即拖放目标对象实现拖放目标方协议,通过协议的一系列方法依次调用,最终完成跟拖放源的数据交换,结束拖放。


注册接受的拖放类型

NSView 或 NSWindow 提供了注册拖放类型的方法registerForDraggedTypes

[self.window registerForDraggedTypes:[NSArray arrayWithObjects:

NSColorPboardType,

NSFilenamesPboardType,

NSURLPboardType,

NSFilesPromisePboardType

nil]];

可以使用Cocoa系统中预定义的类型,也可以自定义一种类型。


拖放目标方协议NSDraggingDestination

NSView 或 NSWindow 已经继承了NSDraggingDestination协议,需要拖放目标类来实现协议方法。

@protocol NSDraggingDestination

@optional

//拖放进入目标区

- (NSDragOperation)draggingEntered:(id )sender;

//拖放进入目标区移动

- (NSDragOperation)draggingUpdated:(id )sender;

//拖放退出目标区,拖放的图像会弹回到拖放源

- (void)draggingExited:(id )sender;

//拖放预处理,一般是根据拖放类型type,判断是否接受拖放。

- (BOOL)prepareForDragOperation:(id )sender;

//允许接收拖放,开始接收处理拖放数据

- (BOOL)performDragOperation:(id )sender;

//拖放完成结束

- (void)concludeDragOperation:(id )sender;

。。。

@end


拖放接收方处理过程

拖放源的代表图像进入拖放目标区,触发draggingEntered方法。draggingEntered中必须返回一种有效的拖放操作,如果返回NSDragOperationNone,接收拖放,拖放的图像会弹回到拖放源。

鼠标在拖放目标区移动,触发draggingUpdated。

鼠标退出目标区,调用draggingExited方法,拖放的图像会弹回到拖放源。

拖放源代表图像完全进入拖放目标区,释放鼠标按键。触发prepareForDragOperation方法,1. 如果返回NO,拖放的图像会弹回到拖放源,接收拖放。2. 如果返回YES,调用performDragOperation处理拖放,获取拖放数据,进行相关操作。最后调用concludeDragOperation完成一次成功的拖放。


拖放编程实践

前面的章节完整的阐述了拖放处理的全流程,实际应用中很多场景,不需要进行拖放源部分的编程,只需要进行拖放目标方的编程处理。


文件拖放处理

从OSX 系统Finder目录中拖放一个文件到应用。

自定义应用内部接收拖放的view视图类FileDragView,注册拖放类型,实现目标拖放协议NSDraggingDestination。

注册拖放类型

- (void)awakeFromNib {
[super awakeFromNib];

[self registerForDraggedTypes:@[NSFilenamesPboardType]];


}

拖放文件进入拖放区,返回拖放操作类型

- (NSDragOperation)draggingEntered:(id )sender {
NSLog(@"drag operation entered");

NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];

NSPasteboard *pboard = [sender draggingPasteboard];

if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {

if (sourceDragMask & NSDragOperationLink) {
return NSDragOperationLink;
} else if (sourceDragMask & NSDragOperationCopy) {
return NSDragOperationCopy;
}
}
return NSDragOperationNone;


}

执行拖放处理,获取文件路径。可以通过代理或通知形式发送消息通知Controller去处理。

- (BOOL)performDragOperation:(id )sender

{

NSPasteboard *pboard = [sender draggingPasteboard];
NSLog(@"drop now");

if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {

NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];

NSInteger numberOfFiles = [files count];

if(numberOfFiles>0)
{
NSString *filePath = [files objectAtIndex:0];

//if(self.delegate){
// [self.delegate didFinishDragWithFile:filePath];
//}

return YES;

}

}
else{
NSLog(@"pboard types(%@) not register!",[pboard types]);
}
return YES;


}


NSTableView的内部数据拖放处理

自定义NSTableView类DragTableView,在awakeFromNib方法中注册拖放类型为kDragTableViewTypeName.

实现NSTableViewDataSource协议,实现下面3个方法

1.数据复制到NSPasteboard

这里是将拖放时选择的的行rowIndexes 按kDragTableViewTypeName类型注册存储到系统剪贴板

- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet )rowIndexes
toPasteboard:(NSPasteboard)pboard {

// Copy the row numbers to the pasteboard.

NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];

[pboard declareTypes:[NSArray arrayWithObject:kDragTableViewTypeName] owner:self];

[pboard setData:zNSIndexSetData forType:kDragTableViewTypeName];

return YES;

}

validate,返回允许的拖放操作类型

(NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op {

//Add code here to validate the drop

//NSLog(@validate Drop);

return NSDragOperationEvery;

}

3.拖放数据处理。

从剪贴板获取到rowIndexes,进行相关处理。

- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id )info

row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation {
NSPasteboard* pboard = [info draggingPasteboard];
NSData* rowData = [pboard dataForType:kDragTableViewTypeName];
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];

NSInteger dragRow = [rowIndexes firstIndex];

return YES;


}


NSOutlineView的数据拖放处理

将拖放的节点数据 存储到剪切板

(BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard{

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:items];

[pboard declareTypes:[NSArray arrayWithObject:kDragOutlineViewTypeName] owner:self];

[pboard setData:data forType:kDragOutlineViewTypeName];

return YES;

}

2.validate,返回允许的拖放操作类型

- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index {

// Add code here to validate the drop
NSLog(@"validate Drop");

return NSDragOperationEvery;


}

3.从剪贴板获取到拖放的数据items,进行相关处理。

- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id)info item:(id)item childIndex:(NSInteger)index {
NSPasteboard* pboard = [info draggingPasteboard];

NSData* data = [pboard dataForType:kDragOutlineViewTypeName];

NSArray* items = [NSKeyedUnarchiver unarchiveObjectWithData:data];

....
}

[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: