NSCell copyWithZone

有时候,我们的TableView需要显示的内容是自定义样式的。
这个时候你需要创建一个自定义的Cell来渲染你自己想要的内容。
所以你继承了NSCell,并拥有一个数据对象,用来作为渲染的数据来源。

由于NSTableView需要使用到NSCell的copyWithZone方法在适当的时候复制NSCell,而NSCell默认的copyWithZone方法的实现方式是使用NSCopyObject创建了原始cell对象的一份浅拷贝,所以在复制的时候只会简单的复制指针的值,而不会去深拷贝或者去retain,那么这样程序运行的时候就会出错了。原因就是Cell所引用的某个对象被错误的完全释放了(复制的时候没有retain,但是在释放cell的时候,将这个对象release了一把)。所以在实现copyWithZone的时候可以采用如下方法。

1
2
3
4
5
6
7
8
9
10
- (id)copyWithZone:(NSZone *)zone
{
    id cellCopy = [super copyWithZone:zone];
    /* Assume that other initialization takes place here. */
 
    cellCopy->titleCell = nil;
    [cellCopy setTitleCell:[self titleCell]];
 
    return cellCopy;
}

参考文档:
1.内存管理编程指南:实现对象复制
2.NSTableView copies cells – bug or feature?

Mac 多屏显示 编程

一.多屏介绍

mac可以支持多屏显示,有一个屏幕是作为主屏存在的,也就是那个显示Menu Bar以及Dock的屏幕。别的屏幕可以作为扩展屏,或者作为镜像屏幕(其实就是两个屏幕的内容一样)。

我们可以将Menubar 拖到任意一个屏幕(改变主屏),我们可以调整屏幕的左右或者上下位置,我们可以调整屏幕的错位大小(就是上下或者左右拖动屏幕,最大程度的时候,两个屏幕角对角,以保证俩个屏幕有相交区域,鼠标从一个屏幕进入另一个屏幕,需要从从相交的区域进去)。

二.NSScreen类

在Cocoa中,我们可以通过NSScreen类获取所有screen的信息。
可以通过 + (NSArray *)screens 这个类方法获取所有的screen对象,其中第一个元素是放置Menu Bar的屏幕。“The screen containing the menu bar is always the first object (index 0) in the array returned by the screens method.”

+ (NSScreen *)mainScreen 这个类方法所获得的screen并不一定是包含Menu bar的screen,而是key window(当前响应用户键盘事件的窗口)所在的screen。

我们可以通过- (NSRect)frame方法获得screen的frame,包括menu bar以及dock的区域。
- (NSRect)visibleFrame获得的区域不包含menu bar以及dock占用的区域。

三.计算区域并截图

多个屏幕的情况下,其实系统将所有的screen放置在一个包括所有屏幕的矩形内。
我们可以通过如下方法计算出整个矩形的frame

1
2
3
4
NSRect fullFrame = NSZeroRect;
for (NSScreen *screen in [NSScreen screens]) {
fullFrame = NSUnionRect(fullFrame, [screen frame]);
}


图中坐标显的是对称的,如果你将其中一个screen上下拖动,就会变得不对称,各个screen的坐标也会随着变化。

在Cocoa中可以通过一个非常简单的方式对屏幕进行截图,方法如下,cropRect为整个screen中的frame。

1
2
3
4
CGImageRef cgImageRef = CGWindowListCreateImage(cropRect,
kCGWindowListOptionOnScreenOnly,
kCGNullWindowID,
kCGWindowImageDefault);、

之前我们用screen的frame获得的区域都是以包含menu bar的屏幕的左下角为坐标原点的。
而在CG的相关方法中,则以左上角为坐标原点。
如果只是在单屏幕的情况下,cropRect的计算就非常简单。但是在多屏的情况下,且各个屏幕的分辨率可能不一样,所以就有点复杂了,比如像刚才的情况,想截图副屏左上角的四分之一的区域,这个时候就需要将包含menubar的screen的左上角作为原点在推算出目标区域坐标。

Code Formatting in Xcode

一.介绍

看代码的时候,代码的格式对代码的可读性影响非常大。所以我们一般写代码的时候需要严格遵守编码规范,该空格的地方要空格。
以前在用Eclipse的时候,代码的格式化也非常好用,可是在XCode中Re-Indent Selection格式化功能非常有限,
能否有一些外部的工具用来帮忙格式化呢?
uncrustify 就是这样的一个工具,支持C, C++, C#, ObjectiveC, D, Java, Pawn and VALA等语言的格式化。
uncrustify 可以通过配置文件来控制格式化的细节。当然你可以通过UniversalIndentGUI 这个软件来生成uncrustify的配置。这里有一个objc的配置(下载之后将后缀修改为cfg)。uncrustify_obj_c.
uncrustify的用法为

1
uncrustify -c ~/.uncrustify/mystyle.cfg -f somefile.c > somefile.c.unc

-c 指定配置 -f 指定源文件 然后标准输出为格式化后内容

二.使用

首先你得安装 uncrustify 你可以通过port进行安装。
sudo port install uncrustify
然后在XCode中配置脚本

这个时候你可以全选并在脚本中选择这个刚才的脚本。
注意Xcode4中没有脚本的功能,需要添加Service然后通过apple script来实现,可以参看这里:
http://blog.carbonfive.com/2011/03/10/code-formatting-in-xcode-4/

关于Field Editor

1.介绍

在Cocoa 中,每一个窗口中的所有NSControl对象(包括按钮,输入框NSTextField…),都公用一个称之为Field Editor的对象(其实就是一个NSTextView的单例)。

同一时间只可能有一个NSControl对象处于active状态。所以一个窗口会存在一个公用的Field Editor来处理界面中的所有NSControl对象的输入以及键盘响应。
当一个NSControl对象Active的时候,一个Field Editor会被插入到responder chain中并作为first responder,所以这个时候你获取first responder并不是你的那个Text Field 而是这个Field Editor对象。
这个时候view的层次是这样的,Field Editor的superview是一个_NSKeyboardFocusClipView对象,_NSKeyboardFocusClipView对象的superview才是Text Field。
且Field Editor的delegate被设置成text field。

当你用鼠标点击一个输入框,使其从一般状态进去编辑状态的时候,
First Responder先是这个输入框,然后瞬间交给FieldEditor。所以如果你想测试这个,你可以写一个NSTextField的子类,并在becomeFirstResponder和resignFirstResponder中打log,这个时候你会发现 这两个方法会被连续的调用,之后Field Editor成为了FirstResponder。然后你将焦点再次转移到别的控件,这个时候Field Editor的resignFirstResponder会被调用,然后再在另一个NSControl中进行着上面的过程。

2.Field Editor的delegate方法

其实就是NSTextViewDelegate中定义的那些方法
比如你想改变编辑时按 方向键的行为,你就需要subclass nstextfield并实现下面的方法,
-(BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector

3.自定义的Field Editor

有时候,我们可能需要自定义输入框编辑状态下的外观或者行为,这个时候可能需要使用我们自己定义的Field Editor了,
这里有两个方法,一个是subclass一个nswindow,然后覆盖其
- (NSText *)fieldEditor:(BOOL)createFlag forObject:(id)anObject方法。
返回自定义的Field Editor,
或者在NSWindowControl中实现一个nswindow的delegate方法
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)anObject
这里你可以返回你自定义的Field Editor,如果某些情况下还是需要使用系统默认的Field Editor,那么返回nil就可以了。