写点什么

MonoTouch:用.NET 开发 iPhone 应用

  • 2010-05-11
  • 本文字数:12550 字

    阅读完需:约 41 分钟

引言

直到最近,要为苹果的 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 不是开源的,且是一个收费产品。这意味着,如果你打算开发一些实际的应用,就必须购买软件许可。

  • 专业版($399)——单个的个人开发人员许可,让你可以开发应用程序,并通过苹果 App Store 来分发它们。
  • 企业版($999)——单个的企业开发人员许可,让你可以开发应用程序,并通过苹果 App Store 来分发它们,或者进行企业部署。
  • 企业版,5 人($3999)——和企业版一样,只是提供了 5 个坐席的授权。

以上所有选项都包括了一年的免费升级权益。

还有一个评估版本,你只能把应用部署到模拟器中。出于介绍的目的,我们只需要评估版本就行。

MonoTouch 有哪些限制?

没有即时(JIT)编译

根据苹果的 iPhone 政策,任何应用程序都不能包含需要 JIT 编译的代码。但是稍等,.NET 确实能正确工作,是不?对,不过 MonoTouch 是通过把应用程序编译为原生的 iPhone 程序集来跳过这个限制的。但是,这也带来了几个限制。

  • 泛型——泛型是由 JIT 编译器在运行时进行实例化的,然而,Mono 具备一种提前(Ahead of Time ,AOT)编译的模式,可以为类似 List这样的泛型集合生成方法和属性。而泛型的其他用法,例如泛型虚方法、泛型类型上的 P/Invokes 和 Dictionary<TKey, TValue> 上的值类型,就不被支持(虽然存在 Dictionary<TKey, TValue> 的代替方法)。
  • 动态代码生成——因为动态代码生成依赖于 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 SDK

iPhone 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,因为我们不需要这些功能:

你现在应该可以看到如下所示的解决方案视图了(注意,我展开了解决方案中的节点,以便显示出所有文件和引用程序集):

我们来过一遍这些东西:

  • References ——这个文件夹包含 MonoTouch 应用程序需要的基本引用。MonoTouch 程序集包括特定于 iPhone 的任何东西,也包括了所有 Cocoa Touch 控件的包装器,以及类似位置、数据等核心 iPhone 接口。以 System. 开头的程序集是.NET 的基类库和运行时,其被裁减过以便能运行在 iPhone 上。
  • Main.cs ——这和控制台应用程序、WPF 应用程序等是一致的。在这里调用的是 static void main() ,其是作为应用程序的入口点。过一下我们会仔细研究一下这个文件。
  • MainWindow.xib 和 MainWindow.xib.designer.cs ——这个和 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 类中运行周期事件。

你不必把主窗体的名称命名为“MainWindow.xib” (即所谓的主界面文件)。你可以任意命名它,只需告知编译系统去查找哪个文件就行。如果你希望它去查找不同的文件,在项目文件上右键打开项目选项,点击 Options ,接着在 Build : iPhone Application : Main Interface File 中即可设置主窗体对应的文件。在应用程序启动的时候,Objective-C 运行时将会尝试去加载那个文件,并根据这个文件内的设置来查找应用程序委托类。 另外,你也可以任意命名你的应用程序委托类。默认情况下,它的名称为“AppDelegate”。要改变它,在 Interface Builder 中打开主界面文件,修改 Application Delegate 的名称。

一会我们会回到 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 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;
}

正如注释所建议的,这个方法在 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》中,我们将会谈及具有多个界面的应用程序。

示例代码

查看英文原文: MonoTouch: .NET Development for the iPhone


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2010-05-11 11:1414388
用户头像

发布了 254 篇内容, 共 57.8 次阅读, 收获喜欢 2 次。

关注

评论 1 条评论

发布
用户头像
10年的资料,都过时多少年了,居然还出现在推荐阅读列表中。这推荐算法的真的要改进下噢
2020-08-10 17:22
回复
没有更多了
发现更多内容

平安社区平台解决方案,智慧社区综合服务平台搭建

t13823115967

智慧社区管理平台开发

大数据分析决策平台建设方案,警务情报研判系统搭建

t13823115967

程序员如何解决中年危机?资深Android开发带你入门Framework,醍醐灌顶!

欢喜学安卓

android 程序员 面试 移动开发

干了三年的Java,你竟然还不会MySQL性能优化

华为云开发者联盟

Java MySQL sql

字节内部MySQL宝典意外流出!极致经典,堪称数据库的天花板

比伯

Java 编程 架构 面试 技术宅

「每日一题」抖音面试题:请阐述vue数据绑定的实现原理

Java架构师迁哥

使用 iTerm2 打造美观高效的 Mac 终端

郭旭东

Mac 终端 iterm2

影像恋上智能:麒麟9000中的高甜CP创新

脑极体

Spring Cloud Gateway (六) 自定义 Global Filter

Java 网关 SpringcloudGateway

免费下载来自阿里巴巴 双11 的《云原生大规模应用落地指南》

阿里巴巴云原生

阿里巴巴 阿里云 开发者 云原生 k8s

ChaosBlade 在工商银行混沌工程体系中的应用实践

阿里巴巴云原生

云计算 高可用 开发者 云原生 实践

涨知识!一个三非渣本的Android校招秋招之路,赶紧收藏!

欢喜学安卓

android 程序员 面试 移动开发

SourceTree 如何连接 GitLab

TroyLiu

git gitlab SSH sourcetree

Ubuntu 使用 Iptables 做网络转发

wong

iptables Ubuntu20.04

数字化浪潮下 哪些银行业务或“生变”

CECBC

金融科技

数字银行成长性和盈利能力可期

CECBC

数字化转型

如何利用状态同步开发一款游戏

Isa 婷婷

node.js 游戏开发 24小时自助游戏厅 联机游戏

“区块链+有机蔬菜”农产品溯源项目落地

CECBC

农业发展 农业

阿里巴巴云原生的 2020,注定不凡的一年

阿里巴巴云原生

阿里云 容器 开发者 云原生 年终总结

Pulsar 2.7.0 新增特性概览:事务支持、Topic 级别策略配置等

Apache Pulsar

大数据 开源 pulsar Apache Pulsar 消息中间件

Android开发经验谈:2021最新Android常用开源库总结,成功收获美团,小米安卓offer

欢喜学安卓

android 程序员 面试 移动开发

深层互联带领自动旅游讲解耳麦进入“非入耳”时代

DT极客

移动设备管理平台的搭建(基于STF/ATXServer2)

行者AI

人工智能

Arthas 定位 Dubbo 手动注册 Eureka 异常

阿里巴巴云原生

阿里云 云原生 dubbo 工具 征文大赛

Spring 源码学习 13:initMessageSource

程序员小航

spring 源码 源码解析

面试官:不会真有人不知道什么是线程池吧?

Java鱼仔

Java 线程池 并发

引起故障的原因

jorden wang

2021 第一份唠嗑

大头虾

如何通过 Serverless 轻松识别验证码?

阿里巴巴云原生

人工智能 阿里云 Serverless 云原生 数据采集

稻盛和夫《干法》| 教你做快乐的打工人

小匚

读书笔记 程序员 个人成长

【薪火计划】07 - 与领导沟通的方法

AR7

管理

MonoTouch:用.NET开发iPhone应用_.NET_Bryan Costanich_InfoQ精选文章