返回博客首页
← 所有文章

使用 NativeScript 和 UI for NativeScript 在 iOS 中创建 Today 小部件

2015 年 9 月 10 日 — 作者:Nikolay Diyanov

自 iOS 8 以来,Apple 已将 iOS 的各个方面开放给第三方开发者进行扩展。这些扩展包括为您的应用添加自定义键盘,或为 iOS 通知中心创建小部件

今天,我们将介绍在 iOS 通知中心创建 Today 小部件所需的步骤,但由于我们将使用 NativeScript 框架来完成此操作,因此所有操作都将使用 JavaScript 而不是 Swift/Objective-C。

“但是什么是 NativeScript?”,您可能会问。对于那些不知道的人

NativeScript 是一个用于构建跨平台原生移动应用程序的框架。您可以编写一个源代码并在 iOS 和 Android 上重复使用它。但是,为了灵活并能够充分利用原生平台的功能,有时您需要添加平台特定的代码来创建一个真正出色的移动应用程序。根据我们能够访问整个原生 API 并支持 NativeScript 中平台特定代码的承诺,我们想向您展示如何在 NativeScript 应用中实现丰富的 iOS 特定通知。

我们的 Widget 将使用随UI for NativeScript提供的原生 Chart 组件。该组件将显示来自TIOBE 指数的静态数据(为简单起见)。请注意,我们将为此项目使用NativeScript CLI

001-Today-Widget-Simulator

在本文结尾,您可以看到正在运行的应用程序。

> 注意:在 NativeScript 中创建 iOS Today 小部件目前需要在 Xcode 中进行操作,但我们正在考虑无需 Xcode 即可允许这些类型的扩展的方法。如果您有兴趣,请投票支持此功能,并在GitHub 上的此问题中告知我们您的用例。

假设您已安装 NativeScript CLI,让我们深入探讨创建项目的具体步骤。

设置项目

要创建项目并向其中添加必要的 iOS 平台,请在终端中执行以下命令

tns create sample-ios-todaywidget
cd sample-ios-todaywidget
tns platform add ios
tns prepare ios

安装 UI for NativeScript

在任何其他操作之前,让我们确保 Chart 库已就绪。由于上述考虑,我们将直接使用原生 Chart 框架而不是跨平台 JS 封装器。尽管如此,由于 NativeScript 的优势,我们仍将能够仅使用 JavaScript 来使用该库。

UI for NativeScript 可作为 npm 包获得,因此请确保您位于应用程序的主目录(sample-ios-todaywidget)中,并执行以下命令以安装产品

npm i nativescript-telerik-ui --save

这将安装 UI for NativeScript,并将我们感兴趣的 iOS 库(即包含 Chart 组件的 TelerikUI.framework)放置在sample-ios-todaywidget/node_modules/nativescript-telerik-ui/platforms/iOS/TelerikUI.framework 以及sample-ios-todaywidget/lib/iOS/TelerikUI.framework中。

请注意,我们正在使用标志 --save。感谢它,npm 会将此依赖项记录在您应用的 package.json 中。如果打开 package.json,您现在应该在应用的“dependencies”数组中看到nativescript-telerik-ui

> 提示:通过将应用的 npm 依赖项保存在 package.json 文件中,您可以始终通过运行 npm install 来重新生成 node_modules 文件夹。因此,通常的做法是从源代码控制中排除 node_modules 文件夹。sample-ios-todaywidget 应用使用 git 进行源代码控制,因此在其 .gitignore 中包含了 node_modules/

> 注意:UI for NativeScript 是一款商业产品,我们从 npm 获取的 Chart 组件是试用版。非试用版很快就会上市销售。UI for NativeScript 中包含的 SideDrawer 组件是完全免费的。

设置主应用程序目标

在 Xcode 中打开位于 platforms/ios/sampleiostodaywidget.xcodeproj 的 Xcode 项目。

  1. 从左侧窗格中选择 sampleiostodaywidget 项目文件,然后选择 sampleiostodaywidget 目标,最后选择 General 部分。002-NativeScript-Add

  2. General 部分,向下滚动到 Embedded Binaries 部分。单击“+”按钮并导航到 platforms/ios/NativeScript.framework 以添加对它的引用。NativeScript.framework 提供了 NativeScript 运行时。003-NativeScript-Add

    004-NativeScript-Add

NativeScript.framework 是一个共享框架,应用和扩展程序都将使用它。最后,由于两个目标之间共享了一个库,因此带有 Widget 的应用的大小将更小(约 1 MB)。

设置 Today 扩展目标

  1. 选择 sampleiostodaywidget 项目,然后从顶部菜单中添加,选择 Editor >> Add Target
    006-Today-Target-Add

  2. 在出现的对话框中,选择 Application Extension >> Today Extension
    007-Today-Extension

  3. 在出现的对话框中,选择 TodayWidget 作为产品名称
    008-Today-Widget-Name

  4. 导航到 TodayWidget 目标的 General 部分,并在 Link Binary With Libraries 中添加对 platforms/ios/NativeScript.framework 的引用。

  5. sampleiostodaywidget 目标的 Build Phases 中复制 Generate MetadataTodayWidget 目标的 Build Phases。以下是操作方法

  6. 为了添加对 UI for NativeScript 的 Chart 封装器背后的 iOS Chart 组件的引用,请执行以下操作

    1. 导航到 sampleiostodaywidget 目标的 General 选项卡。

    2. 单击 Linked Frameworks and Libraries 的“+”按钮

    3. sample-ios-todaywidget/node_modules/nativescript-telerik-ui/platforms/TelerikUI.framework 或从创建用于包含 UI for NativeScript 原生库的目录中选择 TelerikUI.frameworksample-ios-todaywidget/lib/iOS/TelerikUI.framework011-TelerikUI

  7. 将 TelerikUI.framework 文件夹添加到“Build Settings”中的“Framework Search Paths”。screen_shot_2018-08-15_at_4.54.24_pm

    "$(SRCROOT)/../../node_modules/nativescript-telerik-ui/platforms/ios/"
  8. 将以下标志添加到“Build Settings” -> “Other Linker Flags”:other-linker-flags

    -sectcreate __DATA __TNSMetadata "$(CONFIGURATION_BUILD_DIR)/metadata-$(CURRENT_ARCH).bin" $(inherited)
  9. 从左侧导航窗格中,导航到 TodayWidget/SupportingFiles 并删除文件

    TodayViewController.h
    TodayViewController.m
    MainInterface.storyboard

    012-Delete-Files

     
    我们将在几分钟内使用纯 JavaScript 实现它们。

  10. TodayWidget/Supporting Files/Info.plist 中,将设置为 MainInterfaceNSExtensionMainStoryboard 属性替换为设置为 TodayViewControllerNSExtensionPrincipalClass 属性。结果应如下所示
    013-Info

  11. 右键单击 TodayWidget >> SupportingFiles 并添加一个名为 main.m 的新 Objective-C 文件。
    014-Add-New-File-main

    015-Objective-C

    016-main.m

  12. 在此处插入以下引导代码

    #import <NativeScript/NativeScript.h>

    static TNSRuntime* runtime;

    __attribute__((constructor))
    void initialize() {
     
      extern char startOfMetadataSection __asm("section$start$__DATA$__TNSMetadata");
      [TNSRuntime initializeMetadata:&startOfMetadataSection];
     
      runtime = [[TNSRuntime alloc] initWithApplicationPath:[NSBundle mainBundle].bundlePath];
      TNSRuntimeInspector.logsToSystemConsole = YES;
      [runtime executeModule:@"./tiobe-widget"];
    }

    您可以看到我们正在尝试加载 tiobe-widget 模块。我们将在稍后介绍该模块的实现。

     

  13. 最后但并非最不重要的是,对于此步骤集,选择 sampleiostodaywidget/app,然后从右侧窗格的文件检查器(其中包含 Target Membership 部分)中,选中 TodayWidget 复选框。这会将 JavaScript 文件从主包(sampleiostodaywidget)复制到扩展包(TodayWidget)。017-Target-Membership

您现在需要编写一些 JavaScript 并实现一个柱状图。

添加 TodayWidget 的 JavaScript 代码

在您的应用程序目录中创建一个新文件,并将其命名为 tiobe-widget.ios.js。在仅编译 iOS 时,此文件将移动到 platforms/ios/sampleiostodaywidget/app,并去除“ios”后缀。

基本上,我们首先创建了一个柱状图,并用一些静态数据填充它,并设置了适当的轴样式。 在底部,我们定义了在 TodayWidgetInfo.plist 中设置的主类:

var chart;

UIViewController.extend({
    viewDidLoad: function() {
        this.super.viewDidLoad();

        // 设置图表
        if (!chart) {
            chart = TKChart.alloc().initWithFrame(CGRectMake(0, 0, this.view.frame.size.width, 150));

            chart.plotView.backgroundColor = UIColor.clearColor;
            chart.backgroundColor = UIColor.clearColor;

            chart.gridStyle.horizontalFill = null;
            chart.gridStyle.horizontalAlternateFill = null;
        }
        this.view.addSubview(chart);

        this.preferredContentSize = CGSizeMake(0, 150);
    },
    // 删除任何冗余边距
    widgetMarginInsetsForProposedMarginInsets: function(defaultMarginInsets) {
        return UIEdgeInsetsZero;
    },
    // 当需要更新时重新填充图表数据
    widgetPerformUpdateWithCompletionHandler: function(completionHandler) {
        var dataPoints = [TKChartDataPoint.alloc().initWithXY("C++", 6.782),
                          TKChartDataPoint.alloc().initWithXY("JS", 2.342),
                          TKChartDataPoint.alloc().initWithXY("C#", 4.909),
                          TKChartDataPoint.alloc().initWithXY("Java", 19.565),
                          TKChartDataPoint.alloc().initWithXY("Python", 3.664),
                          TKChartDataPoint.alloc().initWithXY("PHP", 2.530),
                          TKChartDataPoint.alloc().initWithXY("C", 15.621)];

        var tiobeColumnSeries = TKChartColumnSeries.alloc().initWithItems(dataPoints);
        chart.addSeries(tiobeColumnSeries);

        var xAxis = chart.xAxis;
        xAxis.minorTickIntervalUnit = TKChartDateTimeAxisIntervalUnitDays;
        xAxis.style.labelStyle.textColor = UIColor.whiteColor;

        var yAxis = chart.yAxis;
        yAxis.majorTickInterval = 5;

        yAxis.style.labelStyle.textColor = UIColor.whiteColor;
        yAxis.style.labelStyle.firstLabelTextAlignment = TKChartAxisLabelAlignmentLeft | TKChartAxisLabelAlignmentTop;
        yAxis.style.labelStyle.firstLabelTextOffset = { horizontal: 4, vertical: 0 };

        chart.update();

        completionHandler(NCUpdateResultNewData);
    }
}, {
    name: "TodayViewController",
    protocols: [NCWidgetProviding]
});

运行扩展

这就是实现的所有内容。现在在 NativeScript 项目的根目录下运行以下命令

tns prepare ios

platforms/ios 中打开 Xcode 项目。在 Xcode 窗口的左上角选择 TodayWidget 目标和相应的模拟器。点击“构建并运行”按钮。系统会询问要运行哪个应用,选择 Today,然后点击 Run

018-Run

下面您可以看到我们的窗口部件的外观。从顶部绘制通知中心,然后点击“编辑”按钮并将我们全新的 TodayWidget 添加到可用窗口部件列表中。
  


这就是您可以使用 NativeScript 和 UI for NativeScript(全部使用 JavaScript)创建和添加窗口部件的方式。 您可以在 GitHub 上找到完整的项目

编码愉快!