很久以前,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. 仔细找找看吧.
相关推荐
博客内容: http://blog.csdn.net/jerryleefighting/article/details/30213697 两种方式实现该设计模式
target——action模式
这是一个学习IOS设计模式中得target-action模式demo,需要的拿去吧
关灯游戏,实现了Target-Action模式,欢迎下载
pod "CTMediator"CTMediator可帮助您将项目划分为多个项目,并使用Target-Action模式使子项目相互通信。 没有注册程序! 您可以查看演示以获取更多详细信息:在运行演示之前添加私人仓库: pod repo add PrivatePods...
利用target-action设计模式写的demo
github-action-demo项目 该项目使用Quarkus(超音速亚原子Java框架)。 如果您想了解更多有关Quarkus的信息,请访问其网站: ://quarkus.io/。 在开发人员模式下运行应用程序 您可以在开发模式下运行应用程序,该...
var actions = [ControlEvent: TargetAction]() func setTarget(target: T, action: @escaping (T) -> () -> (), controlEvent: ControlEvent) { actions[controlEvent] = TargetActionWrapper( target: ...
� 良好的盈利模式( 3/7 开),产业链条的各方:运营商、制造商、独立软件生产商都可以获得不错的利 益 。 将移动终端的评价标准从硬件向软件转变,极大的激发了软件开发者的热情。 � Android 的源代码遵循 Apache...
TargetTarget TargetTarget -Action Action Action模式 29 类型匹配与统一规范 类型匹配与统一规范 类型匹配与统一规范 类型匹配与统一规范 . 29 Using Swift with Cocoa and Objective-C 完整中文版(CocoaChina ...
C对M:APIC对V:OutletV对C:Target-action,Delegate,DatasourceM对C:Notification,KVOMVVM是在MVC的基础上多了一个ViewModel:表示逻辑,将model的数据转换为view可以呈现的东西.适合大量展示类的...
您可以在开发模式下运行您的应用程序,该模式可使用以下方式启用实时编码: ./mvnw compile quarkus:dev 注意: Quarkus现在带有一个Dev UI,仅在开发人员模式下可用。 打包并运行应用程序 可以使用以下程序打包该...
adb shell am start -n 包名/包名+类名(-n 类名,-a action,-d date,-m MIME-TYPE,-c category,-e 扩展数据,等)。 23、发布端口: 你可以设置任意的端口号,做为主机向模拟器或设备的请求端口。如: adb ...
1.滑动的时候报警告:Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/5093566007214080 解决方法,可以加上* { ...
RN使用了Virtual DOM,不需要Target绑定->Action修改UI属性,只要当状态变化,render新状态下的组件,数据单向传递,而MVC的设计模式存在双向数据流。 RN不易进行测试,Redux提供了非常方便的mock测试方式。 Redux...
public String save() throws IllegalAccessException, InvocationTargetException{ System.out.println("xxxxxxxxxxxxxxxxxxxxxxxx "); return null; } } * 建立请求路径和...
<li><a href="${a.url}" target="_blank">${a.title}</a></li> [/#list] 就是简单的将tag_list中的内容,即“paramWrap.put(OUT_LIST, DEFAULT_WRAPPER.wrap(list));”中放入的数据遍历出来 style_2-1.html中的...
INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE 已安装 target SDK 支持运行时权限的同名应用,要安装的版本不支持运行时权限 INSTALL_PARSE_FAILED_NOT_APK 指定路径不是文件,或不是以 .apk 结尾 INSTALL_PARSE_...
<form method="get" action="@SiteUrl@/search.asp" target="_blank" name="search" onSubmit="return so()"> 影片或主演" > 提交" /> 相关调用 评论:评论显示区 评分:评分显示区</div> 引用 签到:;" ...