如何在React-Native应用程序中添加iOS Today扩展或小部件
我们或多或少都熟悉适用于iOS的精美应用程序扩展。 例如天气扩展,它以一种非常漂亮的方式为我们提供了天气更新。 当您想要快速浏览重要人物时,有时应用程序扩展或窗口小部件非常方便。 在本教程中,我们将介绍如何在React-Native应用程序中添加iOS Today应用程序扩展。
我们将通过本教程逐步实现以下目标:
- 创建基本的应用扩展程序或小部件(步骤1-11)
- 具有表格视图布局的小部件(步骤12–18)
- 一个小部件将通过区块链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个标签项Title和Value 。 再次打开小部件情节提要文件。 点击“表格视图单元格”。 在“ 检查器”视图的右侧,单击“身份检查器”。 从“类”下拉列表中,选择我们新创建的类文件“ 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(动画)
如果 #available ( iOSApplicationExtension 10.0,*){
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
}
自我 .preferredContentSize.height = 200
}
func widgetMarginInsets(forProposedMarginInsets defaultMarginInsets:UIEdgeInsets)->(UIEdgeInsets){
返回 UIEdgeInsets.zero
}
@available ( iOSApplicationExtension 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(动画)
如果 #available ( iOSApplicationExtension 10.0,*){
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
}
自我 .preferredContentSize.height = 200
}
func widgetMarginInsets(forProposedMarginInsets defaultMarginInsets:UIEdgeInsets)->(UIEdgeInsets){
返回 UIEdgeInsets.zero
}
@available ( iOSApplicationExtension 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
快乐的编码…