返回博客首页
← 所有文章

在您的 NativeScript 应用中使用第三方 iOS 框架

2015年5月12日 — 作者:Nikolay Diyanov

NativeScript 框架已经提供了构建应用所需相当全面的标准 UI 控件集。但是,如果您想使用第三方库来构建 NativeScript 框架应用呢?我们已经为您准备好了。使用 NativeScript 框架,您可以毫不费力地获取原生 iOS 或 Android 库,并开始使用 JavaScript 来使用它。例如,让我们看看如何使用 NativeScript 框架和 Telerik Chart for iOS 构建一个简单的测震仪应用。最终结果如下所示
sample-ios-seismograph-nativescript

对原生 iOS 库的支持

一周前,我们发布了 Telerik UI for iOS 的新版本,该版本现在包含嵌入式框架。由于 NativeScript 处理此类库并将其 API 暴露在 JavaScript 中的方式,**我们可以保证对最新原生框架的 0 天支持**。

需要注意的是,嵌入式框架有两种形式:调试版和发布版。调试版嵌入式框架允许您在模拟器和真实设备上使用它。但是,Apple 不会接受包含模拟器切片的应用,因此需要发布版。发布版仅支持真实设备。因此,首先我们将使用调试版,这将允许我们在模拟器和设备上测试应用。当部署时间到来时,应将调试版框架替换为发布版框架。

您可以从 此处获取最新 Telerik UI for iOS 的试用版

最后但并非最不重要的一点是,目前要使用第三方 iOS 库,您需要一台 Mac,但这很快就会改变,因为 Telerik AppBuilder 客户端很快将能够将第三方框架和插件加载到 NativeScript 应用中。由于 Telerik AppBuilder 在云端执行 iOS 构建,因此您可以在任何操作系统上的 NativeScript 应用中使用这些第三方库。
 

NativeScript CLI 设置

目前,要在您的 NativeScript iOS 项目中使用第三方库,您需要使用 NativeScript 命令行界面 (CLI)。要获取这些工具,首先您需要 NPM 包管理器,您可以从以下地址下载:https://node.org.cn/download/

安装 NPM 包管理器后,打开一个终端实例并编写以下命令以安装 NativeScript 命令行界面

npm install -g nativescript

提示:如果需要提升权限,请尝试使用以下命令

sudo npm install -g nativescript

有关如何安装 NativeScript CLI 的更详细说明,请查看 本文

项目设置

创建项目

  1. 使用终端中的导航命令,导航到您想要创建项目的目录,然后编写

    tns create Seismograph

    其中 Seismograph 是我们应用的名称。这将在其中包含项目文件的目录中创建一个名为 Seismograph 的文件夹。
  2. 进入 Seismograph 目录

    cd seismograph

  3. 创建 NativeScript 框架和 NativeScript 项目将使用的 iOS 项目模板。为此,请运行以下命令

    tns platform add ios
您可以在 Seismograph/platforms/ios 中找到 iOS 项目模板

添加原生库

现在是时候将 UI for iOS 的嵌入式 iOS 框架添加到项目中了,以便您可以在 NativeScript 项目中使用此框架。假设嵌入式框架的调试版位于 /Users/[USER NAME]/Documents/Telerik/UI for iOS Q1 2015/embedded/TelerikUI/Debug,并且您当前的终端目录是 Seismograph,则命令应为

tns library add ios “/Users/[USER NAME]/Documents/Telerik/UI for iOS Q1 2015/embedded/TelerikUI/Debug/TelerikUI.framework”

让我们跳到您喜欢的文本编辑器并开始编码!

编写应用代码

移除样板代码

为了改善您的入门体验并加快您使用 NativeScript 框架的开发速度,您创建的项目是在一个带有按钮和标签的示例应用的基础上构建的。我们目前不需要这些,所以让我们移除不必要的内容

  1. 转到 app → main-page.xml 并移除 Page 开头和结尾标签之间的内容。
  2. 转到 app → main-page.js 并移除内容。
  3. 删除 app → main-view-model.js 文件
  4. 删除 app.css 文件的内容。
现在我们的项目是一个空白画布,我们可以在上面构建我们的测震仪应用。

测震仪代码

测震仪应用将由 Telerik Chart for iOS 和三个按钮组成 - 用于启动、停止和重置测震仪功能。因此,我们可以使用以下布局

<Page loaded="onPageLoaded" xmlns="http://www.nativescript.org/tns.xsd">
    <GridLayout margin="20" columns="auto, *">
        <StackLayout orientation="vertical" dock="top">
          <Button id="Start" tap="tapStart" text="Start"/>
          <Button id="Stop" tap="tapStop" text="Stop"/>
          <Button id="Reset" tap="tapReset" text="Reset"/>
        </StackLayout>
    <Placeholder creatingView="creatingChart" col="1"/>
    </GridLayout>
</Page>

在这里我们可以看到,我们声明了需要 onPageLoaded、tapStart、tapStop、tapReset 函数,我们将在稍后的 main-page.js 文件中添加这些函数。

您可以注意到,我们在这里没有显式声明 Chart 类型。我们可以这样做,因为 Placeholder 控件为我们提供了便利。借助它,我们可以快速加载任何原生组件,而无需为其创建包装器。以下是创建 Chart 方法的实现,该方法在 creatingView 事件中调用

var chart;
  
function creatingChart(args) {
    chart = TKChart.new();
    args.view = chart;
}
  
exports.creatingChart = creatingChart;

需要注意的是,Placeholder 控件与任何包装器的缺乏最适合单平台项目。您甚至可以注意到创建新的 TKChart 实例的 Objective-C 符号,其中 TKChart 是原生 iOS 图表的类型。在跨平台项目中,最好先创建一个公共包装器,然后直接在 xml 中使用它。但是,这是另一篇博文要讨论的主题。

现在我们已经有了 Chart,是时候用一些加速度计数据来填充它了。为此,我们将使用专用的 Apple CoreMotion API。当然,尽管我们正在使用原生 API,但我们编写的都是 JavaScript 代码。

  1. 首先,让我们创建一个 CMMotionManager iOS 类的实例,该类负责从加速度计传感器获取数据。在 onPageLoaded 方法中将它返回数据的间隔设置为 0.2

    var motionManager = new CMMotionManager();
      
    function onPageLoaded(args) {
        motionManager.accelerometerUpdateInterval = 0.2;
      
        startOperations();
    }
      
    exports.onPageLoaded = onPageLoaded;

    在这里,我们调用了一个 startOperations() 方法,其用途将在下面解释。

  2. startOperations 检查设备上是否存在加速度计传感器,如果存在,则每 0.2 秒返回来自加速度计的更新。以下是方法实现

    var coefficient = 1000;
      
    function startOperations() {
        if(motionManager.accelerometerAvailable)
        {
            motionManager.startAccelerometerUpdatesToQueueWithHandler(NSOperationQueue.currentQueue(),
                function (accelerometerData, error) {
                    var acceleration = accelerometerData.acceleration;
      
                    buildChartWithPoints(acceleration.x*coefficient);
                }
            );
        }
    }
      
    exports.startOperations = startOperations;

    每次发送更新时,我们都会调用 buildChartWithPoints 方法来构建图表并根据原生图表的要求填充数据。我们将应用一个系数到提供给图表的值,以便图表可以在一个绘图区域中显示大小不同的值,而无需进行缩放。

  3. 现在是时候讨论 buildChartWithPoints 函数是如何实现的了。

    var dataPoints = [];
      
    function buildChartWithPoints(point) {
    //此处为实现代码
    }

    1. 每次发送更新时,都需要重置图表。为此,我们需要移除图表当前包含的所有数据和注释。

      chart.removeAllData();
      chart.removeAllAnnotations();
    2. 此外,添加到图表中的数据点应根据更新时间绘制在 x 轴上,并根据加速度的大小绘制在 y 轴上。并且,如果图表中已经添加了 26 个数据点,我们应该从图表中移除第一个测量点,使测量数据“流动”。

      var dataPoint = TKChartDataPoint.alloc().initWithXY(NSDate.date(), point);
        
      dataPoints.push(dataPoint);
        
      if (dataPoints.length > 25) {
          dataPoints.shift();
      }
    3. 现在是设置 y 轴的时候了。这是一个范围为 (-1000; 1000) 的轴,它应该与 y 轴在 0 值处相交。以下是操作方法。

      var yAxis = TKChartNumericAxis.alloc().initWithMinimumAndMaximum(-coefficient, coefficient);
      yAxis.position = TKChartAxisPosition.Left;
      yAxis.majorTickInterval = 200;
      yAxis.minorTickInterval = 1;
      yAxis.offset = 0;
      yAxis.baseline = 0;
      yAxis.style.labelStyle.fitMode = TKChartAxisLabelFitMode.Rotate;
      yAxis.style.labelStyle.firstLabelTextAlignment = TKChartAxisLabelAlignment.Left;
      chart.yAxis = yAxis;
    4. 接下来,我们应该添加图表折线序列。我们首先初始化它们,传递 dataPoints 数组。然后,我们使用相应的原生 API 将线的颜色设置为红色。最后,我们将序列添加到图表中。

      var lineSeries = TKChartLineSeries.alloc().initWithItems(dataPoints);
      lineSeries.style.palette = new TKChartPalette();
      var strokeRed = TKStroke.strokeWithColor(UIColor.colorWithRedGreenBlueAlpha(1, 0, 0, 1));
      strokeRed.width = 1.5;
      lineSeries.style.palette.addPaletteItem(TKChartPaletteItem.paletteItemWithDrawables([strokeRed]));
      chart.addSeries(lineSeries);

      添加具有日期时间值作为 x 轴的数据点将自动为图表创建日期时间轴。

    5. 我们可以更改预创建的 x 轴的样式,隐藏标签和刻度,使用以下代码。
      var axisColor = TKStroke.strokeWithColor(UIColor.blackColor());
      axisColor.width = 1;
      chart.xAxis.style.lineStroke = axisColor;
      chart.xAxis.style.majorTickStyle.ticksHidden = true;
      chart.xAxis.style.labelStyle.textHidden = true;
    6. 为了突出显示测量值的幅度,让我们添加一些注释 - 在 -150 和 150 处添加两个虚线注释,以及绿色/黄色/红色带状注释,以描绘出小/中等/大幅度偏差。

      var annotationBandRed = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-1000, 1000), chart.yAxis);
      annotationBandRed.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(255/255.0, 149/255.0, 149/255.0, 0.7));
      chart.addAnnotation(annotationBandRed);
        
      var annotationBandYellow = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-500, 500), chart.yAxis);
      annotationBandYellow.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(252/255.0, 255/255.0, 138/255.0, 0.7));
      chart.addAnnotation(annotationBandYellow);
        
      var annotationBandGreen = TKChartBandAnnotation.alloc().initWithRangeForAxis(TKRange.alloc().initWithMinimumAndMaximum(-300, 300), chart.yAxis);
      annotationBandGreen.style.fill = TKSolidFill.solidFillWithColor(UIColor.colorWithRedGreenBlueAlpha(152/255.0, 255/255.0, 149/255.0, 1));
      chart.addAnnotation(annotationBandGreen);
        
      var dashStroke = TKStroke.strokeWithColor(UIColor.colorWithRedGreenBlueAlpha(0, 0, 0, 0.5));
      dashStroke.dashPattern = ([6, 1]);
      dashStroke.width = 0.5;
        
      var positiveDashAnnotation = TKChartGridLineAnnotation.alloc().initWithValueForAxis(150, chart.yAxis);
      positiveDashAnnotation.style.stroke = dashStroke;
      chart.addAnnotation(positiveDashAnnotation);
        
      var negativeDashAnnotation = TKChartGridLineAnnotation.alloc().initWithValueForAxis(-150, chart.yAxis);
      negativeDashAnnotation.style.stroke = dashStroke;
      chart.addAnnotation(negativeDashAnnotation)
    7. 在 BuildChartWithPoint 函数的底部,我们添加了一个自定义指针注释,它在图表上“绘制”线条。下面我们只展示如何使用自定义注释。在下一段中,您可以看到如何实现注释本身。

      if(dataPoints.length > 1) {
        var needle = NeedleAnnotation.alloc().initWithXYForSeries(dataPoint.dataXValue, dataPoint.dataYValue, lineSeries);
        needle.zPosition = TKChartAnnotationZPosition.AboveSeries;
        chart.addAnnotation(needle);
      }

      至此,buildChartWithPoint 方法的实现已完成。
  4. 自定义指针注释可以在一个新的 js 文件中实现,我们可以将其命名为 needle-annotation.js。

    var NeedleAnnotation = TKChartPointAnnotation.extend({
        layoutInRect: function(bounds)
        {
            var xval = this.series.xAxis.numericValue(this.position.dataXValue);
            var x = TKChartSeriesRender.locationOfValueForAxisInRect(xval, this.series.xAxis, bounds);
            var yval = this.series.yAxis.numericValue(this.position.dataYValue);
            var y = TKChartSeriesRender.locationOfValueForAxisInRect(yval, this.series.yAxis, bounds);
            center = CGPointMake(x, y);
        },
      
        drawInContext: function(context)
        {
            CGContextBeginPath(context);
            CGContextMoveToPoint(context, center.x-20, center.y);
            CGContextAddLineToPoint(context, center.x+20, center.y+20);
            CGContextAddLineToPoint(context, center.x+20, center.y-20);
                 
            CGContextSetRGBFillColor(context, 0, 0, 0, 1);
            CGContextFillPath(context);
        }
    });
      
    exports.NeedleAnnotation = NeedleAnnotation;
  5. 然后,为了在 buildChartWithPoint 函数中使用该实现,我们应该在 main-page.js 文件的顶部加载 needle-annotation.js 文件,方法如下所示。

    var needleAnnotation = require("./needle-annotation");
    var NeedleAnnotation = needleAnnotation.NeedleAnnotation;
  6. 接下来,我们应该添加用于控制图表操作的按钮的实现。

    function tapStart() {
        startOperations();
    }
      
    function tapStop() {
        motionManager.stopAccelerometerUpdates();
        NSOperationQueue.currentQueue().cancelAllOperations();
    }
      
    function tapReset() {
        chart.removeAllData();
        while(dataPoints.length > 0)
        {
            dataPoints.pop();
        }
      
        buildChartWithPoints(0);
    }
      
    exports.tapStart = tapStart;
    exports.tapStop = tapStop;
    exports.tapReset = tapReset;
  7. 最后,我们可以添加一些 CSS 来设置按钮的样式。为此,打开 app.css 文件并添加以下 CSS。

    button {
        font-size: 20;
        background-color: gray;
        color: #3BB9FF;
        margin: 10;
    }
至此,编码部分已完成。现在是时候进行测试了。

应用程序测试

如果我们要编写一个不依赖于任何传感器的应用程序,我们可能会使用以下命令在模拟器中对其进行测试。

tns run ios --emulator

但是,由于我们依赖于加速度计数据,为了获得一个可运行的应用程序,我们应该使用以下命令将应用程序部署到设备上。

tns run ios

就是这样!您可以在 GitHub 上找到完整的 Seismograph 项目

请注意,为了运行此存储库,您需要按照本文前面“添加原生库”部分中讨论的说明添加 Telerik UI for iOS 库。

所以,这就是如何使用 NativeScript 框架和第三方库创建原生 iOS 应用程序的方法。正如我提到的,Telerik AppBuilder 客户端很快将能够加载第三方框架和插件,但这还不是全部 - **我们将围绕 Telerik UI for iOS 和 UI for Android 套件构建一个通用的 API 封装器**,并且得益于它,您将能够从单个共享 JavaScript 代码库中使用 Telerik iOS 和 Android 组件来构建跨平台应用程序。

您最喜欢的 iOS 和 Android 库的跨平台封装器怎么样?

我们即将在 6 月底发布一个 NativeScript 插件市场,它将为您的 NativeScript 开发提供跨平台组件。这些将是通过通用 API JavaScript 封装器封装的原生 iOS 和 Android 库。如果您是 iOS/Android 库的作者,并且希望在市场上看到它们的 NativeScript 封装器,或者如果您正在使用 NativeScript 并缺少一些您最喜欢的原生库的 NativeScript 版本,请告诉我们。

编码愉快!