読み込んでいます...

在Mac OS的应用程序开发中,有一个非常好玩的东西,就是Growl,相信用过“鸭子”的人都知道,虽然写这个东西不是什么难事,而且也有一些文章,但是实在无奈那些文章写的太不详细,于是折腾了半天终于折腾出来了,现在我就来说一下如何在应用程序中发送Growl消息。

首先,要了解Growl的话,可以去Growl官方网站,我们只需要下载一个Growl库就可以了。

下载完成后,要做的事情很简单,我们首先添加这个库到我们代码中去,直接拖动过去就可以了,不需要添加现有的framework,因为如果添加现有的话,可能会有些奇怪的诡异错误。注意,如果这个地方怎么拖动过去,怎么放到下面都出现了奇怪的诡异问题的话,直接将这个framework放置到/HD/系统/资源库/Frameworks目录下,做全局的资源库,这样就不会出错。

好了,既然我们添加了资源库,我们就可以写代码了,代码很简单,我们在应用程序中,会有一个MainController,我们在MainController里写一些测试代码即可。

#import <Cocoa/Cocoa.h>
#import
"Growl/Growl.h"

@interface MainController : NSObject<GrowlApplicationBridgeDelegate> {
    IBOutlet NSButton
*button;
}

@property (nonatomic, retain) NSButton *button;

-(IBAction)buttonClicked:(id)sender;
-(void) growlAlert:(NSString *)message title:(NSString *)title;
-(void) growlAlertWithClickContext:(NSString *)message title:(NSString *)title;
-(void) exampleClickContext;
@end 

上面是MainController,下面是详细的代码,不用我过多解释,自己看就行了,实在不行就下载代码看看。:)

#import "MainController.h"

@implementation MainController

@synthesize button;

- (id) init {
    
if ( self = [super init] ) {
        [GrowlApplicationBridge setGrowlDelegate:self];
    }
    
return self;
}

//按钮按下的时候发送消息
-(IBAction)buttonClicked:(id)sender{
    [self growlAlert:
@"test" title:@"haha"];
}

//定义Growl需要的字典
- (NSDictionary *) registrationDictionaryForGrowl {
    NSArray
*array = [NSArray arrayWithObjects:@"example", @"error", nil];
    NSDictionary
*dict = [NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithInt:
1],
                          
@"TicketVersion",
                          array,
                          
@"AllNotifications",
                          array,
                          
@"DefaultNotifications",
                          nil];
    
return dict;
}

//定义当点击了消息的事件
- (void) growlNotificationWasClicked:(id)clickContext{
    
if (clickContext && [clickContext isEqualToString:@"exampleClickContext"])
        [self exampleClickContext];
    
return;
}

//发送消息
-(void) growlAlert:(NSString *)message title:(NSString *)title{
    [GrowlApplicationBridge notifyWithTitle:title
                                description:message
                           notificationName:
@"example"
                                   iconData:nil
                                   priority:
0
                                   isSticky:NO
                               clickContext:nil];
}

//发送可以点击的消息
-(void) growlAlertWithClickContext:(NSString *)message title:(NSString *)title{
    [GrowlApplicationBridge notifyWithTitle:title
                                description:message
                           notificationName:
@"example"
                                   iconData:nil
                                   priority:
0
                                   isSticky:NO
                               clickContext:
@"exampleClickContext"];
}
-(void) exampleClickContext{}
@end 

上面的代码不是很复杂,不过这里只是一个测试程序,点击了按钮之后自动发消息,我们如果要多次使用,需要封装一下,这个封装就很简单了,我就不过多写了。
下载
GrowlTester

892路过 3评论 Mac开发 阅读全文..

在应用程序开发的时候有很多这样的情况,就是我想在全局使用一个快捷键然后我可以随时调用它。例如Things这个应用,你可以在窗体写自己的一些内容,如下图所示。但是更有意思的是,如果我能在全局去使用就好了,因为我们在大多数的时候,都要跳转到这个程序中,然后在程序中查询,如Things,我必须打开Things,然后添加我的内容,或者减少一个内容,如下图。

如果我能在邮件中,或者浏览器中,忽然发现这是我想看的东西,然后添加到Things中,这样多好,于是Things就出了下面这个功能。

也就是说,我可以不在应用程序窗体上进行应用程序操作。

听起来好像有点复杂,因为在操作系统中,任何程序的激活状态都是单一的,比如你不能一边魔兽一边写程序,你必须要“专注”某个应用程序来使用,比如我想发Twitter,而我又在写代码,我必须切换到Twitter客户端去写东西,而无法在当前“激活”程序的程序中去执行,非要将Twitter客户端激活,然后才能执行。

我最近写了一个Password Saver,专门用于保存非常复杂的密码,当我需要密码的时候,我也希望能够马上查询然后关闭,而不需要“激活”程序来使用,查了一些资料,下面来说说如何创建全局快捷键。

首先,这里我假设的是,你有了UI,有了Controller,一切都就绪了。应用程序会有一个主要的Controller,这里我们就叫MainController吧。我们首先需要添加一个库,以便可以注册我们的全局快捷键,我们在左侧中如下操作,Frameworks->Add->Exiting Frameworks->Carbon.framework,添加Carbon框架。添加之后,我们就有了基础设施。

然后我们需要在MainController中写如下代码。

//重写这个方法,这个方法默认定义的
//当这个controller绑定到view的时候,会自动调用这个方法
-(void)awakeFromNib{
    
//注册事件
    EventHotKeyRef myHotKeyRef;
    EventHotKeyID myHotKeyID;
    EventTypeSpec eventType;
    
    
//注册对应的事件,如键盘按钮
    eventType.eventClass=kEventClassKeyboard;
    eventType.eventKind
=kEventHotKeyPressed;
    
//注册快捷键事件
    InstallApplicationEventHandler(&myHotKeyHandler,1,&eventType,self,NULL);
    myHotKeyID.signature
=mhk1;
    myHotKeyID.id
=1;
    
//注册EventHandler
    RegisterEventHotKey(49, cmdKey+optionKey, myHotKeyID, GetApplicationEventTarget(), 0, &myHotKeyRef);
    NSLog(
@"awake");
}

上面的代码就是实现awakeFromNib方法,当窗体载入的时候(前提是绑定了相应窗体),会自动调用这个方法,调用的时候,我们注册了相应的事件。注意这里的RegisterEventHotKey方法,RegisterEventHotKey方法为注册快捷键的键值的方法,其中最主要的参数为(参数1)初始键,(参数2)附加键,初始键这里为空格(49),后面是command key和option key。

注册之后,我们需要写相应事件。

首先我们将如下代码添加到@implementation之前。

OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData);

添加之后,我们在该类中实现上面的方法,代码如下。

OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void  *userData){
    NSLog(
@"call hot key %@", userData);
    
//你想做什么事情都可以在这里做
    return noErr;
}

上面的代码都是写在MainController里的,使用上面的代码就定义了全局快捷键。

但是在全局快捷键中,我们如何拿到自己的对象呢,因为这个Carbon方法是一个C方法,是无法使用self对象的,这里我们可以使用userData来得到返回的数据。可以使用MainController *controller = (MainController *)userData;来获得调用的窗体(也可以说是钩子)。

上面的获得窗体的方法,是获得本身发送窗体的对象,如果你理解了之后,或者使用了之后就会发现,就算全局快捷键使用成功,也无法激活相应的功能,这是为什么呢。首先,这个窗体是一个Window,Window是不能被后台激活的,Window只能在前台激活,也就是说你必须使用该应用程序。

在Cocoa中,我们可以使用HUD Window作为后台激活程序的入口,如下图。该类型的窗口可以独立应用程序,屏蔽其他应用程序窗口而存在在前端显示器的窗口。

我们可以创建一个新的xib文件(可以取名叫QuickWindow),并在新的xib文件中删除默认的窗口,然后拖动一个HUD窗口,然后链接。同时,我们还需要为这个窗体写一个Controller。也就是说,基本上一个窗体的view就应该对应一个Controller。

ok之后,我们可以更改一下响应的方法来调用这个窗体,代码如下(注意,这代码还是在MainController中)。

OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void  *userData){
    NSLog(
@"call hot key %@", userData);
    
//打开QuickWindow
    NSWindowController *add_window = [[NSWindowController alloc] initWithWindowNibName:@"QuickWindow"];
    [add_window loadWindow];
    
//打开窗体
    [add_window showWindow:[add_window window]];
    [[add_window window] makeMainWindow];
    
return noErr;
}

上面的代码就是打开窗体的代码,另一个问题就来了。窗体打开之后,该窗体是默认不会被激活和选中的,我们需要在对应的窗体的Controller中用代码设置默认焦点。如下。

-(void)awakeFromNib{
    
//这里的self_panel就是窗体panel
    
//定义变量的原因是通过在IB链接窗体,然后调用改变量就能够使用窗体
    [[self self_panel] makeKeyAndOrderFront:self_panel];
}

在新的窗体中,还需要设置Panel的属性为Non Activating为True才行,当然,记得也要设置一下firstResponer(事件中拖动即可),如下。

总结:

1.首先添加Framework。
2.然后定义全局快捷键。
3.其次创建新窗口xib和controller。
4.然后修改快捷键响应方法,打开新窗口。
5.给新窗口设置默认焦点。

617路过 2评论 Mac开发 阅读全文..

为什么Table View有这么多文章系列,因为Table View是最常用的也是最复杂也是最可定制性的一个控件,现在我就来说说如何去写一个更加复杂的Table View,通过这个,你就可以知道如何去使用,绑定和更新Table View了。

前面我们写了几种Table View的操作方式,但是1,2都比较初级,3还是比较中等,从这之后,我会写更高级的开发Table View的方式。这次我会提供源代码下载。ok,我们首先来看看程序的样子。

嗯,看上去有那么点点小复杂,那么我来说说这个程序做什么。这个程序是个很经典的搜索程序,当在右上角搜索的时候,我们就需要使用tableview来显示结果。

了解了程序的功能后,我们首先先在Interface Builder上面做出这个UI来,如何去做我就不多说了。拖动几下。

拖动完成后,我们同样要为整个程序写一个Controller,创建一个Controller文件,并写头文件如下。

@interface Controller : NSObject {

    //搜索框,后面链接用
    IBOutlet NSSearchFieldCell *search_field;
    
//绑定的数据数组
    IBOutlet NSMutableArray *array;
    
//array controller
    IBOutlet NSArrayController *array_controller;
}

@property (nonatomic,retain) NSSearchFieldCell *search_field;
@property (nonatomic,retain) NSMutableArray
*array;
@property (nonatomic,retain) IBOutlet NSArrayController
*array_controller;

//搜索结束后响应的事件
-(IBAction)onSearchEnd:(id)sender;

@end

头文件写完之后,我们写内容。代码如下。

#import "Controller.h"

@implementation Controller

@synthesize search_field;
@synthesize array;
@synthesize array_controller;

//初始化
-(id)init{
    [super init];
    array
= [[NSMutableArray alloc] init];
    
return self;
}

-(IBAction)onSearchEnd:(id)sender{

}

@end

上面就是我们的内容代码了,现在还没写完,因为我们还有一些事情没做。

我们前面绑定Table View的方法都是通过字符或者字典去绑定的(Table View系列1,2),这样很好,当很简单的时候我们可以这么做,但是复杂之后,我们就不能那么做了,如系列3,从这之后,我们都要经常按照类的方式去做。例如我有多少个列,我就封装成一个对象,以便调用,这里我们有key和value两个列,我们可以写类代码如下。

创建一个名字为KeyValuePair的类,并写头文件如下。

#import <Cocoa/Cocoa.h>

//有几个列我们就写几个
@interface KeyValuePair : NSObject {
    NSString
*key;
    NSString
*value;
}

//定义为属性
@property (readwrite, copy) NSString *key;
@property (readwrite, copy) NSString
*value;

//自己写一个实例化方法
-(id)initWithKey:(NSString*)nkey initWithValue:(NSString*)nvalue;

然后写内容代码,内容代码比较好写,因为这个只是为Table View的列去做实例化的。

#import "KeyValuePair.h"

@implementation KeyValuePair

@synthesize key;
@synthesize value;

-(id)init{
    [super init];
    
return self;
}

-(id)initWithKey:(NSString*)nkey initWithValue:(NSString*)nvalue{
    self.key
= nkey;
    self.value
= nvalue;
    
return self;
}

@end

好,我们的代码写完了,但是还有很多事情要做,现在要做的就是链接。我们先从ui开始链接。

拖动一个Object和Array Controll到面板上,更改Object类为Controller。

很老套,但是还是要这么做。然后,就是绑定Array Controller了,下面是重中之重。首先打开Array Controller属性,更改Class,并添加相应的字段(就是类里面的字段。),注意,每个类的字段都要添加上去,以便在后面绑定的时候用到。

更改了Array Controller的属性之后,还需要更改绑定的对象,如下图所示。

这里的意思是,我要绑定到Controller类里面的array变量中,回头看看Controller里面的array变量的申明就知道了。

在绑定好了Array Controller之后,我们的每个列都可以通过Array Controller中得到数据,而不需要去其他地方拿数据,每次我们只需要增删Array Controller就行了。现在我们选中一列,如下图所示。

选中列了之后我们在属性中选择绑定的对象,选择绑定到Array Controller的key对象,如下所示。

之后我们就绑定了列到Array Controller里了。这里的绑定关系为,Array Controller绑定KeyValuePair和Controllers里的变量,然后Table View的列绑定Array Controller。

绑定完成之后,我们就可以写代码添加数据了,代码如下所示。

-(IBAction)onSearchEnd:(id)sender{
    
//初始化一个对象
    KeyValuePair *key_value_pair = [[KeyValuePair alloc] initWithKey:search_field.title initWithValue:search_field.title];
    
//删除所有的array数组对象,因为每次搜索都要清空列表
    [array removeAllObjects];
    
//同理
    [array_controller removeObjects:array];
    
//最后添加一个对象
    [array_controller addObject:key_value_pair];
    
//这里还可以扩展搜索,如果通过查询数据等方式返回多个结果
    
//我们可以用for循环添加到array_controller里
    
//array_controller会自动绑定
}

我们每次只需要更改array_controller里的对象,然后刷新Table View的数据即可。

代码可以从下面下载。

代码下载

524路过 1评论 Mac开发 阅读全文..

在应用程序开发过程中,我们经常需要用多个窗体,比如用户配置,添加一个东西,或者有关说明等等,毕竟一个窗体能够做的事情太少了,自然而然,我们就需要多个窗体,好吧,现在我来说说如何使用多窗体。

Mac中多窗体包括这么几种,如下图。

基本上Window就是普通的窗体,而后面几种,可以是Panel或者特殊的窗体。而打开窗体的方式有两种,一种是在同一个nib文件里打开,其他的窗体属于一个nib文件描述,第二种是新的nib文件和新的controller。我们的程序还是和上一次的一样,这里说的还是和程序无关,只是用来演示,如下图。

当我们点击“+”的时候,我们希望打开新窗口,我们分别用两种方式打开,第一种为内置类型,这个类型是直接在改nib文件中拖动一个窗口。我们绘制ui如下所示。

我们绘制完成之后,就需要在老的窗体中打开这个窗体,不过值得注意的是,默认情况下,这个panel会自动的打开,我们选中panel窗体并取消选择“Visible At Lunch”选项,这样就不会在打开的时候打开panel了。ok,我们继续实现我们的要求。

首先,在Controller里写上这个Panel的申明和定义,并初始化。

@interface MainController : NSObject {
    IBOutlet NSPanel
*add_panel;
}

@property (nonatomic,retain) IBOutlet NSPanel *add_panel;

初始化我就不写了,就是一个@synthesize,然后我们在Interface Builder中链接,Control+拖动,拖动Controller对象到新的窗体中,并在灰黑色的下拉菜单中选中add_panel。

链接好了之后,我们就在主窗体中添加了一个新窗口的变量,我们可以使用这个变量来打开和关闭窗口。代码如下。

-(IBAction)onAddButtonClicked:(id)sender{
    
if(![add_panel isVisible])
        [add_panel makeKeyAndOrderFront:sender];
}

-(IBAction)onAddPanelClosed:(id)sender{
    [add_panel orderOut:sender];
}

注意:当我们打开Panel窗体的时候,默认情况下,Mac OS X可以通过按ESC来关闭这个窗体,这样有这种可能,打开panel之后关闭再打开出现程序的假死情况,这个时候我们就需要将Panel窗体的“Release On Close”属性给去掉。

这样我们就打开了一个Panel,Panel的特点就是可以用ESC来关闭,适合做配置窗体,当然,也可以使用新窗口来作为窗体。

我们可以创建一个新窗体(xib文件)。创建完成后,和MainWindow一样,我们也有了一个xib文件并且可以使用Interface Builder来编辑。这是两个不同的窗体,我们需要在主窗体中打开新窗体,相比之下,这样代码更加简单。代码如下所示。

-(IBAction)onAddButtonClicked:(id)sender{
    
if(!add_window)
        add_window
= [[NSWindowController alloc] initWithWindowNibName:@"AddPanel"];
    NSWindow
*wnd = [add_window window];
    
if(![wnd isVisible])
        [wnd makeKeyAndOrderFront:sender];
    
else {
        [add_window showWindow: sender];
    }
}

上述方法能够帮助我们打开一个新窗体,这个打开新窗体的方法是通过打开nib文件的名字打开的,比如这里我创建的是AddPanel.xib,那么就是打开xib。不过记得上述方法还要申明一个add_window,方法很简单,就不重复再说了。(IBOutlet NSWindowController *add_window;)

总结一下,虽然有两种打开窗体的方式,但是不同的方式针对不同的需求,个人认为最好还是把两个分的很清楚的窗体分开,而在一起的窗体使用第一种Panel形式最好。

564路过 3评论 Mac开发 阅读全文..

这篇文章要说的东西不多,而且也没那么复杂,只是作为笔记的用途,以后自己可能再回过头写的时候可能会需要这样的资料。

写一个简单的程序,这个程序大概叫Password Saver,我相信应该有很多这样的程序了,而且这样的程序也不是很复杂,不过在Windows下用到过一个比较顺手的,在Mac下还没有,所以就准备自己写一个,也算是自己用的一个小工具。这篇文章不是围绕这个程序来写的,所以,并不会和程序有多大关系。首先还是看看我们的效果。

简单说一下要做的事情,输入一个key,能够得到密码。比如我设置了我这个博客的后台的用户名,然后想要得到密码,我只需要输入用户名,然后就可以得到密码了,我可以将这个key设置为guojingkey,然后得到密码。当然,程序有很多其他部分,我就不展现了。

这里有两个要做的,一个是输入事件,一个是Read-only的设置。当然,我们还是会写一个Controller,鉴于前面我写过多次,这里就不重复了,要看的请看前面的文章。

当我们在key里面输入一个key的时候,输入完毕,按回车,密码就出现了,不需要再去点Get(保留是因为有的习惯不同,可能喜欢点Get),所以这里就需要有输入完成的事件。当然,密码需要是Read-only的,不能随意乱改,否则体验不好。

事件这个地方比较好弄,我们选择Textfield,然后在属性里选择事件触发的情况。这里有两种,一种是Sent On Enter Only和Sent On End Editing,意思为按Enter的时候触发事件和结束编辑的时候触发。

如上图中的Action这个属性,这里我选择了Sent On End Editing,结束编辑的时候触发事件。这个选择和前面的不同,前面的是按回车触发事件,这个是在按回车和丢失焦点的时候触发的,所以这个是更好的。事件绑定的时候我们只需要绑定selector即可。当输入完毕后会自动调用相应的事件,如下。

事件的问题解决了,我们还需要解决只读的问题。在Mac下开发并没有类似Windows开发的消息模式的方式,在封装上面也没有.NET来的方便,例如只读在.NET下开发只需要设置一下Readonly属性即可,在Mac下,我们还需要绑定属性。

不知道如何设置,国内没有这方面的资料,于是查了一下Apple,发现需要绑定一个NSNumber属性,这个属性可以不赋值。比如我们写一个这个View的Controller,代码可以写如下。

@interface MainController : NSObject {
    IBOutlet NSNumber
*editable;
}

@property(readonly) NSNumber *editable;

上面的代码编写完成后,我们还需要绑定一下,我们选中TextFieldCell,注意是Cell,如下图。

选中之后,我们在右侧的属性中绑定我们的属性,如下图。

绑定的类就是我们的Main Controller(这个是我们自己写的Controller,不明白看前面的文章),然后相应的属性就是我们刚才写的editable了。

这样,事件和Read-only属性就都解决了。关于其他的属性配置,也可以参考这个绑定方式,基本上换汤不换药,绑定何种数据类型,还是直接查官方的开发资料比较好。我就不一一列举了。

483路过 1评论 Mac开发 阅读全文..

在读本篇文章之前,推荐先看看Mac开发 – Table View(1)Mac开发 – Table View(2),前两篇文章分别讲解了TableView的一些基本功能和绑定方法,当只有一列的时候,我们可以通过ArrayController的方法绑定,当有两列的时候,我们可以通过Dictionary的方法绑定,但是有多列的时候,我们并不好绑定,现在我就来说说用对象和ArrayController的方法绑定,同时,我们这次实现的程序要复杂一点。

这个程序很简单,我们需要的是点击Add new添加一行,点击Delete删除一行,首先我们先使用Interface Builder创建一个这样的窗体,创建完成后我们可以开始写我们的类。

创建一个People的类然后编写如下代码,以下是头文件代码。

#import <Cocoa/Cocoa.h>

@interface People : NSObject {
    NSString *personName;
    NSString
*infor;
}

@property (readwrite, copy) NSString *personName;
@property (readwrite, copy) NSString
*infor;

@end

头文件代码写完之后我们就要实现这个代码,实现的代码如下所示。

#import "People.h"

@implementation People

-(id)init
{
    [super init];
    personName
= @"GuoJing";
    infor
= @"I’m a programmer";
    
return self;
}

-(void)dealloc
{
    [personName release];
    [infor release];
    [super dealloc];
}

@synthesize personName;
@synthesize infor;

@end

好了,我们的People类实现了之后,我们就要为我们的应用程序创建一个Controller,新建一个类并命名叫Controller,然后在Interface Builder里面拖动一个Object控件到xib窗体中。

拖动完成之后,我们同样要更改这个Object绑定到Controller类,如下图。

更改完成后,我们需要实现Controller,头文件代码如下。

#import <Cocoa/Cocoa.h>

@interface Controller : NSObject {
    //定义NSMutableArray用来绑定
    IBOutlet NSMutableArray *persons;
    IBOutlet NSButton
*addButton;
    IBOutlet NSButton
*delButton;
}

@property (nonatomic,retain) IBOutlet NSMutableArray *persons;
@property (nonatomic,retain) IBOutlet NSButton
*addButton;
@property (nonatomic,retain) IBOutlet NSButton
*delButton;

-(void)setPersions:(NSMutableArray *)a;

@end

同样实现代码如下。

#import "Controller.h"
#import
"People.h"

@implementation Controller

-(id)init
{
    [super init];
    persons
= [[NSMutableArray alloc] init];
    
return self;
}

-(void)dealloc
{
    [self setPersions:nil];
    [super dealloc];
}

-(void)setPersions:(NSMutableArray *)a
{
    
if(a==persons)
        
return;
    [a retain];
    [persons release];
    persons
= a;
}

@synthesize persons;
@synthesize addButton;
@synthesize delButton;

@end

好了,我们写完代码之后,我们就需要更改ArrayController的属性了,我们的ArrayController应该绑定的是People类,我们点击ArrayController然后更改属性如下。

类绑定完之后我们还需要绑定相应的数组字段,前面的代码我们写了一个数组,这里还要设置如何绑定这个数组,如下图。

这里我们绑定的是Controller类下的persons,这样这个ArrayController的绑定设置就完成了。完成之后,我们还需要绑定到相应的TableView的列上面,我们可以按照如下方式绑定。

首先我们先选中一个列,记住是如下图的一个列,TableView控件包含很多控件,我们只需要选择其中的列,选中之后,我们在属性中选择绑定。

如上图,我们绑定了ArrayController并且将这一列选择了personName,同理我们还需要绑定剩下的一列,绑定之后就完成了绑定。(同前面的方法绑定)

绑定完成之后,我们就需要给add和delete按钮添加事件了,这里最简单的事件添加方法就是直接绑定add和delete按钮到ArrayController中的add和delete方法。我们只需要Control+鼠标把ArrayController的add方法拖动到按钮上即可绑定,绑定后如下图。

现在我们就完成了一个比较复杂的TableView的操作了。

总结一下,首先我们先编写每个TableView的对象(即Person),编写完成后更改ArrayController的绑定,绑定到Person,并更改相应的Controller数组的绑定(这样就知道如何绑定和绑定到哪),最后绑定相应的操作方法,如add,delete等。

583路过 1评论 Mac开发 阅读全文..

在开发应用程序的时候,特别是当今网络特别充足和重要的时候,网络应用和桌面应用史无前例的整合起来,其界限也越来越模糊。所以网络应用可以向桌面应用开放API,然后桌面应用去调用,这样可以方便的维护一份代码,并且可以push更新到用户手里。

而在设计的时候,通常情况下都是客户端发送相应的Http请求,并包含某些内容,发送完毕之后,等待服务器端的响应。而在服务器端,得到请求的内容并生成相应的结果返回即可。虽然这个过程很好理解,很简单,但是在Mac和iPhone开发上,还是有点麻烦,而且国内资料又少,所以这里我写一下如何实现发送HttpRequest请求,方便后面的同学。

这段代码可以适用与Mac OS X和iPhone应用。

界面我就用Mac的桌面应用做,因为简单,效果一样,界面可以做成如下模样,如何连接就各位自己去连接了,我前面的文章说的很明白了。

当我们按下按钮的时候,就去获取相应的网站/服务器的返回的请求。调用方法如下。

- (IBAction)buttonClicked:(id)sender
{
    NSString
*receive = [RequestSender sendRequest:@"http://wt.jguoer.com"];
    textbox.title
= receive;
    NSLog(
@"Clicked");
}

RequestSender是我写的一个类,这个类就是用于发送Http请求的,具体代码如下。 

#import <Cocoa/Cocoa.h>

@interface RequestSender : NSObject {

}

+ (NSString*)sendRequest:(NSString*)url;

@end

实现代码如下所示,我已经写了详细的注释了,所以我就不需要再多说什么了。

+ (NSString*)sendRequest:(NSString*)url
{
    
//准备发送httprequest
    NSString *urlString = url;
    NSMutableURLRequest
*request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:
@"GET"];
    
    
//设置http头
    NSString *contentType = [NSString stringWithFormat:@"text/xml"];
    [request addValue:contentType forHTTPHeaderField:
@"Content-Type"];
    
    
//创建http内容
    
//NSMutableData *postBody = [NSMutableData data];
    
//[postBody appendData:[[NSString stringWithFormat:@"<xml>"] dataUsingEncoding:NSUTF8StringEncoding]];
    
//[postBody appendData:[[NSString stringWithFormat:@"<your xml format code here/>"]
    //dataUsingEncoding:NSUTF8StringEncoding]];
    
//[postBody appendData:[[NSString stringWithFormat:@"</xml>"] dataUsingEncoding:NSUTF8StringEncoding]];
    
    
//设置发送内容
    
//[request setHTTPBody:postBody];
    
    
//获取响应
    NSHTTPURLResponse* urlResponse = nil;  
    NSError
*error = [[NSError alloc] init];  
    NSData
*responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];  
    NSString
*result = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    
    
//返回的http状态
    NSLog(@"Response Code: %d", [urlResponse statusCode]);
    
    
//获取返回的内容
    if ([urlResponse statusCode] >= 200 && [urlResponse statusCode] < 300)
    {
        NSLog(
@"Response: %@", result);
        
return result;
        
//执行你想要的内容,代码可以写在这里
    }
    
    
return @"Return value";
}

我们获取的result就是我们想要的HttpResponse了,这样就成功发送了一个请求并获取了相应的内容。

HttpRequestMac.zip (29.12 kb)

我们在Mac开发 – Table View(1)里面使用了Table View这个控件,可是这个控件是一个非常复杂的控件,我们使用于Array Controller绑定的方式去绑定数据有一个明显的缺点,就是只能绑定一个列的数据,这在很多情况下是没有多大的用处的,这个时候我们就需要绑定多个列的数据,当绑定多个列的数据的时候,我们可以使用Dictionary Controller绑定。

我们首先创建一个项目,然后将UI制作如下图即可。

我们在项目中添加一个文件(类),继承自NSObject,注意,这里不需要使用什么NSView的子/派生类,我们只需要使用NSObject类即可。取名NSDicController,然后我们写头文件如下。

#import <Cocoa/Cocoa.h>

@interface NSDicController : NSObject {
    IBOutlet NSWindow *window;
    IBOutlet NSTableView
*tableView;
    IBOutlet NSDictionaryController
*dictionaryProxy;

}

@property (assign) IBOutlet NSWindow *window;
@property (assign) IBOutlet NSTableView
*tableView;

@end

写完头文件之后,写内容代码。

#import "NSDicController.h"

@implementation NSDicController
@synthesize window;
@synthesize tableView;

@end

写到这里都不是那么难理解,好,我们现在先连接一下我们的元素,因为前几篇文章我已经说的很清楚了,所以这里我就不再详细的讲解如何连接了,我这里给出一些示例即可。首先我们还是加一个object,如下所示。

加完之后,我们更改为我们的相应的类即可,更改之后,我们再连接相应的内容到UI上,依旧还是Control+鼠标连接,连接后连接器如下所示。

这样,我们就连接了我们的基本要素,连接完成后,我们就需要编写代码来实现多列的功能实现了,这里我们需要使用的一个方法是awakeFromNib,连接后,当应用程序启动会自动调用和执行awakeFromNib这个方法,我们更改方法如下,代码可能不那么好理解,我加了一些注释。

#import "NSDicController.h"

@implementation NSDicController
@synthesize window;
@synthesize tableView;

- (void)awakeFromNib
{

    
//清空现有的table的列
    NSArray *tableColumns = [NSArray arrayWithArray:[tableView tableColumns]];
    
    
for (NSTableColumn *tableColumn in tableColumns)
        [tableView removeTableColumn:tableColumn];
    
    
//新增加一个列
    NSTableColumn *keyTableColumn = [[[NSTableColumn alloc] initWithIdentifier:@"key"]

    autorelease];
    [tableView addTableColumn:keyTableColumn];
    
    
//新增加一个列
    NSTableColumn *valueTableColumn = [[[NSTableColumn alloc] initWithIdentifier:@"value"]
    autorelease];
    [tableView addTableColumn:valueTableColumn];
    
    
//初始化字典
    dictionaryProxy = [[NSDictionaryController alloc] initWithContent:nil];
    
    
//设置绑定
    [keyTableColumn bind:NSValueBinding toObject:dictionaryProxy withKeyPath:@"arrangedObjects.key"
    options:nil];
    [valueTableColumn bind:NSValueBinding toObject:dictionaryProxy withKeyPath:
@"arrangedObjects.value"
    options:nil];
    
    
//更改字典后自然更改绑定的数据
    [dictionaryProxy setContent:[NSDictionary dictionaryWithObjectsAndKeys:@"obj 1", @"key to obj 1",
    @"obj 2", @"key to obj 2", nil]];
}

@end

通过上面的代码,我们就能够自动的在程序启动的时候添加相应的列,最后效果如下所示。

虽然我们这里使用的是启动时就绑定并添加了数据,但是我们很多时候需要执行按钮按下的方法之后才添加数据,不过不要紧,当我们设置了控件于一些数据的绑定之后,我们只需要更改相应的数据源即可。例如现在这里我们讲table的两个列绑定了一个dictionary,以后我们需要更改列的值的时候,只需要更改这个数据字典即可,系统会自动帮我们更新View。例如可以连接button并写相应的代码去进行操作。

- (IBAction)buttonClicked:(id)sender
{
    
//更改字典后自然更改绑定的数据
    [dictionaryProxy setContent:[NSDictionary dictionaryWithObjectsAndKeys:@"obj 1", @"key to obj 1",
    @"obj 2", @"key to obj 2", nil]];
}

只要在相应的作用域之内,我们只需要更改相应的dictionary的数据即可,无须关心其他部分。

PS:在这里我们就看到一个MVC的一个非常好的地方,就是视图只管视图,数据只管数据,控制器只管控制器的一个优点。

521路过 2评论 Mac开发 阅读全文..

Text View算是Mac里面最常用但是最复杂的控件之一,我也是用了一下午的时间才知道如何使用这个控件,Mac控件并不像VC或者C#一样直观上是消息通信模式,也不是通过事件,添加相应对象的子节点去增加控件内容。Text View算是比较特别的控件,复杂的Text View控件可以做出很多复杂的效果,在iPhone上更是如此,最有名的软件如Twitterrific的Mac版本和iPhone版本显示Twits都是使用的Text View。Text View就好像C#中的Gridview,或者C++的listbox。

首先我们创建一个项目叫TextViewTest,创建好之后,我们在Class文件夹下面添加一个类,取名叫Controller(这里可以创建位NSObject或者位NSView的子类都可以)。

创建好之后,我们打开MainWindow.xib文件,然后制作UI如下图所示,我们的需求很简单,单击按钮添加一行数据。

编写好了UI之后,我们就需要连接一些基本的东西,这里我们拖动一个Object对象进来,并改成相应的Class。

拖动过来还需要更改为相应的Controller。

更改后,我们的这个Controller就是我们的Controller类的一个对象了。这里我们需要写一下Controller类,头文件我们可以定义如下。

#import <Cocoa/Cocoa.h>

@interface Controller : NSView {
    //定义数据源数组
    NSMutableArray *array;
    
//定义数据
源数组的Controller,连接用
    IBOutlet NSArrayController *arrayController;
    IBOutlet NSButton
*button;

}

@property (nonatomic,retain) IBOutlet NSButton
*button;

- (void) buttonClicked:(id)sender;

@end

然后我们实现的代码很简单,每次点击都添加一个test字符串。

#import "Controller.h"

@implementation Controller

@synthesize button;

- (void) buttonClicked:(id)sender
{
    
//添加字符串
    [arrayController addObject:@"test"];
}

- (id)initWithFrame:(NSRect)frame {
    self
= [super initWithFrame:frame];
    
if (self) {
        
// Initialization code here.
    }
    
return self;
}

- (void)drawRect:(NSRect)dirtyRect {
    
// Drawing code here.
}

@end

编写完成后,我们还需要连接各个对象,例如button连接到代码中的button,button的按下的事件连接到buttonClicked。这里我就不累述了。

Text View是一个可以进行数据绑定的控件,所以前面我才说和C#的GridView很像,不过没有VS那么智能了,这里Text View绑定的是类似数组的对象,所以这里我们还需要拖动一个Arrray Controller,进行数据的连接。

拖动完成之后,我们就需要连接Controller和Array Controller,这两个对象组成一个类+数据源的结构。

拖动完成后,我们需要绑定相应的对象,例如上面绑定的是arrayController,也是我们前面代码定义的一个变量,否则看不到这个连接项。这个时候我们连接前面定义的arrayController,就连接了Array Controller。连接后,我们还需要设置绑定,我们在Array Controller的属性窗口中设置即可。

上图我们就将Array Controller绑定了Controller里的array变量。也就是说Array Controller现在已经成为了数据源,任何array对象的改变都会影响到这个Array Controller。

现在我们再打开UI,找到相应的一列,选中该列(而不是选中大的控件,是其中一列,如下图)。

选中该列,然后在属性窗口中绑定相应的数据源即可,如下图。

绑定完成后,我们只需要操作arrayController里面的元素就可以了,而不需要关心如何绑定到Text View中的,如下代码。

[arrayController addObject:@"test"];

 连接完成后的Controller和Array Controller里的连接如下两图所示。

小结一下:

使用Text View的基本步骤如下。

  • 写Controller,并连接。
  • 创建Array Controller并绑定。
  • 绑定相应的Text View的列。

虽然看上去很简单,但是实践起来还是有一定难度的,建议各位自己动手实践一下。

TextViewTest.zip (29.47 kb)

559路过 3评论 Mac开发 阅读全文..

按照我的个性,不会把Hello World写好几遍,但是Mac开发确实和一般的开发过程有点不太一样,说一般,可能是因为我用Windows用太久了吧,对事件和消息模式感到特别的熟悉,但是对Mac的连接模式,感觉还是有点怪怪的,所以在做Mac下的应用程序开发的时候,还是花了一点时间,所以,刚好有空,就纪录一下。由于HelloWorld和iPhone前面说的差不多,所以这里顺带还会说一下事件。

同样,还是打开XCode,但是我们这里创建的是一个Mac应用程序,如下图。

这里,我们创建一个HelloWorld应用程序,创建完成之后,我们就能够看到一个非常熟悉的XCode界面。

虽然我们有XCode这个IDE帮助我们开发Mac应用程序,而且好像看上去和开发iPhone应用程序差不多,这个时候你说不定已经开始摩拳擦掌了,但是可惜的是,在开发一个Mac的Hello World应用程序,并执行一个事件还不是那么容易的问题,当然,这个时候如果我们退缩了的话那就永远写不了一个HelloWorld程序了。

首先,我们打开MainMenu.xib这个文件,首先我们还是会看到熟悉的Interface Builder。

我们做主界面如上图所示即可,上面一个TextField,下面一个Button。一个非常简单的应用程序,我们想做的是当单击按钮,在文本框中显示Hello World。这个时候,可能有iPhone开发经验的你会想我们是不是要加一个Controller,是View还是什么呢?其实iPhone开发和Mac开发有一些相同的地方,但是Mac开发还是有一些复杂的,这里我们首先要创建一个文件,并取名叫MainController。

我们分别在文件的头文件和代码文件中编写如下代码,加粗的是后来加上的意思。头文件如下(注意,我们这里也可以不用创建继承自NSView的类,直接继承NSObject也可以):

#import <Cocoa/Cocoa.h>

@interface MainController : NSView {
    IBOutlet NSButton *button;
    IBOutlet NSTextFieldCell
*textbox;

}

@property (nonatomic,retain) NSButton *button;
@property (nonatomic,retain) NSTextFieldCell
*textbox;

- (IBAction)buttonClicked:(id)sender;

@end 

上面代码想做的事情已经很明显了,现在我们在代码文件中实现如下代码: 

#import "MainController.h"

@implementation MainController

@synthesize button;
@synthesize textbox;

- (IBAction)buttonClicked:(id)sender
{
    textbox.title
= @"Hello World";
}

- (id)initWithFrame:(NSRect)frame {
    self
= [super initWithFrame:frame];
    
if (self) {
        
// Initialization code here.
    }
    
return self;
}

- (void)drawRect:(NSRect)dirtyRect {
    
// Drawing code here.
}

@end

上面的代码很容易理解吧,就是定义一个变量,但是难的是如何去连接这些东西,这也是我觉得Mac里面最麻烦的一个事情。

首先,我们打开Interface Builder然后在Library中拖动一个新的Object到nib中,如下图。

拖动完成之后,我们更改这个Object的Class名称,让它映射到相应的Controller上面去。

我们将相应的类的名称更改为我们刚才创建的Controller的名称,这样这个类就知道如何去映射代码文件了。

更改完成后,我们右键单击Main Controller,可以看到弹出的黑色的下拉菜单,里面有我们编写的button和textbox,我们连接相应的对象即可。

连接完成相应的对象之后,我们就需要连接事件了,我们单击按钮,并在事件区域找到相应的事件,然后拖动到Main Controller中,然后指定相应的事件即可。

然后我们就连接了事件,当事件连接完成之后,我们就连接了所有的对象,需要的事件了,这样,当我们运行程序的时候,就可以实现单击按钮显示HelloWorld的需求了,虽然连接事件和代码结构比较复杂和麻烦,但是时间用久了之后,就会大概知道是怎样一个步骤了,这里我小结一下。

  • 编写UI,xib文件。
  • 编写Controller类,编写相应的控件的对象和事件代码。
  • 连接对象和事件。

基本上经过上面三个步骤,就能够连接事件并执行相应的代码了。不过Mac开发要比iPhone开发复杂一些,下面的一些文章,我会分享一下关于使用控件的一些复杂的地方,刚好我也在自学,如果有错误的,请联系我更改。:)

599路过 1评论 Mac开发 阅读全文..