Cocoa: Drag And Drop 简单实现

先吐个槽:iOS开发在国内还是蛮火的,但是Cocoa的中文资料是在是很少,虽然国内有那么几个论坛,但是总体实力与神马StackOverflow啥的还是差多了,一些问题还是到国外的网站查才是王道。

今天遇到了这样的问题:如何实现向ImageWell中拖入文件,然后显示文件的图标并取得文件路径。

一番查找后没有太大收获,决定回归文档,如果你有同样的问题强烈建议仔细阅读一下文档中的Introduction to Drag and Drop,虽然都是英文,但是其实内容并不多,基本一个小时左右完全可以看完,另外结合一下Cocoa DragAndDrop这个官方样例(文档里也有),保证你能弄清楚Drag and Drop的实现方法。

废话不多说了,喜欢自学的就不用往下看了,懒得看文档的可以听我唠叨两句,但是还要结合文档才能真正弄懂,事先说明一下,我也只是个业余Cocoa爱好者,属于需要什么看什么的类型,所以如果犯了什么错误还请留言指正。

贴代码(点击右边箭头展开)(为了简便以下代码仅实现取得文件路径功能)

 


#import 

@protocol DragAndDropImageViewDelegate

- (void)dragFinished:(NSString *)filePath :(int) tag;

@end

@interface DragAndDropImageView : NSImageView {
	id  delegate;
}

- (id)initWithCoder:(NSCoder *)coder;

@property(nonatomic,assign) id  delegate;

@end

 


#import "DragAndDropImageView.h"

@implementation DragAndDropImageView

@synthesize delegate;

- (id)initWithCoder:(NSCoder *)coder {
	NSLog(@"initWithCoder");
	if (self = [super initWithCoder:coder]) {
		[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
	}
	return self;
}

- (NSDragOperation)draggingEntered:(id )sender {
	NSLog(@"draggingEntered");
	NSPasteboard *pboard;
	NSDragOperation sourceDragMask;

	sourceDragMask = [sender draggingSourceOperationMask];
	pboard = [sender draggingPasteboard];

	if ([[pboard types] containsObject:NSFilenamesPboardType]) {
		if (sourceDragMask & NSDragOperationLink) {
			NSLog(@"return NSDragOperationLink");
			return NSDragOperationLink;
		}
	}
	return NSDragOperationNone;
}

- (BOOL)performDragOperation:(id )sender {
	NSLog(@"performDragOperation");

	NSPasteboard *pboard;
	NSDragOperation sourceDragMask;
	int tag = [self tag];

	sourceDragMask = [sender draggingSourceOperationMask];
	pboard = [sender draggingPasteboard];

	if ([sender draggingSource] != self) {
		if ([[pboard types] containsObject:NSFilenamesPboardType]) {
			NSLog(@"ready to modify");
			NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
			NSString *filePath = [files objectAtIndex:0];
			[delegate dragFinished:filePath :tag];
			// modify here to continue
		}
	}
	return YES;
}

@end

@protocol如果没有相应需求可以忽略。

简单解释一下吧,initWithCoder是给InterfaceBuilder用的,其中的registerForDraggedTypes作用是声明Drag and Drop响应的文件类型,当有文件拖入时就会调用draggingEntered方法,该方法中判断是不是要对拖动进行响应,如果响应则继续进入performDragOperation方法,完成处理。还有些中间过程以及详细的解释,以上代码没有涉及,详情请参阅文档。

–以上–

XCode技巧之UserScripts

XCode作为一款强大的IDE,当然也支持脚本功能。通过添加自己的脚本我们可以很方便的完成我们的工作。

在XCode中点击 Edit User Scripts即可方便的添加脚本,而且XCode自带的脚本也相当于给我们提供了丰富的样例。

比如我们添加一个这样的脚本,并为其添加一个快捷键⇧⌘P(快捷键添加方法参考Comments分类中的Un/Comment Selection脚本)

 

#!/bin/sh

echo "%%%{PBXSelection}%%%"
echo "#pragma mark -"
echo "#pragma mark %%%{PBXSelectedText}%%%"
echo "%%%{PBXSelection}%%%"

注意在Output下拉列表中选择Replace Selection。这个脚本的作用是方便我们建立#pragma,首先提供pragma的名字,然后选中这个名字,按相应快捷键,#pragma就自动完成了,很方便。

这个脚本也可以这样写,我们就不用每行都写echo了

 

#!/bin/sh

cat << EOF
%%%{PBXSelection}%%%
#pragma mark -
#pragma mark %%%{PBXSelectedText}%%%
%%%{PBXSelection}%%%
EOF

除了bash脚本,XCode还支持Ruby,Python等多种语言的脚本,比如这个Ruby脚本

 

#!/usr/bin/env ruby -w

# Source: http://allancraig.net/blog/?p=315

properties = ''
synthesize = ''
release    = ''

STDIN.read.each do |line|
	line.gsub!(/\*/, '').strip!
	words = line.split(/\s+/)
	
	label = words.size > 2 ? words[1] : words[0]
	variable = words[-1]
	properties << "@property (nonatomic, retain) IBOutlet #{label} *#{variable}\n"
	synthesize << "@synthesize #{variable}\n"
	release << "[#{variable.chop} release];\n"
end

synthesize << release.chomp

`echo '#{synthesize.chomp}' | pbcopy`
print properties.chomp

该脚本的作用是帮助我们添加@property、@synthesize、还有dealloc方法中相应的release。使用方法是选中.h文件中的成员变量,比如我们选中UIButton *aButton;,复制到要添加@property的位置,再次选中,按快捷键执行脚本,@property就添加完成了,然后到相应的.m文件中,在要添加@synthesize的位置按⌘V粘贴,@synthesize也添加好了,同时复制过来的还有[aButton release];,选中这行,剪切粘贴到dealloc方法中,大功告成。使用这个脚本不仅可以快速添加@property @synthesize,同时也避免了变量名写错所造成的一些问题。

XCode技巧之自定义TextMacros

这篇文章一样,以下基本都是从becoming productive in xcode中取得的

===Text  Macros===

添加自己的TextMacros(XCode 3.2.5测试通过,XCode 4 未测试)

在~/Library/Application Support/Developer/Shared/Xcode/下面新建Specifications文件夹,建立与/Developer/Applications/Xcode.app/Contents/PlugIns/TextMacros.xctxtmacro/Contents/Resources文件夹下类似的*.xctxtmacro文件即可

例子

 

(
  {
    Identifier = objc.hello;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "Hello";
    TextString = "Hello, XCode!";
    CompletionPrefix = "hello";
    IncludeContexts = ("xcode.lang.objc");
  },
  {
    Identifier = objc.property;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "@property Definition";
    TextString = "@property (nonatomic, retain) IBOutlet <#type#> *<#variable#>;";
    CompletionPrefix = "prop";
    IncludeContexts = ("xcode.lang.objc.interface");
  },
  {
    Identifier = objc.rectmake;
    BasedOn = objc;
    IsMenuItem = YES;
    OnlyAtBOL= YES;
    Name = "RectMake";
    TextString = "CGRect aRect = \n\tCGRectMake(<#x#>, <#y#>, <#width#>, <#height#>);";
    CompletionPrefix = "cgrm";
    IncludeContexts = ("xcode.lang.objc.block");
  }
)

上面的代码添加了三个textmacro:

第一个仅仅是测试用,代码中输入hello即有自动补全提示,补全成Hello,Xcode!。

第二个效果是输入prop,然后 ⌃. 自动补全成@property (nonatomic, retain) IBOutlet <#type#> *<#variable#>;的形式,具体实验一下就知道了。

第三个类似,不多说了。

首先注意一下OnlyAtBOL这一行,在新版本的XCode中如果不加上这一行则textmacro无法生效。具体请看这一段的解释:

There is a bug on TextMacros in the latest versions of Xcode (I encourage you to send a bugreport to Apple as I did)

In fact, only TextMacros that have the value “YES” for the key “OnlyAtBOL” will work. If this key is not present (or set to NO), the macro will not respond to autocompletion.
Actually, the new “OnlyAtBOL” key means “Only at Beginning Of Line”, so the autocompletion for this macro will only work if it is triggered in the beggining of a line. But in fact, quite every macro I need everyday comply to this constraint (ifelse, nslog, nss, …) so this is acceptable.
If you want those macros (that does not work because of the lack of OnlyAtBOL set to YES) to work again, you need to add/override their specifications in your “Application Support” directory (in a custom TextMacro file that override them), until the Xcode team fixes that bug.
 
 

XCode快捷键及小技巧

首先推荐个视频:Becoming Productive in XCode  链接里有个demo版的,完全版是收费的,处于对源作者的尊重,不放出下载地址,各位同学自行解决,推荐有能力的购买一下,还是物有所值的,下面这些基本都是视频中提到的

—Workspace—

⌘N          New project

⌘,             Preference

 

⌘E          Zoom editor in

⌥⇧⌘E       Zoom editor in fully

 

—文件切换—

⌘← →     历史编辑点切换

⌘D          Open Quickly,直接输入文件名,支持查看各种.h .m …

⌘↑          .h与.m切换

 

—File Navigation—

← →         按word移动光标,向右移动到word尾部,向左移动到word头部

⌘← →         移动到该行头/尾,

⇧⌥← →      高亮word

⌘← →      高亮行

⌘↑ ↓            移动到文件头/尾

L               光标所在行放置在编辑器中间

⌘L               Goto(行号)

 

2               打开method列表(应该叫Function Popup)(与系统的Spaces切换有冲突,看自己习惯了)

⌘D              BookMark

4               BookMark列表(BookMark Popup)

⌘M          打开Bookmarks Smart Group

 

⌘F              搜索

⌘G ⌘G   (搜索中)下一个/上一个

⌘F           跨文件搜索

+双击        以浮动窗口形式查看定文档

⌘+双击     查看相应文档

 

—Editing Source Code—

TAB             确认当前补全

  ,        补全列表

/               切换到下一个Placeholder

 

⌘←   →    折叠、展开代码段

⌘↑ ↓         折叠、展开所有method段

⌃⌥⌘F        打开、关闭当前代码段高亮功能

 

⌘/               快速注释、取消注释

⌘T           同时编辑当前文件中所有同一变量名

 

—Refactoring—

⌘J           Refractor对话框

 

—Building and Running—

⌘B              Build

⌘B           Build Result窗口

⌘=  ⌘=    查看warnings、errors

⌘Enter         Build & Go

⌘R           打开Console

⌃⌥⌘R        Clear Console      

 

⌥⇧⌘/        打开文档(这是快捷键么)

 

====Tips====

 

—Workspace–

分组(Group)是任意的,并不会真的产生这个目录

Smart Group很实用

右上角那个搜索是支持通配符和正则表达式的,点击放大镜边上的箭头

 

—File Navigation—

类似列表的东西都是支持键盘输入开头的几个字符来选择相应项目的

#pragma mark [something]     类似分隔线的东西,效果在Function Popup中可见,尤其是 #pragma mark – 的时候

 

// TODO: [something]

// MARK: [something]

// FIXME: [something]

// !!!:

// ???:

以上均可在Function Popup有相应效果

 

Editor部分右上角那个右边带箭头的C按钮 打开可查看Superclass(Navigator popup) 默认无快捷键

 

—Refactoring—

Extract:Refactor那个对话框的另一个功能是Extract 支持自动把当前函数中的一段提取到一个新的函数中,对代码重构很有用。使用方法:选择要提取的部分,按⌘J,选择Ectract,然后修改一下函数名即可,系统已经自动把所需的参数添加好了。 

 

—Building and Running—

Preference 》Debugging 》On Start 选择 Show Console 可以默认打开Console

[AppleScript] 小技巧:执行sudo的方法

AppleScript是个强大的好东西,虽然我只是用它来执行一些命令行操作(呃,实际上我还没学怎么进行别的操作),好了废话不多说了,直接进入主题。

在AppleScript中执行终端操作的方法:

    do shell script "SOME COMMANDS"

很简单吧,如果要有管理员权限怎么做,人们自然想到要这样:

    do shell script "sudo SOME COMMANDS"

但是运行一下会出现这个提示:

    AppleScript 错误

    sudo: no tty present and no askpass program specified

解决方法很简单,不要直接把sudo加到里面,而是改成这种形式

    do shell script "SOME COMMANDS" with administrator privileges

这样运行的时候就会弹出那个提示输入管理员密码的那个对话框,sudo的问题就解决了

 

Mac OS 下命令行编辑plist的方法

    一般我们编辑plist文件都是直接打开,用PlistEditor什么的进行可视化编辑,但是如果想用脚本编辑plist文件就要用到命令行了,其实这是一个很简单的命令:defaults

    defaults 命令的帮助如下

'defaults' [-currentHost | -host ] followed by one of the following:

  read                                 shows all defaults
  read                         shows defaults for given domain
  read                    shows defaults for given domain, key

  read-type               shows the type for the given domain, key

  write            writes domain (overwrites existing)
  write            writes key for domain

  rename 
   renames old_key to new_key

  delete                       deletes domain
  delete                  deletes key in domain

  domains                              lists all domains
  find                           lists all entries containing word
  help                                 print this help

 is (  | -app  | -globalDomain )
         or a path to a file omitting the '.plist' extension

 is one of:
  
  -string 
  -data 
  -int[eger] 
  -float  
  -bool[ean] (true | false | yes | no)
  -date 
  -array   ...
  -array-add   ...
  -dict     ...
  -dict-add  

 

    光看这些就大概知道怎么弄了吧,下面是几个例子:

defaults read com.xxx.xxx // 输出文件中所有信息
defaults write com.xxx.xxx   // 改变某个key的value,如果没有该key则添�

 

    注意:在 "com.xxx.xxx" 之后没有 ".plist",开始我犯了这个错误,弄半天不知道为何plist文件中的值没有改变。

   (END)