`
jiagou
  • 浏览: 2532398 次
文章分类
社区版块
存档分类
最新评论

Target-Action模式

 
阅读更多

很久以前,Apple和IBM公司成立了一个公司Taligent,开发一些类似Cocoa的工具和库,在Taligent已经比较成熟的时候,在一个商业交流会议上,我遇到了该公司的一个工程师.我请他开发一个简单的程序:<wbr>在一个window上创建一个button. 当点击这个button, 在textfield上面显示 "hello world".这个工程师创建了一个工程,并且开始疯狂的开始了类继承:从window和button以及event handler等基本类继承自己的子类. 然后写了n多的代码让button和textfield显示在window上. 45分钟后,我必须得离开了,可以他仍然没有完成. 在那时候我就断定,这个公司没有前途.果然,若干年后,他们关门大吉了.</wbr>

很多的C++和Java工具库和Taligent工具库思想差不多.开发者需要从标准类中继承,编写很多的代码来控制window上显示的控件. 很多类似的工具库确实在使用.

如果使用AppKit framework. 我们很少去继承类去处理windows ,buttons或是events. 相反,我们会使用现有的类. 同样你也不需要编写代码去控制window中的控件.nib文件能包含所有的信息. 整个程序只会包含少数几行重要的代码.开始,你会决定很惊讶.但是,随着时间,你会发现它的优雅和美妙.

<wbr></wbr>

通过学习NSControl可以很好的理解Appkit framework. NSButton,NSSlider,NSTextView和NSColorWell都是NSControl的子类. 每个control都包含target和action. target是一个指向其它对象的指针.action是会发给target的message(selector).<wbr>回忆一下我们在第二章给两个按钮设置的target和action: 我们把foo对象设置成两个按钮的target.一个按钮的action设置成 seed;另一个设置成generate. 如图5.1</wbr>

当用户和控件交互是. action 消息就会发送给它们的target. 例如当点击一个button,它的action消息将发送给它的target. 如图5.2

action方法接受一个参数: sender.该参数可以让接收者(target)知道是哪一个控件发送的这个action消息. 通常,我们会访问sender来获得更多的信息.比如,一个check box 在勾选的时候发送action消息. 而接受者接受到消息后会访问checkbox,得到它是否是选中的.

- (IBAction)toggleFoo:(id)sender

{

<wbr><wbr><wbr> BOOL isOn =[sender state];</wbr></wbr></wbr>

<wbr><wbr><wbr> ...</wbr></wbr></wbr>

}

要更好得理解NSControl, 我们要进一步了解它的父类继承关系:NSControl继承NSView. 而NSView又继承NSResponder. NSResponder的父类就是NSObject.这个继承关系中的每个节点类都增加了一些实现去做某些功能.如图5.3


在继承关系根部是NSObject. 所有的类都从它继承,同时继承了NSObject实现了一些基本方法:retain, release, dealloc,和init.NSResponder是NSObject的子类. 它实现了一些事件处理的方法,象mouseDown: keyDown:等等.NSView是NSResponder的子类. NSView描述了window上的一块区域,来显示自己.我们可以创建NSView的子类来画图,或是让用户拖拽数据.而NSContro继承NSView,并增加了target和action.

<wbr></wbr>

-- 一些常用的NSControl子类

在使用控件前,我们简要的来学习下3个常用控件类:NSButton, NSSlider,NSTextFeild.

<wbr></wbr>

- NSButton

NSButton实例对象可以有几个不同的外表: 椭圆形,方形,复选框.在点击它们时,它们有不同的行为. 还可以给按钮设置图标和声音. 在Interface Builder中选择NSButton的Attributes Inspector 如图5.4


我们可能经常给按钮发送3个消息:

<wbr></wbr>

- (void)setEnabled:(BOOL)yn

用户可以点击enabled的按钮, disabled的按钮会是灰色的.

<wbr></wbr>

- (int)state

一般对于复选框而言,如果按钮是勾选的, 返回NSOnState(1) ,没有勾选则为NSOffState (0).

<wbr></wbr>

- (void)setState:(int)aState

该方法可以使复选框勾选或不勾选.

<wbr></wbr>

--NSSlider

NSSlider实例-slider可以是横向或是纵向.它可以设置成当拖动时连续不断的发送消息,或是只有当拖动结束(用户mouse up)才发送消息.slider还可以设置标尺,把拖动的改变值限制在一个刻度 图 5.5.同时我们还可以创建圆形的slider


两个常用方法

<wbr></wbr>

- (void)setFloatValue:(float)x

移动slide到x位置

<wbr></wbr>

- (float)floatValue

得到当前值(位置)

<wbr></wbr>

-- NSTextField

NSTextField实例对象文本框,能让用户输入单行文本.文本框可以是可编辑(容许输入),也可以是不可编辑.通常不可编辑的文本框就是窗体上的文本标签.相对于button和slider.文本框相对复杂一些,以后我们还会讨论到其中奥秘.图5.6是在Interface Builder里面 NSTextField的属性


我们看到如果当文本框为空的时候,会自动包含了灰色的站位文本

<wbr></wbr>

NSSecureTextField是NSTextField的子类,处理类似密码文本.用户的输入会由*号代替.我们也不能拷贝,剪切其中的文本.

<wbr></wbr>

NSTextField常用方法:

- (NSString *)stringValue

- (void)setStringValue:(NSString*)aString

得到和设置文本框中的文本

<wbr></wbr>

- (NSObject *)objectValue

- (void)setObjectValue:(NSObject *)obj

这些方法得到和设置文本框中任意对象类型数据

当你需要使用formatter时,这会很有帮助.NSFormatter负责把字符串转换为另外的类型.如果没有相关的NSFormatter指定,这些方法会使用对象obj的descripte方法返回的字符串.

举个例子: 我们需要一个文本框来让用户输入日期.我们不希望用户之间输入文本.而是一个NSCalendarDate对象.通过绑定一个NSDateFormatter,可以保证文本框的objectVaule方法返回一个NSCalendarDate对象,而setObjectValue:可以接受一个NSCalendarDate对象,NSDateFormatter会把NSCalendarDate对象转换成我们相应的文本(在23章,我们会创建自己的formatter)

图5.7显示了其他我们可能用到的控件. 试着吧它们拖放到你的window上,看看它们有什么属性.编译运行程序看看它们是怎么响应的吧.


开始SpeakLine例子

<wbr></wbr>

我们来创建一个简单的例子来试着使用控件. 这个例子容许用户在文本框中输入一行文本,然后使用MacOSX的 speech synthesizer来朗读这行文本. 当我们完成它后,样子如图5.8


图5.9是我们将要创建的对象,以及它们的关系图.其中所以一NS为前缀的是cocoaframework中已经有的类.我们创建了AppController类

使用XCode,创建一个Cocoa Applictiaon. 并命名为SpeakLine.

<wbr></wbr>

布局界面 (nib file)

双击MainMenu.nib,打开Interface Builder. 从LibraryWindow中拖处一个文本框和两个按钮.双击文本框,修改文本为"Peter Piper picked a peck ofpickled peppers",(或是你希望朗读的文字.) 把按钮的标题改为 Speak 和 Stop. 如图5.8

<wbr></wbr>

回到XCode, 我们创建一个类: AppController.AppController将是两个按钮的target.每个按钮都会触发一个不同的action方法.编写AppController.h

#import<Cocoa/Cocoa.h>

<wbr></wbr>

@interface AppController : NSObject

{

<wbr><wbr><wbr> IBOutletNSTextField *textField;</wbr></wbr></wbr>

}

- (IBAction)sayIt:(id)sender;

- (IBAction)stopIt:(id)sender;

@end

<wbr></wbr>

在nib文件中创建一个AppController对象: 拖一个蓝色的NSObject正反体到docwindow. 在Identity Inspector中,把他的class设置成AppController. 如图5.10


-- 使用Interface Builder连接

对象连接就像我们做人员介绍: "小明,这是小强".如果你认为小强也有必要知道小明.你会说"小强,这位就是小明." 在InterfaceBuilder中,我们从个某对象拖动到它想知道的那个对象,从而建立连接.

<wbr></wbr>

比如.当用户点击Stop按钮, 按钮发送一个消息给AppController.那么,按钮对象就要"知道"AppController对象.这里,我们从按钮对象Control-Drap到AppController对象.这时会弹出一个面板,我们可以利用它来知道action为stopIt:如图5.11


同样的,我们Conrtrol-drap Speak按钮,设置action为sayIt:

<wbr></wbr>

为了能够朗读文本框中的文字, AppController对象需要得到文本框中的文本.因此,AppController对象有一个指向文本框的指针. Control-Click(按住Control点击鼠标).当outlets列表出现后,从textField拖动到文本框上.如图5.12


到现在,我们设置了几乎所有对象关系图 5.9中的对象连接. 除了speechSynth.它将通过代码而不是Interface Builder来连接.

<wbr></wbr>

-NSWindow的 initialFirstResponder outlet

当我们的程序运行,窗口出现后, 用户如果没有点击文本框,他没有办法输入文本.我们可以设置:当窗口弹出,哪一个view可以接受用户的键盘输入. Control-clickwindow图标.在弹出面板上拖拽initialFirstResponder到文本框

<wbr></wbr>

-- 实现AppController 类

<wbr></wbr>

现在我们来编写一些代码.回到XCode.打开AppController.h文件.给AppController添加一个NSSpeechSynthesizer类型的成员变量:speechSynth

#import<Cocoa/Cocoa.h>

<wbr></wbr>

@interface AppController : NSObject

{

<wbr><wbr><wbr> IBOutletNSTextField *textField;</wbr></wbr></wbr>

<wbr><wbr><wbr><strong>NSSpeechSynthesizer *speechSynth;</strong></wbr></wbr></wbr>

}

- (IBAction)sayIt:(id)sender;

- (IBAction)stopIt:(id)sender;

<wbr></wbr>

@end

<wbr></wbr>

打开AppController.m文件.在这里我们要让程序动起来

#import "AppController.h"

<wbr></wbr>

@implementation AppController

<wbr></wbr>

- (id)init

{

<wbr><wbr><wbr> [superinit];</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Logs canhelp the beginner understand what</wbr></wbr></wbr>

<wbr><wbr><wbr> // ishappening and hunt down bugs.</wbr></wbr></wbr>

<wbr><wbr><wbr>NSLog(@"init");</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Create anew instance of NSSpeechSynthesizer</wbr></wbr></wbr>

<wbr><wbr><wbr> // with thedefault voice.</wbr></wbr></wbr>

<wbr><wbr><wbr> speechSynth= [[NSSpeechSynthesizer alloc] initWithVoice:nil];</wbr></wbr></wbr>

<wbr><wbr><wbr> returnself;</wbr></wbr></wbr>

}

<wbr></wbr>

- (IBAction)sayIt:(id)sender

{

<wbr><wbr><wbr> NSString*string = [textField stringValue];</wbr></wbr></wbr>

<wbr></wbr>

<wbr><wbr><wbr> // Is thestring zero-length?</wbr></wbr></wbr>

<wbr><wbr><wbr> if ([stringlength] == 0) {</wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr>NSLog(@"string from %@ is of zero-length", textField);</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr><wbr><wbr><wbr><wbr>return;</wbr></wbr></wbr></wbr></wbr></wbr></wbr>

<wbr><wbr><wbr> }</wbr></wbr></wbr>

<wbr><wbr><wbr> [speechSynthstartSpeakingString:string];</wbr></wbr></wbr>

<wbr><wbr><wbr> NSLog(@"Havestarted to say: %@", string);</wbr></wbr></wbr>

}

<wbr></wbr>

- (IBAction)stopIt:(id)sender

{

<wbr><wbr><wbr>NSLog(@"stopping");</wbr></wbr></wbr>

<wbr><wbr><wbr> [speechSynthstopSpeaking];</wbr></wbr></wbr>

}

@end

<wbr></wbr>

好了,编译运行我们的程序.现在我们可以开始朗读文本,并随时停止朗读

<wbr></wbr>

注意: 一个菜单项(NSMenuItem对象)同样有target和action.我们在这章所讨论的东西一样适合于菜单项.

<wbr></wbr>

思考: 通过代码来设置target

<wbr></wbr>

我们注意到,控件的action是一个selector. NSControl有一个方法:

- (void)setAction:(SEL)aSelector

<wbr></wbr>

那,我们怎么获取一个selector呢? 我们可以使用Objective-C编译指令@selector来查找selector[在那里查找呢.在self中,也就是当前类里面].比如,要设置一个按钮的action为drawMickey:.我们可以这样做

SEL mySelector;

mySelector = @selector(drawMickey:);

[myButton setAction:mySelector];

在编译的时候, @selector(drawMickey:)将会被selectordrawMickey: 来代替.

<wbr></wbr>

如果你要在运行时查找selector.可以使用NSSelectorFromString()

SEL mySelector;

mySelector =NSSelectorFromString(@"drawMickey:");

[myButtonsetTarget:someObjectWithADrawMicke<wbr>yMethod];</wbr>

[myButton setAction:mySelector];

<wbr></wbr>

挑战

<wbr></wbr>

这个练习一定对你意义非凡. 虽然前面在我的指示下能训练完成朗读的例子.这个练习毕竟是你自己完成的.多参考一些前面的例子.你一定行

<wbr></wbr>

创建一个只有一个窗口的程序(不是document-base).图5.13显示的是程序启动后,你没有任何输入时的样子.图5.14显示了,用户作了一些输入后的样子.



当用户输入一些文本,点击按钮. 下面的文本标签将显示输入的文本,并计算出文本的字符数.

<wbr></wbr>

怎么使用Cocoa现有的类去实现这些功能对你很重要.在这个练习中你会认识NSTextField类的两个方法

- (NSString *)stringValue;

- (void)setStringValue:(NSString*)aString;

<wbr></wbr>

同时你也会发现NSString的两个方法很有帮助

- (int)length;

+ (NSString *)stringWithFormat:(NSString*),...;

<wbr></wbr>

你还会创建一个controller对象, 并包含2个outlet和一个action. (恩,虽然有点难,但你一定能完成它. 加油!!)

<wbr></wbr>

<wbr></wbr>

--调试建议

<wbr></wbr>

现在你不再是简单的从书中拷贝代码,而是动手自己编写一些代码了.我想是时候给你一些代码调试的建议了

时刻注意console: 一旦Cocoa对象抛出异常,它会在console中记录相关信息.如果你没注意console.你就不能发现这些错误.

开发过程中使用debug 编译设定: release编译设定移除了一个debuggingsymbol. 因此调试器会没有办法正常调试

<wbr></wbr>

以下是一些常见问题及其解决方法:

<wbr></wbr>

1、没有任何响应: 你可能忘记了在Interface Builder做对象连接.因此,指针为nil.还记得给nil发消息什么都不会做

2、做了连接,还是没有响应: 方法名字是否有拼错. Objective-C是大小写敏感的.所以setFoo: 和 setfoo:是不同的方法. 设置一个断点,看看方法得到调用了么?

3、程序崩溃: 给一个已经释放调的对象发送消息,将会导致你的程序崩溃.(如果你使用了garbagecollector,问题将很难解决). 解决这类问题可能比较难-毕竟,产生问题的对象已经释放掉了.一个方法是设置对象僵化来替代释放.当我们给一个僵化的对象发送消息是,debugger会抛出异常显示一些描述, 比如"You triedto send the message -count to a freed instance of class Fido".并且debugger会停止在发送消息那行.

<wbr></wbr>

在XCode中双击executable . 在Info面板上添加两个环境变量:NSZombiesEnabled为YES. CFZombieLevel 为16. 如图5.15


4、对象没有释放,照样崩溃: 检查你的参数类型. 例如下面就会崩溃

int x = 5;

NSLog(@"x is %@", x);

<wbr></wbr>

5、没法通过Interface Builder做对象连接: 是不是.h 搞错了,是不是忘掉分号: .还是变量定义出错, 比如NSTabView写成NSTableView. 仔细找找看吧.






分享到:
评论

相关推荐

    target-action设计模式

    博客内容: http://blog.csdn.net/jerryleefighting/article/details/30213697 两种方式实现该设计模式

    target——action模式

    target——action模式

    TargetActionDemo

    这是一个学习IOS设计模式中得target-action模式demo,需要的拿去吧

    ios实现关灯游戏

    关灯游戏,实现了Target-Action模式,欢迎下载

    CTMediator:无需注册程序即可将您的iOS项目拆分为多个项目的中介程序

    pod "CTMediator"CTMediator可帮助您将项目划分为多个项目,并使用Target-Action模式使子项目相互通信。 没有注册程序! 您可以查看演示以获取更多详细信息:在运行演示之前添加私人仓库: pod repo add PrivatePods...

    ios关灯游戏

    利用target-action设计模式写的demo

    github-action-demo:由code.quarkus.io生成

    github-action-demo项目 该项目使用Quarkus(超音速亚原子Java框架)。 如果您想了解更多有关Quarkus的信息,请访问其网站: ://quarkus.io/。 在开发人员模式下运行应用程序 您可以在开发模式下运行应用程序,该...

    Swifter-Swift 开发者必备 Tips (第四版).zip

    var actions = [ControlEvent: TargetAction]() func setTarget(target: T, action: @escaping (T) -&gt; () -&gt; (), controlEvent: ControlEvent) { actions[controlEvent] = TargetActionWrapper( target: ...

    新版Android开发教程.rar

    � 良好的盈利模式( 3/7 开),产业链条的各方:运营商、制造商、独立软件生产商都可以获得不错的利 益 。 将移动终端的评价标准从硬件向软件转变,极大的激发了软件开发者的热情。 � Android 的源代码遵循 Apache...

    Using Swift with Cocoa and Objective-C完整中文CocoaChina精校版

    TargetTarget TargetTarget -Action Action Action模式 29 类型匹配与统一规范 类型匹配与统一规范 类型匹配与统一规范 类型匹配与统一规范 . 29 Using Swift with Cocoa and Objective-C 完整中文版(CocoaChina ...

    移动App架构设计

    C对M:APIC对V:OutletV对C:Target-action,Delegate,DatasourceM对C:Notification,KVOMVVM是在MVC的基础上多了一个ViewModel:表示逻辑,将model的数据转换为view可以呈现的东西.适合大量展示类的...

    action-demp:由code.quarkus.io生成

    您可以在开发模式下运行您的应用程序,该模式可使用以下方式启用实时编码: ./mvnw compile quarkus:dev 注意: Quarkus现在带有一个Dev UI,仅在开发人员模式下可用。 打包并运行应用程序 可以使用以下程序打包该...

    android adb shell 命令大全

    adb shell am start -n 包名/包名+类名(-n 类名,-a action,-d date,-m MIME-TYPE,-c category,-e 扩展数据,等)。 23、发布端口: 你可以设置任意的端口号,做为主机向模拟器或设备的请求端口。如: adb ...

    mobile_shop_based_in_vue

    1.滑动的时候报警告:Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080 解决方法,可以加上* { ...

    ReactNative中使用Redux架构总结

    RN使用了Virtual DOM,不需要Target绑定-&gt;Action修改UI属性,只要当状态变化,render新状态下的组件,数据单向传递,而MVC的设计模式存在双向数据流。 RN不易进行测试,Redux提供了非常方便的mock测试方式。 Redux...

    客户关系管理系统框架搭建(二)

    public String save() throws IllegalAccessException, InvocationTargetException{ System.out.println("xxxxxxxxxxxxxxxxxxxxxxxx "); return null; } } * 建立请求路径和...

    cms后台管理

    &lt;li&gt;&lt;a href="${a.url}" target="_blank"&gt;${a.title}&lt;/a&gt;&lt;/li&gt; [/#list] 就是简单的将tag_list中的内容,即“paramWrap.put(OUT_LIST, DEFAULT_WRAPPER.wrap(list));”中放入的数据遍历出来 style_2-1.html中的...

    adb1.0.26包含fastboot.exe

    INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE 已安装 target SDK 支持运行时权限的同名应用,要安装的版本不支持运行时权限 INSTALL_PARSE_FAILED_NOT_APK 指定路径不是文件,或不是以 .apk 结尾 INSTALL_PARSE_...

    视频分享系统源码 VideoSharingSystem.rar

    &lt;form method="get" action="@SiteUrl@/search.asp" target="_blank" name="search" onSubmit="return so()"&gt; 影片或主演" &gt; 提交" /&gt; 相关调用 评论:评论显示区 评分:评分显示区&lt;/div&gt; 引用 签到:;" ...

Global site tag (gtag.js) - Google Analytics