65.9K
CodeProject 正在变化。 阅读更多。
Home

MvvmCross TipCalc - 第 3 步:创建 iOS UI

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2013 年 3 月 23 日

Ms-PL

8分钟阅读

viewsIcon

45126

TipCalc 教程 MvvmCross v3 - Hot Tuna 的第 3 步。

引言

本文是 MvvmCross v3 - Hot Tuna! TipCalc 教程的第三步!

迄今为止...

我们开始的目标是创建一个应用程序,以帮助计算在餐厅应该留下多少小费。

我们计划基于此概念创建一个 UI

Sketch

为了实现这一点,我们构建了一个“Core”可移植类库项目,其中包含

  • 我们的“业务逻辑” - ICalculation
  • 我们的 ViewModel - TipViewModel
  • 我们的 App,其中包含应用程序的连接,包括启动说明。

然后,我们添加了第一个用户界面 - 用于 Xamarin.Android

Android

对于下一个项目,让我们转向 Xamarin.iOS

要创建 iPhone MvvmCross UI,您可以使用 Visual Studio 项目模板向导,但在这里,我们将像 Core 和 Android 项目一样,从“空项目”开始构建一个新项目。

此外,为了使用 iPhone,我们现在将切换到 Mac 上的 Xamarin Studio 进行开发。

创建新的 iOS UI 项目

向您的解决方案添加一个新项目 - 一个名为 TipCalc.UI.Touch 的“Xamarin.iOS”iPhone 应用程序。

在此之中,您会找到标准的 iOS 应用程序结构

  • Resources 文件夹
  • info.plist “配置”信息
  • AppDelegate.cs
  • Main.cs
  • MyViewController.cs

删除 MyViewController.cs

没有人真正需要 MyViewController微笑 | <img src= " align="top" src="https://codeproject.org.cn/script/Forums/Images/smiley_smile.gif" />  

同样,删除 MyViewController.xib (如果存在)。

添加引用

添加对 CoreCross、Binding 和 MvvmCross - PCL 版本的引用

为新项目添加对可移植库的引用

  • Cirrious.CrossCore.dll
    • 核心接口和概念,包括跟踪、IoC 和插件管理
  • Cirrious.MvvmCross.Binding.dll
    • DataBinding 类 - 您主要从 C# 代码中使用它们
  • Cirrious.MvvmCross.dll
    • Mvvm 类 - 包括视图和 ViewModel 的基类

通常,这些文件位于类似 {SolutionRoot}/Libs/Mvx/Portable/ 的文件夹路径中。

添加对 CoreCross、Binding 和 MvvmCross - iOS 特定的版本的引用

为新项目添加对 Xamarin.iOS 特定库的引用

  • Cirrious.CrossCore.Touch.dll
  • Cirrious.MvvmCross.Binding.Touch.dll
  • Cirrious.MvvmCross.Touch.dll

这些每个库都通过 iOS 特定的添加来扩展其 PCL 对等项的功能。

通常,这些文件位于类似 {SolutionRoot}/Libs/Mvx/Touch/ 的文件夹路径中。

另外,在同一个文件夹中,您还需要添加

添加对 TipCalc.Core.csproj 的引用

添加对您的 TipCalc.Core 项目的引用 - 这是我们在上一步创建的项目,其中包含

  • 您的 Calculation 服务
  • 您的 TipViewModel
  • 您的 App 连接

添加 Setup 类

正如我们在 Android 构建过程中所说的那样,每个 MvvmCross UI 项目都需要一个 Setup 类

此类位于我们 UI 项目的根命名空间(文件夹)中,负责初始化 MvvmCross 框架和您的应用程序,包括: 

  • 控制反转 (IoC) 系统
  • MvvmCross 数据绑定
  • 您的 App 及其 ViewModel 集合
  • 您的 UI 项目及其 View 集合

其中大部分功能都是自动提供的。在您的 iOS UI 项目中,您只需要提供

  • 您的 App - 您与业务逻辑和 ViewModel 内容的连接

对于 TipCalcSetup.cs 中需要的所有内容如下

using System;
using Cirrious.MvvmCross.Touch.Platform;
using TipCalc.Core;
using Cirrious.MvvmCross.Touch.Views.Presenters;

namespace TipCalc.UI.Touch
{
    public class Setup : MvxTouchSetup
    {
        public Setup (MvxApplicationDelegate appDelegate, IMvxTouchViewPresenter presenter)
            : base(appDelegate, presenter)
        {
        }

        protected override Cirrious.MvvmCross.ViewModels.IMvxApplication CreateApp ()
        {
            return new App();
        }
    }
}

修改 AppDelegate 以使用 Setup

您的 AppDelegate 提供了一系列回调,iOS 用它们来通知您应用程序生命周期中的事件。

要在 MvvmCross 中使用此 AppDelegate ,我们需要

  • 修改它,使其继承自 MvxApplicationDelegate 而不是 UIApplicationDelegate

    public partial class AppDelegate : MvxApplicationDelegate
  • 修改它,以便在启动时调用的方法(FinishedLaunching)执行一些 UI 应用程序设置

    • 创建一个新的 presenter - 这个类将决定如何显示 View - 在这个示例中,我们选择了一个“标准”的

      var presenter = new MvxTouchViewPresenter(this, window);
    • 创建一个 Setup 实例并调用 Initialize

      var setup = new Setup(this, presenter);
      setup.Initialize();
    • Setup 完成后,使用 Mvx 控制反转容器来查找并 Start IMvxAppStart 对象

      var startup = Mvx.Resolve<IMvxAppStart>();
      startup.Start();

总而言之,这看起来是这样的

using System;
using System.Collections.Generic;
using System.Linq;

using MonoTouch.Foundation;
using MonoTouch.UIKit;
using Cirrious.MvvmCross.Touch.Platform;
using Cirrious.MvvmCross.Touch.Views.Presenters;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.CrossCore.IoC;

namespace TipCalc.UI.Touch
{
    [Register("AppDelegate")]
    public partial class AppDelegate : MvxApplicationDelegate
    {
        UIWindow window;

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            window = new UIWindow(UIScreen.MainScreen.Bounds);

            var presenter = new MvxTouchViewPresenter(this, window);

            var setup = new Setup(this, presenter);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            window.MakeKeyAndVisible();

            return true;
        }
    }
}

添加您的 View

创建初始 UIViewController

创建一个 Views 文件夹

在此之中,添加一个新的“iPhone UIViewController”,并将其命名为 TipView

这将生成

  • TipView.cs
  • TipView.designer.cs
  • TipView.xib

编辑 XIB 布局

双击 XIB 文件。

这将在 xCode 中打开 XIB 编辑器。

就像我们在 Android 中所做的那样,我在这里不会深入探讨如何使用 XIB iOS 编辑器 - 相反,我只会介绍基础知识,我还会尝试为熟悉 XAML 的人提供一些比较。

从“对象库”拖放以添加

  • 一些 UILabels 用于显示 static 文本 - 这些类似于 TextBlocks
  • 一个 UITextField 用于编辑 SubTotal - 这类似于 TextBox
  • 一个 UISlider 用于编辑 Generosity - 这类似于 ProgressBar
  • 一个 UILabel 用于显示 Tip 结果 - 这类似于 TextBlock

通过拖放,您应该能够快速生成一个与以下类似的界面

design

在 XIB 编辑器中创建“outlets”

一旦您绘制好了 UI,您就可以将这些显示的 UI 字段链接到称为 outlets 的 ObjectiveC 变量。

完成此操作后,Xamarin Studio 中的 Xamarin.iOS 工具将自动检测这些更改,并将这些 ObjectiveC 字段映射回 iOS 应用中的 C# 属性。

要开始执行此操作,您需要从菜单选项“View” -> “Assistant Editor” -> “Show Assistant Editor”在 xCode 中打开“Assistant Editor”。这将显示一个带有 ObjectiveC 代码的小窗格 - 这就是“Assistant Editor”。

完成此操作后,您可以依次按住 Ctrl 键并单击(右键单击)SubTotal、Generosity 和 Tip 这三个字段。

outlet

对于每个字段

  • 在设计器中按住 Ctrl 键单击 UI 字段
  • 这将“弹出”一个列表,其中包含该字段可用的“outlets 和 actions”
  • 找到标记为“New Referencing Outlet”的那个
  • 单击“New Referencing Outlet”右侧的圆圈,然后将其拖到 Assistant Editor
  • 将字段拖放到 Assistant Editor
  • 它会要求您为 outlet 选项提供一个名称

按照此过程,您应该能够为这三个字段创建三个 ObjectiveC outlet 变量

  • SubTotalTextField
  • GenerositySlider
  • TipLabel

完成后,保存您的 xCode 更改(使用 File 菜单)然后退出 xCode。

编辑 TipView.cs

回到 Xamarin Studio,您现在应该会看到 Xamarin 产品已更新 TipView.designer.cs 文件 - 它现在将包含三个带有这三个相同名称的 [Outlet] 属性

  • SubTotalTextField
  • GenerositySlider
  • TipLabel

关闭 TipView.designer.cs 文件 - 此文件是自动生成的局部类,Xamarin Studio 随时可以重新生成它 - 因此没有必要自己编辑它。

而是打开 TipView.cs - 它包含同一个局部类的可编辑部分。

由于我们希望 TipView 不仅是一个 UIViewController ,而且还是一个 Mvvm View,因此请更改 TipView 的继承,使其继承自 MvxViewController

public class TipView : MvxViewController

现在,为了将 TipView 链接到 TipViewModel,请创建一个 public new TipViewModel ViewModel 属性 - 与您在 Xamarin.Android 中所做的完全一样

public new TipViewModel ViewModel
{
    get { return (TipViewModel) base.ViewModel; }
    set { base.ViewModel = value; }
}

要添加数据绑定代码,请转到 TipView 类中的 ViewDidLoad 方法。这是一个在 View 在 iOS 中加载后但在屏幕上显示之前调用的方法。

这使得 ViewDidLoad 成为调用一些数据绑定扩展方法的理想位置,这些方法将指定我们希望 UI 如何绑定到 ViewModel

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    this.Bind (this.TipLabel, (TipViewModel vm) => vm.Tip ); 
    this.Bind (this.SubTotalTextField, (TipViewModel vm) => vm.SubTotal );
    this.Bind (this.GenerositySlider, (TipViewModel vm) => vm.Generosity );
}

这段代码的作用是“在代码中”生成与我们在 Android 中“在 XML 中”生成的完全相同的数据绑定信息。

注意,在调用 this.Bind 之前,我们先调用 base.ViewDidLoad()。这一点很重要,因为 base.ViewDidLoad() MvvmCross 定位 TipViewModel (这个 TipView 将绑定到它)的地方。

总而言之,这看起来是这样的

using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using Cirrious.MvvmCross.Touch.Views;
using Cirrious.MvvmCross.Binding.BindingContext;
using TipCalc.Core;

namespace TipCalc.UI.Touch
{
    public partial class TipView : MvxViewController
    {
        public new TipViewModel ViewModel
        {
            get { return (TipViewModel)base.ViewModel; }
            set { base.ViewModel = value; }
        }

        public TipView () : base ("TipView", null)
        {
        }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            this.CreateBinding (this.TipLabel).To( (TipViewModel vm) => vm.Tip ).Apply(); 
            this.CreateBinding (this.SubTotalTextField).To( (TipViewModel vm) => vm.SubTotal ).Apply();
            this.CreateBinding (this.GenerositySlider.To( (TipViewModel vm) => vm.Generosity ).Apply();
        }
    }
}

Xamarin.iOS 中的绑定

您无疑会注意到,iOS 中的数据绑定看起来与 Android 中的方式大不相同 - 也与您可能期望的 XAML 不同。

这是因为 iOS 中使用的 XIB 格式比 Android AXML 和 Windows XAML 中使用的 XML 格式更不易于手动操作和扩展 - 因此,使用 C# 而不是 XIB 来注册我们的绑定更有意义。

在本教程的这一部分中,我们所有的 iOS 绑定都如下所示

this.CreateBinding (this.TipLabel).To ((TipViewModel vm) => vm.Tip ).Apply(); 

这行代码的意思是

  • TipLabel 的默认绑定属性 - 碰巧是名为 Text 的属性
  • 绑定到 ViewModelTip 属性

与 Android 一样,这默认情况下是一个 TwoWay 绑定 - 这与 XAML 开发人员可能期望看到的不同。

如果您想显式指定 TipLabel 属性使用 Text 而不是依赖默认值,那么您可以这样做: 

this.CreateBinding (this.TipLabel).For(label => label.Text).To( (TipViewModel vm) => vm.Tip ).Apply(); 

在后续主题中,我们将涵盖更多关于 iOS 绑定的内容,包括更多关于绑定到非默认字段;其他基于代码的绑定代码机制;自定义绑定;使用 ValueConverters;以及创建绑定的子视图。

iOS UI 完成!

此时,您应该能够运行您的应用程序。

当它启动时...您应该看到

v1

这似乎工作得很完美,尽管您可能会注意到,如果您点击 SubTotal 属性并开始输入文本,那么您之后就无法关闭键盘。

这是 View 的职责 - 这是一个 UI 问题。所以我们可以只在 iOS UI 代码中,在这个 View 中修复它。例如,要在此处修复,您可以在 ViewDidLoad 方法的末尾添加一个手势识别器,如下所示

View.AddGestureRecognizer(new UITapGestureRecognizer(() => {
    this.SubTotalTextField.ResignFirstResponder();
})); 

继续...

我们可以做更多的事情来使这个用户界面更好,并使应用程序更丰富......但对于这个第一个应用程序,我们将暂时就此打住。 

让我们继续 Windows!

文章

历史 

  • 2013 年 3 月 22 日 - 首次提交    
© . All rights reserved.