如何在React-Native应用程序中添加iOS Today扩展或小部件

我们或多或少都熟悉适用于iOS的精美应用程序扩展。 例如天气扩展,它以一种非常漂亮的方式为我们提供了天气更新。 当您想要快速浏览重要人物时,有时应用程序扩展或窗口小部件非常方便。 在本教程中,我们将介绍如何在React-Native应用程序中添加iOS Today应用程序扩展。

我们将通过本教程逐步实现以下目标:

  1. 创建基本的应用扩展程序或小部件(步骤1-11)
  2. 具有表格视图布局的小部件(步骤12–18)
  3. 一个小部件将通过区块链API获取比特币价格数据,并将定期获取更新。 此小工具将显示美元,INR和THB的比特币汇率。 (步骤19)

我们将创建一个新的应用程序只是为了从一开始就演示它,如果您有现有的应用程序,则可以按照方法的其余部分(从第4步开始)进行操作。

  • 步骤1 :创建一个新应用“ HelloWorldApp ”。 打开终端>导航到您要保存新项目的文件夹,然后运行以下命令
  react-native初始化HelloWorldApp 
  • 步骤2:创建项目后,在项目文件夹中导航。
  cd HelloWorldApp 
  • 第3步 :通过运行命令安装所有基本依赖项-
  npm安装 
  • 步骤4 :进入“ ios ”文件夹,然后在xcode中打开 .xcodeproj文件(如果有,则打开 .xcworkspace文件),方法是双击该文件或使用命令“ open 。 xcworkspace>
  • 步骤5 :在Xcode中打开项目后,单击左侧项目浏览器中的主项目文件,然后选择“ 常规”选项卡。 对于新项目,您可以设置Bundle Identifier值。
  • 第6步 :从菜单中选择文件 > 新建 > 目标…
  • 步骤7 :将显示目标窗口列表。 单击顶部的“ iOS ”选项卡,然后在“ 应用程序扩展 ”下选择“ 今日扩展 ”,然后单击下一步。
  • 步骤8 :在下一个屏幕中输入Product Name(产品名称),保留默认值,然后单击Finish(完成)。
  • 步骤9 :现在您将看到一个弹出窗口,用于激活您的小部件项目方案,如下所示。 点击“ 激活 ”按钮。
  • 步骤10 :您的基本小部件现已准备就绪。 已经为小部件创建了一个包含所有基本文件的新文件夹。
  • 第11步 :在进行下一步之前,请测试我们的基本小部件。 打开终端并使用以下命令运行您的应用-
 反应本机运行iOS 

在模拟器中安装项目后,向左滑动到通知窗口,然后单击那里的“ 编辑”按钮。

在“添加小部件”窗口下,您现在可以看到新的小部件。 按下窗口小部件旁边的“ Plus(+) ”符号,然后单击顶部的“ 完成 ”。 Tada…您的新窗口小部件现在应该在通知部分中可见。

在iOS中,创建基本的小部件是非常简单的过程,但是现实生活中的小部件不仅仅是显示问候世界消息。 让我们修改新创建的小部件以使其更实用。

  • 步骤12 :删除默认的窗口小部件视图。 单击MainInterface.storyboard (在小部件文件夹内)>展开Today View Controller场景 >在Today View Controller中选择View >按键盘上的Delete按钮。
  • 步骤13 :将Today View Controller的高度增加到150。此步骤是可选的,我们这样做是为了更好地了解小部件的故事板布局。
  • 步骤14 :添加TableView。 单击顶部的“ 库”按钮(标记为1),它将显示对象窗口列表。 在搜索框中输入“表格”,然后双击“ 表格视图”
  • 第15步 :创建表格视图后,单击表格视图,然后单击右侧顶部的Size Inspector菜单按钮。 将行高度值从自动更改为60。
  • 步骤16 :在Table View中添加一个Table View单元格 。 单击按钮>在对象列表弹出窗口的搜索框中键入“表”>双击表视图单元格
  • 步骤17 :创建一个Swift类WidgetItemTableViewCell 。 此类将包含我们将为小部件表视图中的每一行呈现的项目。 让我们首先创建类。 右键单击小部件项目文件夹,然后从菜单中选择“ New File…

在“选择模板”窗口中,选择顶部的iOS标签,然后在“源”下选择“ Cocoa Touch Class ”,然后单击“ 下一步”按钮。

在“下一步”窗口中,将默认类名称更新为“ WidgetItemTableViewCell ”,然后单击“ 下一步”按钮

在下一个窗口中,确保在目标下选择了小部件项目,然后按创建按钮。

将使用一些默认代码创建一个WidgetItemTableViewCell.swift文件。 我们将暂时关闭该课程。

  • 步骤18 :现在创建表格视图单元格项。 在每个单元格中,我们将有2个标签项TitleValue 。 再次打开小部件情节提要文件。 点击“表格视图单元格”。 在“ 检查器”视图的右侧,单击“身份检查器”。 从“类”下拉列表中,选择我们新创建的类文件“ WidgetItemTableViewCell ”。

现在,单击“ 属性检查器”按钮,并将“ 标识符 ”的值修改为“ WidgetItem ”。

现在单击“ 大小检查器”按钮,并将“ 行高”修改为60。

让我们在“内容视图”中添加2个标签。 单击WidgetItem内部的“ 内容视图” >单击顶部的“ 库”按钮>在“对象”列表弹出式窗口中,键入“标签”>双击“标签”项

将创建一个标签。 我们将其命名为Title 。 选择新创建的标签>单击“ 属性检查器”按钮>将“ 文本”值修改为“标题”>将颜色更改为“白”>并将文本的对齐方式设置为

现在,单击“ 大小检查器”按钮>单击“ 排列”下拉列表,然后选择“ 水平填充容器 ”>再次单击“ 排列”下拉列表,然后选择“ 垂直填充容器 ”。 现在,您将看到标题标签占据了单元格的整个宽度和高度。

让我们给“标题”标签设置一个10的右边距,以便在小部件的左边框和“标题”标签起点之间有一定的距离。 通过选择“标题”标签,单击“添加约束”按钮(在下图中标记为1)并提供4个约束值(“上”:0,“左”:10,“下”:0,“右”:0),然后单击“添加约束”按钮(标记为3)在下面的图片中)。

现在我们已经完成了标题标签,让我们以相同的方式创建值标签。 单击标题标签>单击顶部的库按钮>在对象列表弹出窗口中搜索“标签”>双击标签

将创建一个新标签,我们现在需要设置Value标签的属性。 单击属性检查器按钮>将标签文本设置为“值”,将颜色设置为“白色”,将对齐方式设置为“右”

与之前单击“尺寸检查器”>单击“ 排列”下拉列表并选择“水平填充容器”相同,然后再次单击“排列”下拉列表并选择“垂直填充容器”。

现在是时候在“价值”标签上添加一些适当的边距了。 选择值标签>在情节提要中单击添加新约束按钮>添加新约束(顶部:0,左侧:0,底部:0,右侧:10)>单击添加约束按钮

我们的表视图行项目已准备就绪,现在我们需要将它们与我们先前创建的类(WidgetItemTableViewCell.swift)链接。 单击情节提要中的“标题”标签,然后单击“ 显示助手编辑器 ”按钮

单击该按钮后,将显示一个新的助手编辑器,如下所示。

默认情况下,该编辑器可能会显示“ TodayViewController.swift”文件,我们需要将其更改为“ WidgetItemTableViewViewCell.swift”。 为此,请单击顶部显示的文件名,然后从下拉列表中选择“ WidgetItemTableViewCell.swift”文件。

现在,将光标放在“标题”项目的顶部>在键盘上按“控制”按钮,然后将其拖动到“ WidgetItemTableViewCell.swift”类中。

弹出窗口应如下所示。 只需填写“ 名称”字段(我给了一个名称“ widgetItemTitle”),将其他名称保持不变,然后按“ 连接”按钮。

它将在WidgetItemTableViewCell类内部生成如下代码。

现在,对“值”标签重复相同的操作。 选择值标签>按下键盘上的Control键,然后将其拖动到WidgetItemTableViewCell类中>填写名称值,然后单击连接按钮。

现在,我们的两个小部件单元项(标题和值)均已创建并与WidgetItemTableViewCell类链接

  • 步骤19 :打开TodayViewController.swift文件,让我们编写其余代码以使我们的小部件可行。

首先在TodayViewController类中添加以下代码(在ViewDidLoad函数之后)

  覆盖 func viewDidAppear( _动画:布尔){ 
  超级 .viewDidAppear(动画) 
  如果 #availableiOSApplicationExtension 10.0,*){ 
  extensionContext?.widgetLargestAvailableDisplayMode = .expanded 
  } 
  自我 .preferredContentSize.height = 200 
  } 
  func widgetMarginInsets(forProposedMarginInsets defaultMarginInsets:UIEdgeInsets)->(UIEdgeInsets){ 
  返回 UIEdgeInsets.zero 
  } 
  @availableiOSApplicationExtension 10.0,*) 
  func widgetActiveDisplayModeDidChange( _ activeDisplayMode:NCWidgetDisplayMode,withMaximumSize maxSize:CGSize){ 
  如果 activeDisplayMode == .expanded { 
  preferredContentSize = CGSize(width:maxSize.width,height:300) 
  } 
  否则, 如果 activeDisplayMode == .compact { 
  preferredContentSize = maxSize 
  } 
  } 

视图出现后,将执行这段代码。 它还将管理表格视图的高度。

我们已经准备好了表格视图,但是还没有编写任何代码来获取数据并将其显示在表格视图中。 我们开始做吧。

在我们的TodayViewController类中,您可以看到一个预填充的函数,名为widgetPerformUpdate 。 每当我们向左滑动到通知窗口以查看我们的小部件时,都会调用此函数。 此功能仅负责更新窗口小部件视图。

如果您查看函数内部的代码,您可能会注意到以下代码行:

  completeHandler(NCUpdateResult.newData) 

completeHandler通知系统是否刷新窗口小部件视图。 检查函数内部的注释,它可以很好地描述它。

好的,让我们写一些代码来获取数据并将其显示在我们的小部件中。

首先在TodayViewController类定义中将 UIViewController替换为UITableViewController ,然后定义一个数组变量,该变量将保存我们的数据以在小部件中显示。

  fileprivate var数据:Array  = Array() 

现在,让我们修改viewDidLoad函数,以定义窗口小部件的初始视图(加载时)。 添加以下代码-

   dictUSD:[String:String] = [“ title”:“ USD”,“ value”:“正在加载...”] 
  let dictINR:[String:String] = [“ title”:“ INR”,“ value”:“正在加载...”] 
   dictTHB:[String:String] = [“ title”:“ THB”,“ value”:“正在加载...”] 
  自我 .data.append(dictUSD 作为 NSDictionary) 
  自我 .data.append(dictINR 作为 NSDictionary) 
  自我 .data.append(dictTHB 作为 NSDictionary) 

我们还需要添加功能以支持我们的表视图控制器

  //此函数根据数据返回表格视图的总行数 
  覆盖 func tableView( _ tableView:UITableView,numberOfRowsInSection部分:Int)-> Int { 
  返回 data.count 
  } 

  //此函数设置表格视图的单元格元素(标题,值) 
  覆盖 func tableView( _ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell { 
   cell = tableView.dequeueReusableCell(withIdentifier:“ WidgetItem”,for:indexPath)  !  WidgetItemTableViewCell 
   item = data [indexPath.row] 
  cell.widgetItemTitle.text = item [“ title”]  ? 串 
 cell.widgetItemValue.text = item [“ value”]  ? 串 
  返回单元 
  } 

现在,我们需要像这样修改widgetPerformUpdate函数-

  func widgetPerformUpdate(completionHandler:( @转义 (NCUpdateResult)->无效)){ 
  //执行必要的设置以更新视图。 
  //如果遇到错误,请使用NCUpdateResult.Failed 
  //如果不需要更新,请使用NCUpdateResult.NoData 
  //如果有更新,请使用NCUpdateResult.NewData 
  let urlPath:字符串=“ https://blockchain.info/ticker” 
   url:NSURL = NSURL(string:urlPath)! 
  let request:URLRequest = URLRequest(URL:URL  URL) 
  let queue:OperationQueue = OperationQueue() 
  NSURLConnection.sendAsynchronousRequest(request,queue:queue,complementHandler:{(响应:URLResponse !, data:Data !, error:Error!)->无效 
  如果 (错误!=  ){ 
  completeHandler(NCUpdateResult.failed) 
  } 其他 { 
   { 
   jsonResult = 试试 JSONSerialization.jsonObject(with:data,options:[]) 
   formatter = NumberFormatter() 
  自我 .data.removeAll() 
  如果  dictionary = jsonResult as ?  [String: Any ] { 
  如果  nestedDictionary = dictionary [“ USD”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
   dictUSD:[String:String] = [“ title”:“ USD”,“ value”:值] 
  自我 .data.append(dictUSD 作为 NSDictionary) 
  } 
  如果  nestedDictionary = dictionary [“ INR”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
  let dictINR:[String:String] = [“ title”:“ INR”,“ value”:值] 
  自我 .data.append(dictINR 作为 NSDictionary) 
  } 
  如果  nestedDictionary = dictionary [“ THB”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
   dictTHB:[String:String] = [“ title”:“ THB”,“ value”:值] 
  自我 .data.append(dictTHB 作为 NSDictionary) 
  } 
  DispatchQueue.main.async { 
  自我 .tableView.reloadData() 
  completeHandler(NCUpdateResult.newData) 
  } 
  } 
  }  let错误捕获  NSError { 
 打印(错误) 
  } 
  } 
  }) 
  } 

如前所述,widgetPerformUpdate函数仅负责小部件视图更新。 在上面的函数中,我们首先从https://blockchain.info/ticker api获取比特币汇率数据,然后解析响应JSON数据并显示在我们的小部件中。 该API返回多种货币的汇率,但我们仅显示USD,INR和THB的比特币汇率。

这是TodayViewController.swift类的完整代码。

  // 
  // TodayViewController.swift 
  // HelloWorldWidget 
  // 
  //由Mai Kachaban在28/01/2019创建。 
  //版权所有©2019 Facebook。  版权所有。 
  // 
  导入 UIKit 
  导入 NotificationCenter 
 今日类类 :UITableViewController,NCWidgetProviding { 
  fileprivate var数据:Array  = Array() 
  覆盖 func viewDidLoad(){ 
  超级 .viewDidLoad() 
  //从其笔尖加载视图后,进行任何其他设置。 
   dictUSD:[String:String] = [“ title”:“ USD”,“ value”:“正在加载...”] 
  let dictINR:[String:String] = [“ title”:“ INR”,“ value”:“正在加载...”] 
   dictTHB:[String:String] = [“ title”:“ THB”,“ value”:“正在加载...”] 
  自我 .data.append(dictUSD 作为 NSDictionary) 
  自我 .data.append(dictINR 作为 NSDictionary) 
  自我 .data.append(dictTHB 作为 NSDictionary) 
  } 
  覆盖 func viewDidAppear( _动画:布尔){ 
  超级 .viewDidAppear(动画) 
  如果 #availableiOSApplicationExtension 10.0,*){ 
  extensionContext?.widgetLargestAvailableDisplayMode = .expanded 
  } 
  自我 .preferredContentSize.height = 200 
  } 
  func widgetMarginInsets(forProposedMarginInsets defaultMarginInsets:UIEdgeInsets)->(UIEdgeInsets){ 
  返回 UIEdgeInsets.zero 
  } 
  @availableiOSApplicationExtension 10.0,*) 
  func widgetActiveDisplayModeDidChange( _ activeDisplayMode:NCWidgetDisplayMode,withMaximumSize maxSize:CGSize){ 
  如果 activeDisplayMode == .expanded { 
  preferredContentSize = CGSize(width:maxSize.width,height:300) 
  } 
  否则, 如果 activeDisplayMode == .compact { 
  preferredContentSize = maxSize 
  } 
  } 
  func widgetPerformUpdate(completionHandler:( @转义 (NCUpdateResult)->无效)){ 
  //执行必要的设置以更新视图。 
  //如果遇到错误,请使用NCUpdateResult.Failed 
  //如果不需要更新,请使用NCUpdateResult.NoData 
  //如果有更新,请使用NCUpdateResult.NewData 
  let urlPath:字符串=“ https://blockchain.info/ticker” 
   url:NSURL = NSURL(string:urlPath)! 
  let request:URLRequest = URLRequest(URL:URL  URL) 
  let queue:OperationQueue = OperationQueue() 
  NSURLConnection.sendAsynchronousRequest(request,queue:queue,complementHandler:{(响应:URLResponse !, data:Data !, error:Error!)->无效 
  如果 (错误!=  ){ 
  completeHandler(NCUpdateResult.failed) 
  } 其他 { 
   { 
   jsonResult = 试试 JSONSerialization.jsonObject(with:data,options:[]) 
   formatter = NumberFormatter() 
  自我 .data.removeAll() 
  如果  dictionary = jsonResult as ?  [String: Any ] { 
  如果  nestedDictionary = dictionary [“ USD”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
  dictUSD:[String:String] = [“ title”:“ USD”,“ value”:值] 
  自我 .data.append(dictUSD 作为 NSDictionary) 
  } 
  如果  nestedDictionary = dictionary [“ INR”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
  let dictINR:[String:String] = [“ title”:“ INR”,“ value”:值] 
  自我 .data.append(dictINR 作为 NSDictionary) 
  } 
  如果  nestedDictionary = dictionary [“ THB”]  ?  [String: Any ] { 
  值= formatter.string(来自:nestedDictionary [“ last”] as !NSNumber)?  ” 
   dictTHB:[String:String] = [“ title”:“ THB”,“ value”:值] 
  自我 .data.append(dictTHB 作为 NSDictionary) 
  } 
  DispatchQueue.main.async { 
  自我 .tableView.reloadData() 
  completeHandler(NCUpdateResult.newData) 
  } 
  } 
  }  let错误捕获  NSError { 
 打印(错误) 
  } 
  } 
  }) 
  } 
  //此函数根据数据返回表格视图的总行数 
  覆盖 func tableView( _ tableView:UITableView,numberOfRowsInSection部分:Int)-> Int { 
  返回 data.count 
  } 
  //此函数设置表格视图的单元格元素(标题,值) 
  覆盖 func tableView( _ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell { 
   cell = tableView.dequeueReusableCell(withIdentifier:“ WidgetItem”,for:indexPath)  !  WidgetItemTableViewCell 
   item = data [indexPath.row] 
  cell.widgetItemTitle.text = item [“ title”]  ? 串 
  cell.widgetItemValue.text = item [“ value”]  ? 串 
  返回单元 
  } 
  } 

现在我们完成了小部件的所有设置和编码。 运行项目后,您应该会看到如下输出:

您可以从 GitHub 下载该项目

注意 :即使我写了本教程来演示如何为响应本机项目创建iOS扩展,此方法也适用于本机iOS应用。 我对本机iOS开发不是很熟悉,所以请不要将此处编写的代码作为最佳实践。 您的意见,建议将始终受到欢迎。

我发现了这个库@matejkr,它可以帮助您创建没有iOS扩展的iOS扩展。 值得尝试。

matejkriz / react-native-today-widget
React Native中的iOS Today小部件。 通过创建帐户为matejkriz / react-native-today-widget开发做出贡献…… github.com

快乐的编码…