MonoTouch:用.net开发iPhone应用
2011年02月22日
作者 [b]Bryan Costanich[/b]
引言
直到最近,要为苹果的iPhone开发应用程序的唯一选择就是一头扎进苹果的开发系统中。这意味着,你必须?愿意?在XCode IDE中编写Objective-C代码。对于很多开发人员,学习Objective-C被看作是一个巨大的障碍。特别对于哪些从来不用担心内存管理、指 针和C语言要负责处理的东西的开发人员来说,更是如此。
随着MonoTouch框架(Novell的Mono Project的一部分)的出现,这一切都将改变。Mono Project是微软.NET平台的开源实现。其允许你在几乎任何平台上运行.NET应用程序,包括Apple、FreeBSD、Linux、Unix等 等。MonoTouch是Mono Project的新组成部分,让你能够用C#和.NET平台编写可以运行在iPhone上的应用程序。
本篇文章的目的,就是提供MonoTouch平台的一个完整介绍,让大家知道到那里获取必要的工具,MonoTouch有什么样的限制,以及如何构建一个简单的应用程序。
背景和限制对于打算为iPhone开发应用的.NET开发人员而言,MonoTouch的出现无疑是一件好事。然而,在决定创建应用程序之前,有一些限制和背景知识需要先了解清楚。
它如何工作?在创建MonoTouch应用程序的时候,大部分非UI方面的.NET 3.5功能依旧可用,或者一些还处于计划之中(如.NET 4.0的功能)也囊括其中。这让你可以使用很多业已熟悉的.NET Framework技术来编写应用程序,包括Windows Communication Framework (WCF)、Workflow Foundation (WF)等等。也包括几乎所有的基类库(Base Class Library,BCL),涵盖诸如垃圾收集(Garbage Collection)、线程、数学函数、System.Net、加密等。对于可用的标准.NET程序集列表,见http://monotouch.net/Documentation/Assemblies。MonoTouch是一种基础.NET函数库的特别定制版本,这也类似Silverlight和Moonlight的实现方式。
这意味着,你能够使用MonoTouch的核心程序集来编译标准的.NET 3.5代码成相应的函数库,并在你的应用程序中使用它们。因此,如果你有一个用于其它应用程序的特别函数库,其包含着一些用于工程问题的高级数学函数,那 么只需简单地把这些代码库加入到你的MonoTouch解决方案中,并引用它。在你构建解决方案的时候,编译器就利用MonoTouch核心函数库对其进行编译,接着就能在iPhone应用程序中使用它了。
MonoTouch也包括一些原生iPhone API的包装函数库,如访问位置(Location,GPS)、加速计、地址簿等的函数。MonoTouch也提供相应的功能,让你能够调用那些尚未进行包装的原生Objective-C函数库,所以你可以直接和现存的Objective-C代码进行互操作。
我如何创建用户界面(UI),我能使用Silverlight吗?MonoTouch应用程序的UI需要使用苹果的Interface Builder(界面创建器,IB)应用程序来创建,IB连同iPhone SDK一起提供。Interface Builder使用Cocoa Touch(苹果用于iPhone的UI框架)控件对象,这些控件对象在iPhone上原生提供的。这意味着,你能在应用程序中使用所有的标准 iPhone控件,如选择器(Pickers)、滑动条(Sliders)、按钮等等。
你也能通过代码来创建界面,即实例化Cocoa Touch对象后,把它们添加到应用程序的视图(Views)中(关于视图,后面会详细讲述)。
然而,你不能利用传统的.NET技术,如Silverlight、WPF、WinForms等来创建MonoTouch界面。
Cocoa Touch使用一种融合了MVC(Model View Controller)模式思想的结构,我们将在后面一篇文章中介绍。
我如何分发我的应用?MonoTouch应用程序的分发完全和传统iPhone应用程序的分发一样,既可以通过苹果App Store,也可以通过企业部署。
App Store是一个在线资源库,让用户可以付费购买(如果不是免费的话)和下载应用程序。可以从iTunes中访问,或直接通过iPhone本身来访问。为 了得到通过App Store分发应用的许可,你必须向苹果注册,并支付每年99美元的年费。
企业部署方式就是为公司开发内部应用程序,并分发给员工等人员使用,无需把应用在App Store中列出。
什么是许可模型?不像Mono那样,MonoTouch不是开源的,且是一个收费产品。这意味着,如果你打算开发一些实际的应用,就必须购买软件许可。
[b]专业版($399)[/b]??单个的个人开发人员许可,让你可以开发应用程序,并通过苹果App Store来分发它们。 [b]企业版($999)[/b]??单个的企业开发人员许可,让你可以开发应用程序,并通过苹果App Store来分发它们,或者进行企业部署。 [b]企业版,5人($3999)[/b]??和企业版一样,只是提供了5个坐席的授权。 以上所有选项都包括了一年的免费升级权益。
还有一个评估版本,你只能把应用部署到模拟器中。出于介绍的目的,我们只需要评估版本就行。
MonoTouch有哪些限制?没有即时(JIT)编译根据苹果的iPhone政策,任何应用程序都不能包含需要JIT编译的代码。但是稍等,.NET确实能正确工作,是不?对,不过MonoTouch是通过把应用程序编译为原生的iPhone程序集来跳过这个限制的。但是,这也带来了几个限制。
[b]泛型[/b]??泛型是由JIT编译器在运行时进行实例化的,然而,Mono具备一种提前(Ahead of Time ,AOT)编译的模式,可以为类似List这样的泛型集合生成方法和属性。而泛型的其他用法,例如泛型虚方法、泛型类型上的 P/Invokes和Dictionary上的值类型,就不被支持(虽然存在Dictionary的代替方法)。 [b]动态代码生成[/b]??因为动态代码生成依赖于JIT编译器,所以对任何动态语言编译的过程也不能支持。包括System.Reflection.Emit、 Remoting和动态语言运行时(DLR)。 C#是唯一的语言另外,目前用于编写MonoTouch应用程序的唯一可用语言是C#。Visual Basic.NET有望在MonoTouch未来的发布中支持,不过此时此刻我们别无选择。
更多信息相关限制的完整列表和更多的信息,包括一些代替方法,可用参见http://monotouch.net/Documentation/Limitations。
入门要进入为iPhone创建MonoTouch应用程序的大门,我们需要下面几样东西:
一个使用Intel CPU的Mac电脑,其要安装MacOSX 10.5或10.6 (Leopard或Snow Leopard) 苹果的iPhone SDK 3.0或更高版本 Mono的当前版本 MonoTouch SDK 一个IDE工具,如MonoDevelop或XCode,或一个文本编辑器程序 安装着Leopard或Snow Leopard的Mac这是最重要也是最容易忽视的需求。尽管,理论上你能在任何平台上开发大部分应用程序,然而iPhone Simulator和Interface Builder只能在Leopard和Snow Leopard上运行。另外,编译器本身用到了一些特定于Intel Mac机器的底层功能,所以购买这样一台电脑是绝对必须的。
苹果的iPhone SDKiPhone SDK可通过http://developer.apple.com/iphone/来免费下载,不过必须在苹果网站上注册,才能访问这个地址。
在安装了iPhone SDK后,要确保你能正常启动iPhone Simulator。要启动它,只需打开Spotlight,键入iPhone Simulator。
Mono一旦你测试iPhone Simulator正常,那么就要安装Mono for OSX的最新版。Mono可以从http://mono-project.com/Downloads下载。记住要点击?Intel?版本的链接,不要点CSDK版本。同样,安装MonoTouch SDK之前也需要安装Mono的。Mono的安装包是磁盘镜像的形式,挂接镜像,双击安装包,根据安装向导完成安装过程。
MonoTouch SDK接下来,下载和安装最新的MonoTouch SDK。你既可以在MonoTouch商店(http://monotouch.net/Store)购买,购买后会收到一个下载链接,也可以从http://monotouch.net/DownloadTrial下载评估版。如果购买了MonoTouch,你就能把应用程序部署到一台正确配置了的iPhone上,不过也可像我这样,仅仅在模拟器中运行。所以,目前 而言试用/评估版就足够了。
文本编辑器或集成开发环境(IDE)如果你打算创建MonoTouch应用程序,所需的所有东西就是前面提及的,和一个文本编辑器。你能创建所有代码文件,并用命令行(终端窗口)来手动编 译。这种方式虽然可行,但是实际操作起来可能会非常痛苦,所以我们还是需要使用一个IDE来开发我们的应用程序。
你可以编辑/hack一下XCode(随iPhone SDK一起安装)来利用MonoTouch的函数库和编译器,也可以使用MonoTouch版的MonoDevelop,其已经为MonoTouch应用 程序做好所有配置了。我们理所当然要用MonoDevelop,所以访问这里http://monodevelop.com/Download/Mac_MonoTouch来 下载它。要安装MonoDevelop,只用把下载文件拖到应用程序目录中就行。
如果你已经正确安装Mono,那么MonoDevelop应该可以正常启动。
Hello World应用程序现在,一切已经准备妥当,让我们开始来尝试开发点东西了。
MonoDevelop首先,启动MonoDevelop。你应该会看到和下图类似的界面【译者注:如果OSX的首选语言是中文的话,MonoDevelop的菜单和工具栏的文字显示不正常,所以最好把English拖到语言选项的第一位】:
作为一个标准的IDE,看上去还是蛮熟悉的。它非常类似Visual Studio、SharpDevelop、Visual C# Express等等。
我们创建一个新解决方案,来包含iPhone项目。这里的解决方案和Visual Studio中的概念一样,实际上你可以在MonoDevelop中打开Visual Studio创建的解决方案。在MonoDevelop中的一个不同点就是,你能够在一个MonoDevelop实例中打开多个解决方案,正如下面的截图所示:
这完全是由于在OSX中,你不能启动MonoDevelop的多个实例(事实上,任何程序都不行),且没有任何变通方法。所以,如果你需要在解决方案间切 换(例如,你希望另外打开一个包含示例代码的解决方案),你就能简单地一次性打开多个。
那么,说了上面这么多,让我们现在来创建一个解决方案吧。在菜单中,点File:New:Solution:
我们要创建一个如下面截图所示的?iPhone MonoTouch Project?。选中它,并命名为Example_HelloWorld_1。
这里再次和Visual Studio中创建新解决方案的对话框很类似。点击Forward ,显示下一屏,直接点击OK,因为我们不需要这些功能:
你现在应该可以看到如下所示的解决方案视图了(注意,我展开了解决方案中的节点,以便显示出所有文件和引用程序集):
我们来过一遍这些东西:
[b]References[/b] ??这个文件夹包含MonoTouch应用程序需要的基本引用。MonoTouch程序集包括特定于iPhone的任何东西,也包括了所有Cocoa Touch控件的包装器,以及类似位置、数据等核心iPhone接口。以System.开头的程序集是.NET的基类库和运行时,其被裁减过以便能运行在 iPhone上。 [b]Main.cs[/b] ??这和控制台应用程序、WPF应用程序等是一致的。在这里调用的是static void main() ,其是作为应用程序的入口点。过一下我们会仔细研究一下这个文件。 [b]MainWindow.xib和MainWindow.xib.designer.cs[/b] ??这个和Winforms Window或WPF Window类似。xib文件实际上要在Interface Builder中进行编辑,而designer.cs文件包含这个窗体的属性。 让我们来仔细研究一下Main.cs文件中的代码:
using System;using System.Collections.Generic;using System.Linq;using MonoTouch.Foundation;using MonoTouch.UIKit;namespace Example_HelloWorld_1{ public class Application { static void Main (string[] args) { UIApplication.Main (args); } } // The name AppDelegate is referenced in the MainWindow.xib file. public partial class AppDelegate : UIApplicationDelegate { // This method is invoked when the application has loaded its UI and its ready to run public override bool FinishedLaunching (UIApplication app, NSDictionary options) { // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); return true; } // This method is required in iPhoneOS 3.0 public override void OnActivated (UIApplication application) { } }}
其中有两个有趣的地方。它包含一个Application类和一个AppDelegate类。从这里开始,和传统的.NET GUI开发就有所不同了。
iPhone应用程序的工作方式是,应用程序类(继承于UIApplication)包含了所有窗口、视图、控件和资源等等;同时应用程序委托类中(继承于UIApplicationDelegate)处理来自iPhone OS的回调,实际上会包括应用程序运行周期(Lifecycle)的事件(例如应用程序启动和应用程序终止)和大量的运行时事件(例如低内存报警)。
通过在应用程序委托类处理这些事件,你就能够响应它们。比如,在应用程序关闭的时候,WillTerminate()方法将被调用,这样就有机会去保存任何用户数据、应用程序状态等。
在Application类中,具有一个Main() 方法。调用UIApplication.Main,Objective-C运行时将查找MainWindow.xib文件(它包含了 UIApplicationDelegate类的名称),实例化Application类(为单实例)后,就接着调用AppDelegate类中运行周期事件。
[b]你不必把主窗体的名称命名为?MainWindow.xib? (即所谓的主界面文件)。你可以任意命名它,只需告知编译系统去查找哪个文件就行。如果你希望它去查找不同的文件,在项目文件上右键打开项目选项,点击Options ,接着在Build : iPhone Application : Main Interface File中即可设置主窗体对应的文件。在应用程序启动的时候,Objective-C运行时将会尝试去加载那个文件,并根据这个文件内的设置来查找应用程序委托类。
另外,你也可以任意命名你的应用程序委托类。默认情况下,它的名称为?AppDelegate?。要改变它,在Interface Builder中打开主界面文件,修改Application Delegate的名称。
[/b]一会我们会回到Main.cs文件上来,不过首先来深入研究下应用程序的实际GUI。
Interface Builder目前为止,已经读到了我们的iPhone应用程序的部分代码,可以深入研究一下如何构建它的界面了。苹果的应用程序设计工具包称之为Interface Builder。Interface Builder可以和开发环境松耦合。它可以编辑那些定义应用程序GUI的.xib文件。NIB和XIB的比较:Nib文件包含了窗口、控件等的XML表示,类似于WPF/Silverlight中的XAML模型。
不管是在XCode中编写Objective-C,或在MonoDevelop中编写C#,Interface Builder的用法都是完全一样的。能这样,完全是因为MonoDevelop能够监测Nib文件的变更,并添加/删除对应于Nib文件的适当代码到 designer.cs文件中。
你当然可以不打开Interface Builder,纯粹通过编程来创建整个GUI,有些开发人员确实也是这样做的。很多事情在Interface Builder也无法完成,就此而言,你还是需要编程来完成某些事情。Interface Builder隐藏了一些复杂的部分,在入门的时候,很容易使用Interface Builder来熟悉iPhone应用程序GUI的一些概念。
那么,说了这么多,让我们来着手创建界面了。在MainWindow.xib文件上双击。Interface Builder将启动,并能看到如下图所示的东西:
让我们逐一研究一下这些窗口。从左到右,分别是:Document Window(文档窗口)、Design Surface Window(设计界面窗口)、Library Window(控件库窗口)和Inspector Window(检查器窗口)。
首先让我们来看Document Window:
这个窗口显示了在.xib文件中的所有对象。这是默认的视图,你会发现它虽然样子漂亮,但没有太大用处,因为你的对象实际上是层级排列的,而图标视图只能同时显示一级。在我们添加控件到界面上的时候,它不会显示在这个视图中。所以,我们需要通过点击View Mode工具条上中间的图标来改变列表视图。改变后,应该会显示如下的样子:
下一个窗口是设计界面。也就是,我们实际拖拽Cocoa Touch控件来设计我们界面的地方:
当然,由于我们尚未添加任何控件到上面,所以现在还是一片空白。
下一个窗口是Library。Library包含了所有能在设计界面上使用的Cocoa Touch控件。如果你运行Leopard【译者注:原文这里为Snow Leopard,实际是错误的。】,那么你的控件库窗口如下所示:
如果你运行Snow Leopard【译者注:原文这里未Leopard,实际是错误的。】,它应该是这个样子:
注意它们两者还是非常类似的。在Snow Leopard里,有一个名为?Classes?的标签页,我们后面会讲到这个东西,不过这也是唯一的不同点。
这是Library的默认视图,不过我喜欢稍微不同的样子,以便我能一下看到更多的控件。为了改变Library窗口中的视图,右键点击控件视图,可以选择不同的显示风格。你也可以点击窗口左下角的Gear(齿轮)按钮。下图是Leopard中的?icons and labels?风格:
而在Snow Leopard中是:
最后一个窗口是Inspector Window:
Inspector具有四种不同的视图,可以通过窗口顶部的标签栏来选择。这些视图分别是Attribute Inspector、Connections Inspector、Size Inspector和Identity Inspector。Inspector大致和Visual Studio中的Property Explorer类似。它向你显示当前选中的Cocoa Touch对象的所有属性。也可以用它来设置可视化属性、布局等等。在创建Outlets和Actions时也用得着它,这个主题后面会谈到。在下面的图片中,我们在Document窗口中选择了Window对象,所以我们可以查看这个对象的相关属性。
现在,我们对Interface Builder窗口已经大致浏览了一遍,接下来让我们实际地创建一些东西。我们要创建如下这样的界面:
首先,拖一个?Round Rect Button(圆角矩形按钮)?(UIButton)到窗口上。接着,在按钮上双击来设置文本。在这个过程中你会注意到,你会获得少许的指导。这些指导是基于苹果的人机交互向导(Human Interface Guidelines)的,来辅助你在视图上以适合的间距等来定位控件。
在窗口上添加了按钮后,拖入一个?Label(标签)?(UILabel)控件。改变它的尺寸以便接近窗口的宽度。接着双击这个label控件,删除文本以使应用程序启动的时候,标签的内容是空白的。
如果你正确地完成了所有步骤,那么你的Document Window将显示如下的样子(点击?Window?旁边的箭头就会看到它包含的子控件):
我们现在创建了第一个窗口界面。不过,不像传统的.NET GUI开发,你还不能编程访问这些控件。假如这是一个WPF应用程序,你一拖动控件到设计界面上,你就能通过this.ControlName这样的形式来访问它。如果你马上去查看MainWindow.designer.cs文件,除了?window?属性外,你看不到任何其他代码。为了让这些对象能被代码访问,我们必须通过Outlets把它们关联上。你在Interface Builder中创建一个Outlet(对象关联口)的时候,MonoDevelop将在这个类的designer.cs文件中添加一个匹配的属性,让你可以编程访问这些控件。
Outlets(对象关联口)我们来为之前添加的标签和按钮控件添加outlets,以便能在代码中访问它们。这点在Leopard和Snow Leopard上也有所区别,所以大家要严格遵循这里提到的正确指示。
【SNOW LEOPARD指示开始】
确保Library Window是打开的。点击顶部的?Classes?标签。点击第一个下拉列表框,这个就是所谓的?Library?,滚动到最后选择?Other Classes?。就会显示在你的项目中存在的自定义类。从上面列表中选择AppDelegate,接着选择下面的?Outlets?标签:
点击?+?按钮两次来创建两个新的Outlet。
【SNOW LEOPARD指示结束】
【LEOPARD指示开始】
首先,保证在Document Window中App Delegate是被选中的。如果App Delegate未被选中,那么在创建Outlets的时候,它们的属性不会被创建,或者会在错误的地方创建。
接下来,来看Identity Inspector。Identity Inspector是Inspector窗口的最后一个标签页。现在,在Identity Inspector中定位到?Class Outlets?部分。在Class Outlets上,点击?+?按钮两次来创建两个新的Outlet。
【LEOPARD指示结束】
每个outlet都具有一个名称和一个类型。名称代表了控件属性的名字,这个类似于ASP.NET的ID,或WPF中的Name。类型是 Outlet的实际类型,比如UIButton、UILabel、UITextView等等。为了命名它们,在它们的名称上双击,键入相应的名称。对于我们之前添加的outlet来说,我们修改为?btnClickMe?和?lblResult?。
目前,它们两者的类型都是?id?。如果你不改变类型,就能把它们挂接到任何东西上,因为id就意味着动态类型,本质上就是.NET世界中的 ?object?。id类型虽然好,不过我们打算把它们改为实际的类型。过一会我们会看到这样做会有什么不同。现在,双击btnClickMe的类型,键入?UIButton?。你的Class Outlets 窗口应该显示如下这个样子:
如果在里面没有window的outlet,意味着你没有在App Delegate创建outlet。如果这样的话,删除Outlets,保证在Document Window中选中App Delegate,重新创建outlets。
现在我们已经创建了这些outlets了,就需要实际地把它们关联到我们的控件上。首先,在Inspector窗口上点击第二个标签页,来选中 Connections Inspector。在Outlets部分,应该可以看到我们之前创建的两个Outlets了。然而,你不能把它挂接到任何东西上。注意,?window?这个outlet已经挂接到?Window?对象上了。
为了挂接我们的Outlets,我们要从?Outlets?中的outlet圆点上,拖动到我们想要挂接的控件上。在这样做的时候,我们将会看到如下所示的效果:
对两个Outlet都进行这样处理。你也可以从Connections Inspector拖到Document Window上。如果控件相互重叠的情况下,这样就很有用。下面的截图就描述了这种方式:
在我们这样做的时候,你可能会注意到一些有趣的事情。因为设置lblResult的类型为UILabel,所以在我们把它的 Outlet拖到Window的时候,它只允许我们挂接到那些类型一致的控件上,在这个例子中就是UILabel。另外一方面,btnClickMe能被拖到任何东西上,因为它具有动态的ID类型。这就是我们要设置强类型outlet的一个原因,以便降低它挂接到错误控件上的可能性。当然,这不是必须的,不过这样做是一个良好的习惯。
好的,现在我们创建好了界面了,outlets也挂接好了,让我们回到MonoDevelop把一起串在一起。
回到MonoDevelop如果你打开MainWindow.designer.cs,在其中会到两个属性:
[MonoTouch.Foundation.Connect("btnClickMe")]private MonoTouch.UIKit.UIButton btnClickMe { get { return ((MonoTouch.UIKit.UIButton)(this.GetNativeField("btnClickMe"))); } set { this.SetNativeField("btnClickMe", value); }}[MonoTouch.Foundation.Connect("lblResult")]private MonoTouch.UIKit.UILabel lblResult { get { return ((MonoTouch.UIKit.UILabel)(this.GetNativeField("lblResult"))); } set { this.SetNativeField("lblResult", value); }}
有了这两个属性,就可以让我们通过代码来访问标签和按钮了。在这里要注意一件有趣的事情??就算我们声明btnClickMe的类型为 id,而自动创建出来的属性也是强类型的UIButton。这是因为MonoDevelop足够智能,可以查看outlet背后实际的类型,以便创建适合类型的属性。这对于我们很有用,因为意味着我们在每次使用它的时候,都无需把btnClickMe属性转换为UIButton类型。
现在回到Main.cs文件,查看AppDelegate。我们来看一下FinishedLaunching方法。
// This method is invoked when the application has loaded its UI and its ready to runpublic override bool FinishedLaunching (UIApplication app, NSDictionary options){ // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); return true;}
正如注释所建议的,这个方法在Application实例化后且已经准备好运行之时,由Objective-C运行时来调用。第一句(window.AddSubview)被注释掉了,我们会在谈论Model View Controller(MVC)模式的时候来研究它的真正作用。
下一句window.MakeKeyAndVisible,设置MainWindow为主窗口并让其显示出来。在 iPhone开发中,真正有意思的事情是你永远有且有一个窗口在显示。如果想在iPhone应用程序中显示不同的界面,你要创建新的视图,并用视图控制器把其?推?到前端。然而,你不调用这个方法,iPhone OS就不会发送事件到你的窗口上。MakeKey这个部分是真正起作用的,而AndVisible部分实际上留有传统OS X Cocoa框架的痕迹。
我们来添加一些新代码到这个文件中。在我们创建Outlets的时候,我们是在AppDelegate中创建它们的。那意味着它们会出现在AppDelegate类中,所以可以在这里来访问它们。和传统的.NET GUI编程有一点不同的是,通常会有一个MainWindow.cs文件,在那里面来处理所有窗口相关的代码。而在这里,我们遵循Objective-C 的模式,就把代码放在AppDelegate中。
我们来把AppDelegate类改为如下这样子:
// The name AppDelegate is referenced in the MainWindow.xib file.public partial class AppDelegate : UIApplicationDelegate{ //---- number of times we've clicked protected int _numberOfClicks; // This method is invoked when the application has loaded its UI and its ready to run public override bool FinishedLaunching (UIApplication app, NSDictionary options) { // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); //---- wire up our event handler this.btnClickMe.TouchDown += BtnClickMeTouchDown; return true; } protected void BtnClickMeTouchDown (object sender, EventArgs e) { //---- increment our counter this._numberOfClicks++; //---- update our label this.lblResult.Text = "Hello World, [" + this._numberOfClicks.ToString () + "] times"; } // This method is required in iPhoneOS 3.0 public override void OnActivated (UIApplication application) { }}
第一件事情,我们添加了一个变量来跟踪点击次数, _numberOfClicks。接着,我们添加这行代码:this.btnClickMe.TouchDown += BtnClickMeTouchDown;
这就把btnClickMe的TouchDown事件(类似于OnClick)挂接到处理程序BtnClickMeTouchDown上
接着,在BtnClickMeTouchDown中,我们简单地用按钮被点击多少次的数量值来更新标签的显示内容。
好了,我们已经完成了所有编程,让我们来构建和运行一下。首先来构建。在菜单中,选择Build : Build All。如果目前为止你都正确的按部就班的话,它应该能构建成功。下一步,就是在iPhone模拟器上运行它。在工具栏上,确保debug|iPhoneSimulator被选中,如图:
接着,在菜单中选择Run : Run。在MonoTouch的评估版中,你只能在模拟器中运行,如果你打算在iPhone中运行,你会得到一个错误。
如果一切正常,模拟器会显示出来(实际上,它有可能隐藏在MonoDevelop窗口的背后,所以你需要切换过去),那么你就能看到如下所示的效果:
点击按钮将会产生下图的结果:
恭喜你!你已经创建并跑起你的第一个iPhone应用程序了。
Actions(动作)在我们刚刚创建的应用程序中,我们有一些Outlets,在代码中可以藉由属性来访问控件。就像在其他.NET GUI模型中,我们能把事件处理程序挂接到它们之上,来对事件作出响应。不过MonoTouch提供了另外一种响应用户输入的方式。这称之为 Actions。Actions类似于WPF的Commands,用这种方式,多个控件可以调用同一个方法,然后依据调用者是谁来决定如何去处理。让我们来稍微仔细地研究一下。确保你已经在MonoDevelop打开了Example_HelloWorld_1应用程序了。
双击MainWindow.xib文件来在Interface Builder中打开它。现在在标签控件下面添加两个按钮,类似下图:
再次,我们要针对Leopard和Snow Leopard作出不同的说明,你要确定按照正确的部分进行操作。
【SNOW LEOPARD指示开始】
在Library窗口中,确保选中?Classes?标签页,并再次在顶部的下拉列表框中选择?Other Classes?。接着,在上面选择AppDelegate,在下面选择?Actions?标签页。创建一个名为ActionButtonClick的 Action。你的Library窗口应该看起来如下所示:
【SNOW LEOPARD指示结束】
【LEOPARD指示开始】
在窗口管理器中,确保选中App Delegate 。接着在Identity Inspector窗口中,在?Class Actions?里面创建一个名为ActionButtonClick的Action。你的Identity Inspector应该看起来如下所示:
【LEOPARD指示结束】
我们刚刚在App Delegate上创建好了一个名为ActionButtonClick的通用动作。现在,要做的就是把它关联到按钮的 TouchDown事件上,以便在按钮被点击的时候,这个Action能被调用。
首先,选择一个动作按钮,接着转到Connections Inspector,把Touch Down拖动Document窗口中的App Delegate。如下图所示:
注意,在我们拖到App Delegate的时候,会显示出一个可用Actions的列表。选择ActionButtonClick,现在按钮上的TouchDown事件就关联到这个动作上了:
为两个动作按钮都进行同样的操作。如果我们在Connections Inspector中查看App Delegate,会这样显示:
通过点击?Multiple?旁边的箭头,可展开和ActionButtonClick相关的控件。保存好.xib文件后,回到 MonoDevelop中。
如果我们看一下MainWindow.designer.cs,会发现多了一行新代码:
[MonoTouch.Foundation.Export("ActionButtonClick")] partial void ActionButtonClick (MonoTouch.UIKit.UIButton sender);
这是我们的Action的分部声明。注意它用MonoTouch.Foundation.Export特性标记进行了装饰。这使 Objective-C运行时可以找到关联到我们Action上的适当方法。编译器实际上会忽略没有任何实现的分部方法(正如我们在这里所见的这个),那么这个分部方法的声明实际上是为了在实现它的时候可以获得代码完成的功能。如果我们回到Main.cs,会看到它的真正作用。在AppDelegate类中,只要键入?partial?就会自动地得到ActionButtonClick的自动完成代码:
我们把如下代码写在里面:
partial void ActionButtonClick (UIButton sender){ //---- show which button was clicked this.lblResult.Text = sender.CurrentTitle + " Clicked";}
现在,如果你运行应用程序,在动作按钮上点击,将会看到如下所示的效果:
此时,我们已经完整地经历了用MonoTouch来开发基本iPhone应用程序的过程。现在,你应该对MonoTouch应用程序的结构、利用事件处理用户交互,以及用Actions来处理用户交互有了基本的了解。
不过,还有一件重要的事情被忽略了,我们应用程序只具有一个界面。在下一篇文章《The Model-View-Controller Pattern in MonoTouch》中,我们将会谈及具有多个界面的应用程序。
示例代码
[b]查看英文原文:[/b]MonoTouch: .NET Development for the iPhone。
2011年02月22日
作者 [b]Bryan Costanich[/b]
引言
直到最近,要为苹果的iPhone开发应用程序的唯一选择就是一头扎进苹果的开发系统中。这意味着,你必须?愿意?在XCode IDE中编写Objective-C代码。对于很多开发人员,学习Objective-C被看作是一个巨大的障碍。特别对于哪些从来不用担心内存管理、指 针和C语言要负责处理的东西的开发人员来说,更是如此。
随着MonoTouch框架(Novell的Mono Project的一部分)的出现,这一切都将改变。Mono Project是微软.NET平台的开源实现。其允许你在几乎任何平台上运行.NET应用程序,包括Apple、FreeBSD、Linux、Unix等 等。MonoTouch是Mono Project的新组成部分,让你能够用C#和.NET平台编写可以运行在iPhone上的应用程序。
本篇文章的目的,就是提供MonoTouch平台的一个完整介绍,让大家知道到那里获取必要的工具,MonoTouch有什么样的限制,以及如何构建一个简单的应用程序。
背景和限制对于打算为iPhone开发应用的.NET开发人员而言,MonoTouch的出现无疑是一件好事。然而,在决定创建应用程序之前,有一些限制和背景知识需要先了解清楚。
它如何工作?在创建MonoTouch应用程序的时候,大部分非UI方面的.NET 3.5功能依旧可用,或者一些还处于计划之中(如.NET 4.0的功能)也囊括其中。这让你可以使用很多业已熟悉的.NET Framework技术来编写应用程序,包括Windows Communication Framework (WCF)、Workflow Foundation (WF)等等。也包括几乎所有的基类库(Base Class Library,BCL),涵盖诸如垃圾收集(Garbage Collection)、线程、数学函数、System.Net、加密等。对于可用的标准.NET程序集列表,见http://monotouch.net/Documentation/Assemblies。MonoTouch是一种基础.NET函数库的特别定制版本,这也类似Silverlight和Moonlight的实现方式。
这意味着,你能够使用MonoTouch的核心程序集来编译标准的.NET 3.5代码成相应的函数库,并在你的应用程序中使用它们。因此,如果你有一个用于其它应用程序的特别函数库,其包含着一些用于工程问题的高级数学函数,那 么只需简单地把这些代码库加入到你的MonoTouch解决方案中,并引用它。在你构建解决方案的时候,编译器就利用MonoTouch核心函数库对其进行编译,接着就能在iPhone应用程序中使用它了。
MonoTouch也包括一些原生iPhone API的包装函数库,如访问位置(Location,GPS)、加速计、地址簿等的函数。MonoTouch也提供相应的功能,让你能够调用那些尚未进行包装的原生Objective-C函数库,所以你可以直接和现存的Objective-C代码进行互操作。
我如何创建用户界面(UI),我能使用Silverlight吗?MonoTouch应用程序的UI需要使用苹果的Interface Builder(界面创建器,IB)应用程序来创建,IB连同iPhone SDK一起提供。Interface Builder使用Cocoa Touch(苹果用于iPhone的UI框架)控件对象,这些控件对象在iPhone上原生提供的。这意味着,你能在应用程序中使用所有的标准 iPhone控件,如选择器(Pickers)、滑动条(Sliders)、按钮等等。
你也能通过代码来创建界面,即实例化Cocoa Touch对象后,把它们添加到应用程序的视图(Views)中(关于视图,后面会详细讲述)。
然而,你不能利用传统的.NET技术,如Silverlight、WPF、WinForms等来创建MonoTouch界面。
Cocoa Touch使用一种融合了MVC(Model View Controller)模式思想的结构,我们将在后面一篇文章中介绍。
我如何分发我的应用?MonoTouch应用程序的分发完全和传统iPhone应用程序的分发一样,既可以通过苹果App Store,也可以通过企业部署。
App Store是一个在线资源库,让用户可以付费购买(如果不是免费的话)和下载应用程序。可以从iTunes中访问,或直接通过iPhone本身来访问。为 了得到通过App Store分发应用的许可,你必须向苹果注册,并支付每年99美元的年费。
企业部署方式就是为公司开发内部应用程序,并分发给员工等人员使用,无需把应用在App Store中列出。
什么是许可模型?不像Mono那样,MonoTouch不是开源的,且是一个收费产品。这意味着,如果你打算开发一些实际的应用,就必须购买软件许可。
[b]专业版($399)[/b]??单个的个人开发人员许可,让你可以开发应用程序,并通过苹果App Store来分发它们。 [b]企业版($999)[/b]??单个的企业开发人员许可,让你可以开发应用程序,并通过苹果App Store来分发它们,或者进行企业部署。 [b]企业版,5人($3999)[/b]??和企业版一样,只是提供了5个坐席的授权。 以上所有选项都包括了一年的免费升级权益。
还有一个评估版本,你只能把应用部署到模拟器中。出于介绍的目的,我们只需要评估版本就行。
MonoTouch有哪些限制?没有即时(JIT)编译根据苹果的iPhone政策,任何应用程序都不能包含需要JIT编译的代码。但是稍等,.NET确实能正确工作,是不?对,不过MonoTouch是通过把应用程序编译为原生的iPhone程序集来跳过这个限制的。但是,这也带来了几个限制。
[b]泛型[/b]??泛型是由JIT编译器在运行时进行实例化的,然而,Mono具备一种提前(Ahead of Time ,AOT)编译的模式,可以为类似List这样的泛型集合生成方法和属性。而泛型的其他用法,例如泛型虚方法、泛型类型上的 P/Invokes和Dictionary上的值类型,就不被支持(虽然存在Dictionary的代替方法)。 [b]动态代码生成[/b]??因为动态代码生成依赖于JIT编译器,所以对任何动态语言编译的过程也不能支持。包括System.Reflection.Emit、 Remoting和动态语言运行时(DLR)。 C#是唯一的语言另外,目前用于编写MonoTouch应用程序的唯一可用语言是C#。Visual Basic.NET有望在MonoTouch未来的发布中支持,不过此时此刻我们别无选择。
更多信息相关限制的完整列表和更多的信息,包括一些代替方法,可用参见http://monotouch.net/Documentation/Limitations。
入门要进入为iPhone创建MonoTouch应用程序的大门,我们需要下面几样东西:
一个使用Intel CPU的Mac电脑,其要安装MacOSX 10.5或10.6 (Leopard或Snow Leopard) 苹果的iPhone SDK 3.0或更高版本 Mono的当前版本 MonoTouch SDK 一个IDE工具,如MonoDevelop或XCode,或一个文本编辑器程序 安装着Leopard或Snow Leopard的Mac这是最重要也是最容易忽视的需求。尽管,理论上你能在任何平台上开发大部分应用程序,然而iPhone Simulator和Interface Builder只能在Leopard和Snow Leopard上运行。另外,编译器本身用到了一些特定于Intel Mac机器的底层功能,所以购买这样一台电脑是绝对必须的。
苹果的iPhone SDKiPhone SDK可通过http://developer.apple.com/iphone/来免费下载,不过必须在苹果网站上注册,才能访问这个地址。
在安装了iPhone SDK后,要确保你能正常启动iPhone Simulator。要启动它,只需打开Spotlight,键入iPhone Simulator。
Mono一旦你测试iPhone Simulator正常,那么就要安装Mono for OSX的最新版。Mono可以从http://mono-project.com/Downloads下载。记住要点击?Intel?版本的链接,不要点CSDK版本。同样,安装MonoTouch SDK之前也需要安装Mono的。Mono的安装包是磁盘镜像的形式,挂接镜像,双击安装包,根据安装向导完成安装过程。
MonoTouch SDK接下来,下载和安装最新的MonoTouch SDK。你既可以在MonoTouch商店(http://monotouch.net/Store)购买,购买后会收到一个下载链接,也可以从http://monotouch.net/DownloadTrial下载评估版。如果购买了MonoTouch,你就能把应用程序部署到一台正确配置了的iPhone上,不过也可像我这样,仅仅在模拟器中运行。所以,目前 而言试用/评估版就足够了。
文本编辑器或集成开发环境(IDE)如果你打算创建MonoTouch应用程序,所需的所有东西就是前面提及的,和一个文本编辑器。你能创建所有代码文件,并用命令行(终端窗口)来手动编 译。这种方式虽然可行,但是实际操作起来可能会非常痛苦,所以我们还是需要使用一个IDE来开发我们的应用程序。
你可以编辑/hack一下XCode(随iPhone SDK一起安装)来利用MonoTouch的函数库和编译器,也可以使用MonoTouch版的MonoDevelop,其已经为MonoTouch应用 程序做好所有配置了。我们理所当然要用MonoDevelop,所以访问这里http://monodevelop.com/Download/Mac_MonoTouch来 下载它。要安装MonoDevelop,只用把下载文件拖到应用程序目录中就行。
如果你已经正确安装Mono,那么MonoDevelop应该可以正常启动。
Hello World应用程序现在,一切已经准备妥当,让我们开始来尝试开发点东西了。
MonoDevelop首先,启动MonoDevelop。你应该会看到和下图类似的界面【译者注:如果OSX的首选语言是中文的话,MonoDevelop的菜单和工具栏的文字显示不正常,所以最好把English拖到语言选项的第一位】:
作为一个标准的IDE,看上去还是蛮熟悉的。它非常类似Visual Studio、SharpDevelop、Visual C# Express等等。
我们创建一个新解决方案,来包含iPhone项目。这里的解决方案和Visual Studio中的概念一样,实际上你可以在MonoDevelop中打开Visual Studio创建的解决方案。在MonoDevelop中的一个不同点就是,你能够在一个MonoDevelop实例中打开多个解决方案,正如下面的截图所示:
这完全是由于在OSX中,你不能启动MonoDevelop的多个实例(事实上,任何程序都不行),且没有任何变通方法。所以,如果你需要在解决方案间切 换(例如,你希望另外打开一个包含示例代码的解决方案),你就能简单地一次性打开多个。
那么,说了上面这么多,让我们现在来创建一个解决方案吧。在菜单中,点File:New:Solution:
我们要创建一个如下面截图所示的?iPhone MonoTouch Project?。选中它,并命名为Example_HelloWorld_1。
这里再次和Visual Studio中创建新解决方案的对话框很类似。点击Forward ,显示下一屏,直接点击OK,因为我们不需要这些功能:
你现在应该可以看到如下所示的解决方案视图了(注意,我展开了解决方案中的节点,以便显示出所有文件和引用程序集):
我们来过一遍这些东西:
[b]References[/b] ??这个文件夹包含MonoTouch应用程序需要的基本引用。MonoTouch程序集包括特定于iPhone的任何东西,也包括了所有Cocoa Touch控件的包装器,以及类似位置、数据等核心iPhone接口。以System.开头的程序集是.NET的基类库和运行时,其被裁减过以便能运行在 iPhone上。 [b]Main.cs[/b] ??这和控制台应用程序、WPF应用程序等是一致的。在这里调用的是static void main() ,其是作为应用程序的入口点。过一下我们会仔细研究一下这个文件。 [b]MainWindow.xib和MainWindow.xib.designer.cs[/b] ??这个和Winforms Window或WPF Window类似。xib文件实际上要在Interface Builder中进行编辑,而designer.cs文件包含这个窗体的属性。 让我们来仔细研究一下Main.cs文件中的代码:
using System;using System.Collections.Generic;using System.Linq;using MonoTouch.Foundation;using MonoTouch.UIKit;namespace Example_HelloWorld_1{ public class Application { static void Main (string[] args) { UIApplication.Main (args); } } // The name AppDelegate is referenced in the MainWindow.xib file. public partial class AppDelegate : UIApplicationDelegate { // This method is invoked when the application has loaded its UI and its ready to run public override bool FinishedLaunching (UIApplication app, NSDictionary options) { // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); return true; } // This method is required in iPhoneOS 3.0 public override void OnActivated (UIApplication application) { } }}
其中有两个有趣的地方。它包含一个Application类和一个AppDelegate类。从这里开始,和传统的.NET GUI开发就有所不同了。
iPhone应用程序的工作方式是,应用程序类(继承于UIApplication)包含了所有窗口、视图、控件和资源等等;同时应用程序委托类中(继承于UIApplicationDelegate)处理来自iPhone OS的回调,实际上会包括应用程序运行周期(Lifecycle)的事件(例如应用程序启动和应用程序终止)和大量的运行时事件(例如低内存报警)。
通过在应用程序委托类处理这些事件,你就能够响应它们。比如,在应用程序关闭的时候,WillTerminate()方法将被调用,这样就有机会去保存任何用户数据、应用程序状态等。
在Application类中,具有一个Main() 方法。调用UIApplication.Main,Objective-C运行时将查找MainWindow.xib文件(它包含了 UIApplicationDelegate类的名称),实例化Application类(为单实例)后,就接着调用AppDelegate类中运行周期事件。
[b]你不必把主窗体的名称命名为?MainWindow.xib? (即所谓的主界面文件)。你可以任意命名它,只需告知编译系统去查找哪个文件就行。如果你希望它去查找不同的文件,在项目文件上右键打开项目选项,点击Options ,接着在Build : iPhone Application : Main Interface File中即可设置主窗体对应的文件。在应用程序启动的时候,Objective-C运行时将会尝试去加载那个文件,并根据这个文件内的设置来查找应用程序委托类。
另外,你也可以任意命名你的应用程序委托类。默认情况下,它的名称为?AppDelegate?。要改变它,在Interface Builder中打开主界面文件,修改Application Delegate的名称。
[/b]一会我们会回到Main.cs文件上来,不过首先来深入研究下应用程序的实际GUI。
Interface Builder目前为止,已经读到了我们的iPhone应用程序的部分代码,可以深入研究一下如何构建它的界面了。苹果的应用程序设计工具包称之为Interface Builder。Interface Builder可以和开发环境松耦合。它可以编辑那些定义应用程序GUI的.xib文件。NIB和XIB的比较:Nib文件包含了窗口、控件等的XML表示,类似于WPF/Silverlight中的XAML模型。
不管是在XCode中编写Objective-C,或在MonoDevelop中编写C#,Interface Builder的用法都是完全一样的。能这样,完全是因为MonoDevelop能够监测Nib文件的变更,并添加/删除对应于Nib文件的适当代码到 designer.cs文件中。
你当然可以不打开Interface Builder,纯粹通过编程来创建整个GUI,有些开发人员确实也是这样做的。很多事情在Interface Builder也无法完成,就此而言,你还是需要编程来完成某些事情。Interface Builder隐藏了一些复杂的部分,在入门的时候,很容易使用Interface Builder来熟悉iPhone应用程序GUI的一些概念。
那么,说了这么多,让我们来着手创建界面了。在MainWindow.xib文件上双击。Interface Builder将启动,并能看到如下图所示的东西:
让我们逐一研究一下这些窗口。从左到右,分别是:Document Window(文档窗口)、Design Surface Window(设计界面窗口)、Library Window(控件库窗口)和Inspector Window(检查器窗口)。
首先让我们来看Document Window:
这个窗口显示了在.xib文件中的所有对象。这是默认的视图,你会发现它虽然样子漂亮,但没有太大用处,因为你的对象实际上是层级排列的,而图标视图只能同时显示一级。在我们添加控件到界面上的时候,它不会显示在这个视图中。所以,我们需要通过点击View Mode工具条上中间的图标来改变列表视图。改变后,应该会显示如下的样子:
下一个窗口是设计界面。也就是,我们实际拖拽Cocoa Touch控件来设计我们界面的地方:
当然,由于我们尚未添加任何控件到上面,所以现在还是一片空白。
下一个窗口是Library。Library包含了所有能在设计界面上使用的Cocoa Touch控件。如果你运行Leopard【译者注:原文这里为Snow Leopard,实际是错误的。】,那么你的控件库窗口如下所示:
如果你运行Snow Leopard【译者注:原文这里未Leopard,实际是错误的。】,它应该是这个样子:
注意它们两者还是非常类似的。在Snow Leopard里,有一个名为?Classes?的标签页,我们后面会讲到这个东西,不过这也是唯一的不同点。
这是Library的默认视图,不过我喜欢稍微不同的样子,以便我能一下看到更多的控件。为了改变Library窗口中的视图,右键点击控件视图,可以选择不同的显示风格。你也可以点击窗口左下角的Gear(齿轮)按钮。下图是Leopard中的?icons and labels?风格:
而在Snow Leopard中是:
最后一个窗口是Inspector Window:
Inspector具有四种不同的视图,可以通过窗口顶部的标签栏来选择。这些视图分别是Attribute Inspector、Connections Inspector、Size Inspector和Identity Inspector。Inspector大致和Visual Studio中的Property Explorer类似。它向你显示当前选中的Cocoa Touch对象的所有属性。也可以用它来设置可视化属性、布局等等。在创建Outlets和Actions时也用得着它,这个主题后面会谈到。在下面的图片中,我们在Document窗口中选择了Window对象,所以我们可以查看这个对象的相关属性。
现在,我们对Interface Builder窗口已经大致浏览了一遍,接下来让我们实际地创建一些东西。我们要创建如下这样的界面:
首先,拖一个?Round Rect Button(圆角矩形按钮)?(UIButton)到窗口上。接着,在按钮上双击来设置文本。在这个过程中你会注意到,你会获得少许的指导。这些指导是基于苹果的人机交互向导(Human Interface Guidelines)的,来辅助你在视图上以适合的间距等来定位控件。
在窗口上添加了按钮后,拖入一个?Label(标签)?(UILabel)控件。改变它的尺寸以便接近窗口的宽度。接着双击这个label控件,删除文本以使应用程序启动的时候,标签的内容是空白的。
如果你正确地完成了所有步骤,那么你的Document Window将显示如下的样子(点击?Window?旁边的箭头就会看到它包含的子控件):
我们现在创建了第一个窗口界面。不过,不像传统的.NET GUI开发,你还不能编程访问这些控件。假如这是一个WPF应用程序,你一拖动控件到设计界面上,你就能通过this.ControlName这样的形式来访问它。如果你马上去查看MainWindow.designer.cs文件,除了?window?属性外,你看不到任何其他代码。为了让这些对象能被代码访问,我们必须通过Outlets把它们关联上。你在Interface Builder中创建一个Outlet(对象关联口)的时候,MonoDevelop将在这个类的designer.cs文件中添加一个匹配的属性,让你可以编程访问这些控件。
Outlets(对象关联口)我们来为之前添加的标签和按钮控件添加outlets,以便能在代码中访问它们。这点在Leopard和Snow Leopard上也有所区别,所以大家要严格遵循这里提到的正确指示。
【SNOW LEOPARD指示开始】
确保Library Window是打开的。点击顶部的?Classes?标签。点击第一个下拉列表框,这个就是所谓的?Library?,滚动到最后选择?Other Classes?。就会显示在你的项目中存在的自定义类。从上面列表中选择AppDelegate,接着选择下面的?Outlets?标签:
点击?+?按钮两次来创建两个新的Outlet。
【SNOW LEOPARD指示结束】
【LEOPARD指示开始】
首先,保证在Document Window中App Delegate是被选中的。如果App Delegate未被选中,那么在创建Outlets的时候,它们的属性不会被创建,或者会在错误的地方创建。
接下来,来看Identity Inspector。Identity Inspector是Inspector窗口的最后一个标签页。现在,在Identity Inspector中定位到?Class Outlets?部分。在Class Outlets上,点击?+?按钮两次来创建两个新的Outlet。
【LEOPARD指示结束】
每个outlet都具有一个名称和一个类型。名称代表了控件属性的名字,这个类似于ASP.NET的ID,或WPF中的Name。类型是 Outlet的实际类型,比如UIButton、UILabel、UITextView等等。为了命名它们,在它们的名称上双击,键入相应的名称。对于我们之前添加的outlet来说,我们修改为?btnClickMe?和?lblResult?。
目前,它们两者的类型都是?id?。如果你不改变类型,就能把它们挂接到任何东西上,因为id就意味着动态类型,本质上就是.NET世界中的 ?object?。id类型虽然好,不过我们打算把它们改为实际的类型。过一会我们会看到这样做会有什么不同。现在,双击btnClickMe的类型,键入?UIButton?。你的Class Outlets 窗口应该显示如下这个样子:
如果在里面没有window的outlet,意味着你没有在App Delegate创建outlet。如果这样的话,删除Outlets,保证在Document Window中选中App Delegate,重新创建outlets。
现在我们已经创建了这些outlets了,就需要实际地把它们关联到我们的控件上。首先,在Inspector窗口上点击第二个标签页,来选中 Connections Inspector。在Outlets部分,应该可以看到我们之前创建的两个Outlets了。然而,你不能把它挂接到任何东西上。注意,?window?这个outlet已经挂接到?Window?对象上了。
为了挂接我们的Outlets,我们要从?Outlets?中的outlet圆点上,拖动到我们想要挂接的控件上。在这样做的时候,我们将会看到如下所示的效果:
对两个Outlet都进行这样处理。你也可以从Connections Inspector拖到Document Window上。如果控件相互重叠的情况下,这样就很有用。下面的截图就描述了这种方式:
在我们这样做的时候,你可能会注意到一些有趣的事情。因为设置lblResult的类型为UILabel,所以在我们把它的 Outlet拖到Window的时候,它只允许我们挂接到那些类型一致的控件上,在这个例子中就是UILabel。另外一方面,btnClickMe能被拖到任何东西上,因为它具有动态的ID类型。这就是我们要设置强类型outlet的一个原因,以便降低它挂接到错误控件上的可能性。当然,这不是必须的,不过这样做是一个良好的习惯。
好的,现在我们创建好了界面了,outlets也挂接好了,让我们回到MonoDevelop把一起串在一起。
回到MonoDevelop如果你打开MainWindow.designer.cs,在其中会到两个属性:
[MonoTouch.Foundation.Connect("btnClickMe")]private MonoTouch.UIKit.UIButton btnClickMe { get { return ((MonoTouch.UIKit.UIButton)(this.GetNativeField("btnClickMe"))); } set { this.SetNativeField("btnClickMe", value); }}[MonoTouch.Foundation.Connect("lblResult")]private MonoTouch.UIKit.UILabel lblResult { get { return ((MonoTouch.UIKit.UILabel)(this.GetNativeField("lblResult"))); } set { this.SetNativeField("lblResult", value); }}
有了这两个属性,就可以让我们通过代码来访问标签和按钮了。在这里要注意一件有趣的事情??就算我们声明btnClickMe的类型为 id,而自动创建出来的属性也是强类型的UIButton。这是因为MonoDevelop足够智能,可以查看outlet背后实际的类型,以便创建适合类型的属性。这对于我们很有用,因为意味着我们在每次使用它的时候,都无需把btnClickMe属性转换为UIButton类型。
现在回到Main.cs文件,查看AppDelegate。我们来看一下FinishedLaunching方法。
// This method is invoked when the application has loaded its UI and its ready to runpublic override bool FinishedLaunching (UIApplication app, NSDictionary options){ // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); return true;}
正如注释所建议的,这个方法在Application实例化后且已经准备好运行之时,由Objective-C运行时来调用。第一句(window.AddSubview)被注释掉了,我们会在谈论Model View Controller(MVC)模式的时候来研究它的真正作用。
下一句window.MakeKeyAndVisible,设置MainWindow为主窗口并让其显示出来。在 iPhone开发中,真正有意思的事情是你永远有且有一个窗口在显示。如果想在iPhone应用程序中显示不同的界面,你要创建新的视图,并用视图控制器把其?推?到前端。然而,你不调用这个方法,iPhone OS就不会发送事件到你的窗口上。MakeKey这个部分是真正起作用的,而AndVisible部分实际上留有传统OS X Cocoa框架的痕迹。
我们来添加一些新代码到这个文件中。在我们创建Outlets的时候,我们是在AppDelegate中创建它们的。那意味着它们会出现在AppDelegate类中,所以可以在这里来访问它们。和传统的.NET GUI编程有一点不同的是,通常会有一个MainWindow.cs文件,在那里面来处理所有窗口相关的代码。而在这里,我们遵循Objective-C 的模式,就把代码放在AppDelegate中。
我们来把AppDelegate类改为如下这样子:
// The name AppDelegate is referenced in the MainWindow.xib file.public partial class AppDelegate : UIApplicationDelegate{ //---- number of times we've clicked protected int _numberOfClicks; // This method is invoked when the application has loaded its UI and its ready to run public override bool FinishedLaunching (UIApplication app, NSDictionary options) { // If you have defined a view, add it here: // window.AddSubview (navigationController.View); window.MakeKeyAndVisible (); //---- wire up our event handler this.btnClickMe.TouchDown += BtnClickMeTouchDown; return true; } protected void BtnClickMeTouchDown (object sender, EventArgs e) { //---- increment our counter this._numberOfClicks++; //---- update our label this.lblResult.Text = "Hello World, [" + this._numberOfClicks.ToString () + "] times"; } // This method is required in iPhoneOS 3.0 public override void OnActivated (UIApplication application) { }}
第一件事情,我们添加了一个变量来跟踪点击次数, _numberOfClicks。接着,我们添加这行代码:this.btnClickMe.TouchDown += BtnClickMeTouchDown;
这就把btnClickMe的TouchDown事件(类似于OnClick)挂接到处理程序BtnClickMeTouchDown上
接着,在BtnClickMeTouchDown中,我们简单地用按钮被点击多少次的数量值来更新标签的显示内容。
好了,我们已经完成了所有编程,让我们来构建和运行一下。首先来构建。在菜单中,选择Build : Build All。如果目前为止你都正确的按部就班的话,它应该能构建成功。下一步,就是在iPhone模拟器上运行它。在工具栏上,确保debug|iPhoneSimulator被选中,如图:
接着,在菜单中选择Run : Run。在MonoTouch的评估版中,你只能在模拟器中运行,如果你打算在iPhone中运行,你会得到一个错误。
如果一切正常,模拟器会显示出来(实际上,它有可能隐藏在MonoDevelop窗口的背后,所以你需要切换过去),那么你就能看到如下所示的效果:
点击按钮将会产生下图的结果:
恭喜你!你已经创建并跑起你的第一个iPhone应用程序了。
Actions(动作)在我们刚刚创建的应用程序中,我们有一些Outlets,在代码中可以藉由属性来访问控件。就像在其他.NET GUI模型中,我们能把事件处理程序挂接到它们之上,来对事件作出响应。不过MonoTouch提供了另外一种响应用户输入的方式。这称之为 Actions。Actions类似于WPF的Commands,用这种方式,多个控件可以调用同一个方法,然后依据调用者是谁来决定如何去处理。让我们来稍微仔细地研究一下。确保你已经在MonoDevelop打开了Example_HelloWorld_1应用程序了。
双击MainWindow.xib文件来在Interface Builder中打开它。现在在标签控件下面添加两个按钮,类似下图:
再次,我们要针对Leopard和Snow Leopard作出不同的说明,你要确定按照正确的部分进行操作。
【SNOW LEOPARD指示开始】
在Library窗口中,确保选中?Classes?标签页,并再次在顶部的下拉列表框中选择?Other Classes?。接着,在上面选择AppDelegate,在下面选择?Actions?标签页。创建一个名为ActionButtonClick的 Action。你的Library窗口应该看起来如下所示:
【SNOW LEOPARD指示结束】
【LEOPARD指示开始】
在窗口管理器中,确保选中App Delegate 。接着在Identity Inspector窗口中,在?Class Actions?里面创建一个名为ActionButtonClick的Action。你的Identity Inspector应该看起来如下所示:
【LEOPARD指示结束】
我们刚刚在App Delegate上创建好了一个名为ActionButtonClick的通用动作。现在,要做的就是把它关联到按钮的 TouchDown事件上,以便在按钮被点击的时候,这个Action能被调用。
首先,选择一个动作按钮,接着转到Connections Inspector,把Touch Down拖动Document窗口中的App Delegate。如下图所示:
注意,在我们拖到App Delegate的时候,会显示出一个可用Actions的列表。选择ActionButtonClick,现在按钮上的TouchDown事件就关联到这个动作上了:
为两个动作按钮都进行同样的操作。如果我们在Connections Inspector中查看App Delegate,会这样显示:
通过点击?Multiple?旁边的箭头,可展开和ActionButtonClick相关的控件。保存好.xib文件后,回到 MonoDevelop中。
如果我们看一下MainWindow.designer.cs,会发现多了一行新代码:
[MonoTouch.Foundation.Export("ActionButtonClick")] partial void ActionButtonClick (MonoTouch.UIKit.UIButton sender);
这是我们的Action的分部声明。注意它用MonoTouch.Foundation.Export特性标记进行了装饰。这使 Objective-C运行时可以找到关联到我们Action上的适当方法。编译器实际上会忽略没有任何实现的分部方法(正如我们在这里所见的这个),那么这个分部方法的声明实际上是为了在实现它的时候可以获得代码完成的功能。如果我们回到Main.cs,会看到它的真正作用。在AppDelegate类中,只要键入?partial?就会自动地得到ActionButtonClick的自动完成代码:
我们把如下代码写在里面:
partial void ActionButtonClick (UIButton sender){ //---- show which button was clicked this.lblResult.Text = sender.CurrentTitle + " Clicked";}
现在,如果你运行应用程序,在动作按钮上点击,将会看到如下所示的效果:
此时,我们已经完整地经历了用MonoTouch来开发基本iPhone应用程序的过程。现在,你应该对MonoTouch应用程序的结构、利用事件处理用户交互,以及用Actions来处理用户交互有了基本的了解。
不过,还有一件重要的事情被忽略了,我们应用程序只具有一个界面。在下一篇文章《The Model-View-Controller Pattern in MonoTouch》中,我们将会谈及具有多个界面的应用程序。
示例代码
[b]查看英文原文:[/b]MonoTouch: .NET Development for the iPhone。