使用 Swift 制作一个新闻通知中心插件(1)

随着 iOS 8 的发布,苹果为开发者们开放了很多新的 API,而在这些开放的接口中 通知中心插件 无疑是最显眼的一个。通知中心就不用过多介绍了,相信大家对这个都很清楚了。在以往的 iOS 版本中,我们只能使用 iOS 系统自带的有限的几个 通知中心组件。 这次新开放的这个功能,就相当于为大家提供了一个全新的市场。相信通过大家的智慧创造,一定会出现很多非常流行的应用。

其他就不多说了,现在我们来以一个新闻插件作为例子,来为大家介绍如何来创建一个 通知中心 插件。我们做好之后,大概就是这个样子:

<!-- more -->

准备工作

我们要开发的是一个简单的新闻插件,那么这就需要一个数据源。 我们这里使用 BBC News 的 RSS 订阅接口来作为新闻数据的来源。

http://feeds.bbci.co.uk/news/rss.xml

这是一个标准的 RSS 2.0 接口,它用 XML 格式返回新闻的数据。如果对 RSS 不了解的话可以参看这篇关于它的介绍 http://en.wikipedia.org/wiki/RSS 。

打开这个 RSS 接口后,我们会看到类似这样的 XML 数据:

<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"\>
<channel>
<title>BBC News - Home</title>
<link>http://www.bbc.co.uk/news/#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
<description>The latest stories from the Home section of the BBC News web site.</description>
<language>en-gb</language>
<lastBuildDate>Tue, 30 Dec 2014 23:52:29 GMT</lastBuildDate>
<copyright>Copyright: (C) British Broadcasting Corporation, see http://news.bbc.co.uk/2/hi/help/rss/4498287.stm for terms and conditions of reuse.</copyright>
<image>
<url>http://news.bbcimg.co.uk/nol/shared/img/bbc_news_120x60.gif</url>
<title>BBC News - Home</title>
<link>http://www.bbc.co.uk/news/#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
<width>120</width>
<height>60</height>
</image>
<ttl>15</ttl>
<atom:link href="http://feeds.bbci.co.uk/news/rss.xml" rel="self" type="application/rss+xml"/>
<item>
<title>Poppy duo and acting stars honoured</title>
<description>The creators of the World War One ceramic poppy display at the Tower of London join acting grandees Joan Collins and John Hurt on the New Year Honours list.</description>
<link>http://www.bbc.co.uk/news/uk-30633687#sa-ns_mchannel=rss&amp;ns_source=PublicRSS20-sa</link>
<guid isPermaLink="false">http://www.bbc.co.uk/news/uk-30633687</guid>
<pubDate>Tue, 30 Dec 2014 23:31:59 GMT</pubDate>
<media:thumbnail width="66" height="49" url="http://news.bbcimg.co.uk/media/images/79989000/jpg/_79989926_honours2_composite.jpg"/>
<media:thumbnail width="144" height="81" url="http://news.bbcimg.co.uk/media/images/79989000/jpg/_79989927_honours2_composite.jpg"/>
</item>
</channel>
</rss>

注意一下 <item>节点,这个节点里面包含了我们每条新闻的具体数据,比如新闻标题,描述,发布日期等等。

有了这个数据源,我们就可以开始专注于代码的编写啦。首先,我们要创建一个新项目。打开 Xcode,然后进入 File > New Project... > Single View Application.

然后,再接下来的界面中,填写项目的信息:

ProductName: rsswidget Orgnaization Name: theswiftworld Orgnaization Identifier: com.theswiftworld Language: Swift Devices: iPhone

都填写好后,点击 Next 按钮。 在接下来的界面中,选择一个合适的位置来存放项目文件。然后点击 Create 按钮来创建项目。这样,我们的基础项目就创建好了。

到现在位置,大家可能会发现,我们创建的还是一个普通的 App 项目。因为任何 App Extension 都是需要在一个宿主应用上运行的。比如我们现在在制作的新闻扩展插件,也是需要通过一个原生的 App 来安装到设备上。

那么接下来就是开始创建 App Extension 的过程了,

打开项目的设置页面,点击左下角的加号按钮。

在弹出的窗口中,选择 Application Extension 分类,然后选择该分类下的 Today Extension 选项,然后点击 Next 按钮。

在弹出的 Extension 详细信息窗口中,填写好创建信息:

Product Name: extension Organization Name: theswiftworld

到这里,我们的 Extension 就创建完成了,现在可以在模拟器中运行它,来看到最终的效果了。注意将运行的 Target 选择到 extension。

然后再宿主App中选择 Today 应用:

选择好后,我们就可以在模拟器中,看到我们自己的 App Extension 运行在通知中心里了。 一个 Hello World 显示在通知中心里面。到这里我们关于 Extension 的基础结构搭建就完成了。接下来我们就要考虑如何在我们自定义的 Extension 中显示新闻列表了。

新闻数据源

我们先暂时抛开 Extension 一会儿,现在我们将注意力转移到新闻的数据源中。我们在上面已经介绍了,我们的新闻数据源是以 XML 格式返回给我们的,我们需要用到以下这些第三方库:

Alamofire PKHUD AEXML

下面一一对它们进行介绍:

  • Alamofire

Alamofire 是专门为 Swift 打造的网络操作库,对很多系统方法进行了封装,并提供了方便地异步处理方法和JSON等数据格式的支持。如果你以前用过AFNetworking,就会对这个库更加了解啦,它其实就是 AFNetworking 的作者 Matt Thompson 的另外一个作品。并且 AFNetworking 中前两个字母 AF 其实就是 Alamofire 的字头缩写。

  • PKHUD

PKHUD 是用 Swift 写的一个非常简洁健壮的浮出框组件,提供了多种显示方式,我们这里用到它来作为我们的加载提示框。

  • AEXML

AEXML 是用来解析 XML 数据的 Swift 库,我们这里的新闻数据源是 XML 格式的,所以我们用到它来解析我们收到的 XML 数据。

介绍完需要用到的这些库我们就可以继续我们的教程了。

首先,我们需要引入 Alamofire 库,进入 Alamofire 的首页 https://github.com/Alamofire/Alamofire, 然后在右边的菜单中得最下面点击 Download Zip 按钮把它下载下来。将这个 Zip 包解压后的文件夹放到项目的目录中:

然后将 Alamofire 的项目文件拖动到 Xcode 中的项目结构中。

并且将 Alamofire 设置为 宿主应用和Extension的依赖库。进入 rsswidget 和 extension 的 Build Phrases > Target Dependencies,并将 Alamofire添加为依赖库:

选择 Alamafire 并点击 Add,将库添加进来。

为主应用添加完成后,还要记得给 Extension 也添加一遍哦。

最后再将 Alamofire 添加一遍就完成了。

现在我们将 Alamofire 集成到项目中了,接下来我们来集成 AEXML。

我们进入 AEXML 的主页 https://github.com/tadija/AEXML ,然后点击 Download Zip 将这个库的包下载下来。这个库的集成就非常容易了,只需将解压后包中得 AEXML.swift 文件拖放到 Xcode 项目结构中即可:

怎么样,很简单吧。最后,我们再把 PKHUD 集成进来。

首先,进到 PKHUD 的主页,https://github.com/pkluz/PKHUD, 然后点击 Download Zip 下载 PKHUD 的文件包,然后解压出来,并将整个文件夹放到项目目录中:

然后将项 PKHUD 的项目文件拖动到 XCode 工程结构里面:

这样,我们的集成工作就完成了。

读取数据

现在,我们这个项目所必须得资源库都已经配置好了,接下来我们就来写代码吧!

首先,我们需要一个用于和新闻订阅接口交互的类,我们新建一个实体类 NewsItem:

在 XCode 中打开 File > New > File.. 然后选择 Swift File。点击 Next 按钮:

然后将文件名命名为 NewsItem.swift,并且要注意同时选中主应用和Extension两个Target:

文件创建好后,我们来看看这个类的代码:

import Foundation

class NewsItem {

    var title:String?
var link:String?
var pubDate:String?
var description:String?
var thumbnail:String? init(title:String, link:String,pubDate:String,description:String, thumbnail:String){ self.title = title
self.link = link
self.pubDate = pubDate
self.description = description
self.thumbnail = thumbnail }
}

这个类很简单,就是一个对新闻数据的实体封装,里面定义了5个属性,分辨对应:

  • title 新闻标题
  • link 新闻链接
  • pubDate 新闻发布时间
  • description 新闻描述
  • thumbnail 新闻标题图片

还定义了一个构造方法,使用这5个参数来分别对这几个属性进行初始化,这个代码应该很容易理解吧。这里只有一天需要提醒下大家,就是我们看到每个类属性定义后面都有一个问号 ?,这个是 Swift 中的一个特性,它叫做 Optionals,关于这个特性的介绍,可以参看 浅谈 Swift 中的 Optionals 这篇文章,里面有详细的介绍。

有了实体类后,我们还需要一个方法来读取新闻数据,这里我们还是在这个类中来定义这个读取方法:


class func getNews(completionHandler: (Array<NewsItem>) -> Void) { //数据结构的URL 地址
var url:String = "http://feeds.bbci.co.uk/news/rss.xml" //使用 Alamofire 库来请求网络
Alamofire.request(.GET, url).responseString { (_, _, string, err) in //验证请求结果
if(err != nil){
print(err?.debugDescription)
} var error: NSError? //将新闻结构返回的数据转换为 NSData 类型,并准备进行 XML 解析。
if let xmlData:NSData = string?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true){ if let xmlDoc = AEXMLDocument(xmlData: xmlData, error: &error) { var resultNewsList:Array<NewsItem> = Array() for item in xmlDoc.rootElement["channel"]["item"].all { var newsTitle:String = item["title"].value
var newsLink:String = item["link"].value
var newsPubDate:String = item["pubDate"].value
var newsDescription:String = item["description"].value
var thumbnail:String = item["media:thumbnail"].all[1].attributes["url"] as String var newsItem:NewsItem = NewsItem(title: newsTitle, link: newsLink, pubDate: newsPubDate, description: newsDescription, thumbnail:thumbnail)
resultNewsList.append(newsItem) } completionHandler(resultNewsList) } } } }

我们来解释一下上面这段代码, 首先我们定义了一个类方法:

class func getNews(completionHandler: (Array<NewsItem>) -> Void) {

这个类方法接受一个 completionHandler 回调函数,用于在任务完成的进行回调通知。

在这个方法里面,我们定义了变量 url 作为数据接口的地址。随后我们用 Alamofire 库来发送网络请求:

Alamofire.request(.GET, url) 第一个参数是请求方式,在这里我们用 .GET 来请求。第二个参数是发送请求的 url 地址。随后我们用一个回调responseString { (_, _, string, err) 来接受请求的返回。这个回调方法里面的 string 变量代表这个接口返回的数据。

在回调方法中,我们先将这个 string 变量转换为 NSData,随后交由 AEXML 来处理。

 //将新闻结构返回的数据转换为 NSData 类型,并准备进行 XML 解析。
if let xmlData:NSData = string?.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true){ if let xmlDoc = AEXMLDocument(xmlData: xmlData, error: &error) {

我们看到 AEXMLDocument 的构造方法接收的是 NSData数据,随后它会返回一个 XML 文档对象,我们这里存放在 xmlDoc 变量中。

接下来我们遍历这个 xmlDoc 对象,并将它里面的数据转换成实体类,保存起来:

 var resultNewsList:Array<NewsItem> = Array()

   for item in xmlDoc.rootElement["channel"]["item"].all {

   var newsTitle:String = item["title"].value
var newsLink:String = item["link"].value
var newsPubDate:String = item["pubDate"].value
var newsDescription:String = item["description"].value
var thumbnail:String = item["media:thumbnail"].all[1].attributes["url"] as String var newsItem:NewsItem = NewsItem(title: newsTitle, link: newsLink, pubDate: newsPubDate, description: newsDescription, thumbnail:thumbnail)
resultNewsList.append(newsItem) }

这个转换过程应该很容易理解吧,就不多做解释了。 最后,我们调用作为参数传递进来的回调函数 completionHandler 并将我们封装好的实体类数组传递进去,通知上级代码来处理这个新闻列表(我们后面会用这个回调函数来通知 UITableView 刷新数据)。

completionHandler(resultNewsList)

构造 Extension 的 UI 界面

有了数据源的支持,我们接下来就可以创建我们的前端显示了。

首先,我们将 Extension 自带的 Hello World 标签删除掉,打开 MainInterface.storyboard 文件,然后将 UI 中的 Hello World 删除掉:

然后打开 Extension 中的 TodayViewController.swift 文件。 我们加入两个成员变量的定义:

 var newsListTableView:UITableView?
var newsList:Array<NewsItem>?

这两个变量分别代表用于显示新闻数据的 UITableView 和用来存放数据的数组。 然后我们重写这个类的 viewDidLoad() 方法:

    override func viewDidLoad() {
super.viewDidLoad() self.preferredContentSize = CGSizeMake(0, 263)
self.newsListTableView = UITableView(frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))
self.newsListTableView?.delegate = self
self.newsListTableView?.dataSource = self
self.view.addSubview(self.newsListTableView!) NewsItem.getNews { (newsList) in self.newsList = newsList
let table:UITableView? = self.newsListTableView dispatch_async(dispatch_get_main_queue()){ table!.reloadData() } } }

第一行代码 self.preferredContentSize = CGSizeMake(0, 263) 设置了 Extension 组件在通知中心的高度。

接下来,我们对 UITableView 进行了初始化:

   self.newsListTableView = UITableView(frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))
self.newsListTableView?.delegate = self
self.newsListTableView?.dataSource = self
self.view.addSubview(self.newsListTableView!)

然后用我们前面写的 NewsItem 类的数据读取方法 getNews 来取得新闻数据,并用得到的数据刷新 UITableView 显示。

        NewsItem.getNews { (newsList) in

            self.newsList = newsList
let table:UITableView? = self.newsListTableView dispatch_async(dispatch_get_main_queue()){ table!.reloadData() } }

我们在 getNews 的回调方法中将得到的新闻列表存储到属性中,并刷新了 UITableView 的数据显示。这样 viewDidload 方法就完成了。

接下来我们添加 UITableView 的代理方法和数据源方法:

  1. 用于确定 UITableView 的 Section 数量,我们的新闻列表只需要一个 Section。
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return 1

    }
  1. 用于确定 UITableView 的显示行数,这里有一个判断,由于我们指定了 extension 的高度为 263,这个高度只可以显示 6 条新闻,所以如果我们的数据源多于 6 条新闻,我们也按 6 条来显示:
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        if(self.newsList != nil){

            if(self.newsList!.count > 6){

                return 6

            }else{

                return self.newsList!.count

            }

        }else{
return 0;
} }
  1. 处理 UITableView 的选择状态,这个主要是让用户点击完某条新闻后,清空单元格的选中状态。
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        tableView.deselectRowAtIndexPath(indexPath, animated: false)

    }
  1. 构造 UITableView 的每个单元格,用我们保存的 newsList 里面的实体类的来构造每个单元格。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { var cellIdentifier:String = "cellIdentifier" var cell:UITableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell? if(cell == nil){ cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: cellIdentifier) } cell?.textLabel.textColor = UIColor.lightGrayColor()
cell?.textLabel.text = self.newsList![indexPath.row].title! return cell! }

经过一番折腾,我们的 extension 基本完成了,现在我们可以运行它看看效果了。还是将 Target 设置为 extension,并选择一个合适的模拟器来运行,我们会看到这样的效果:

是不是发现有些问题呢,新闻列表整体向右偏移了一块。这是因为 extension 默认的内容有一个左边距,我们需要设置一下才可以正常显示,所以我们还需要在 TodayViewController 中重写一个方法:

    func widgetMarginInsetsForProposedMarginInsets(defaultMarginInsets: UIEdgeInsets) -> UIEdgeInsets {

        return UIEdgeInsetsZero

    }

这个方法会将 Extension 的边距设置为 0,这样我们的新闻列表就显示正常啦。 再次运行 Extension,我们会在模拟器中看到:

这样我们的新闻列表就算是完成了,到这里我们对 Extension 差不多有了一个初步的认识啦,并且我们自己完成了一个 Extension 的制作。但这个Extension 还需要进一步的完善,比如这个列表的排版还很初级,而且点击这个列表里面的新闻后,没有任何的反应,我们还需要一个新闻内容的显示界面来响应从通知中心的点击,等等。

相信读完这篇后,大家对通知中心扩展插件已经有了一个比较具体的认识啦,我们会在下一部分继续介绍这个通知中心扩展的继续完善过程,让大家对它的运用有更深入的了解,同时,希望大家继续支持.

更多文章请访问: www.theswiftworld.com

Swift 制作一个新闻通知中心插件1的更多相关文章

  1. 使用 Swift 制作一个新闻通知中心插件(1)

    input[type="date"].form-control,.input-group-sm>input[type="date"].input-grou ...

  2. 使用 Swift 制作一个新闻通知中心插件(2)

    我们在第一部分的文章中详细讲解了创建一个通知中心插件的整体过程.我们成功的在通知中心里面显示了新闻列表.但是截止到目前,我们还不能从通知中心的列表中查看新闻的详细内容.在这次的教程中,我们就以上次的教 ...

  3. 手把手制作一个简单的IDEA插件(环境搭建Demo篇)

    新建IDEA插件File --> new --> Project--> Intellij PlatForm Plugin-->Next-->填好项目名OK 编写插件新建工 ...

  4. 制作一个简洁的jquery插件

    原文:http://mp.weixin.qq.com/s?__biz=MzAxMzgwNDU3Mg==&mid=401571467&idx=1&sn=08cb00963e6ef ...

  5. Swift - 制作一个在线流媒体音乐播放器(使用StreamingKit库)

    在之前的文章中,我介绍了如何使用 AVPlayer 制作一个简单的音乐播放器(点击查看1.点击查看2).虽然这个播放器也可以播放网络音频,但其实际上是将音频文件下载到本地后再播放的. 本文演示如何使用 ...

  6. Swift - 制作一个录音机(声音的录制与播放)

    1,技术介绍 (1)AVFoundation.framework框架提供了AVAudioRecorder类.它可以实现录音功能. (2)而使用该框架的AVAudioPlayer类,可以实现声音的播放. ...

  7. 用 Swift 制作一个漂亮的汉堡按钮过渡动画

    汉堡按钮在界面设计中已经是老生常谈了,但是当我在dribbble看到这个漂亮的过渡动画时,我决定试试用代码实现它.   这是 CreativeDash team 的原型图: 你可能已经注意到了,汉堡顶 ...

  8. 【前端】制作一个handlebars的jQuery插件

    (function($) { var compiled = {}; $.fn.handlebars = function($srcNode, data) { // 取出模版内容 var src = $ ...

  9. ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能

    本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...

随机推荐

  1. linux添加到普通用户sudo才干

    在超级用户模式添加到普通用户sudo才干 1. su -(进root用户) 2. chmod u+w /etc/sudoer 3. vim /etc/sudoers 于root ALL=(ALL) A ...

  2. SICP 习题(1.1,1.2,1.3,1.4)解题总结。

    近来在重读SICP,以前读过一次,读了第一二章就没有坚持下去,时间一长就基本忘记了,脑海里什么都不剩,就隐约记得自己曾经读过一本很牛B的书. 这次读希望能够扎实一点,不管能读到哪里,希望可以理解一些东 ...

  3. ContentProvider总结(Android)

    ContentProvider 1.适用场景 1) ContentProvider为存储和读取数据提供了统一的接口 2) 使用ContentProvider,应用程序能够实现数据共享 3) andro ...

  4. testbench中将外部数据引入输出的方法(转载)

    在进行HDL的仿真测试时,除了用较为直观的波形仿真图像以外,通过编写测试文件testbench进行仿真并将仿真结果保存在对应的文件,显得尤为重要.文件的操作主要用到读和写两种操作. 1. 读操作 读操 ...

  5. express: command not found.

    npm install -g express 可是不行.全局模式不行. With the release of Express 4.0.0 it looks like you need to do s ...

  6. hdu 1542 Atlantis 段树区,并寻求,,,尼玛真坑人数据,不要打开一小阵!

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  7. linux安装和配置 mysql、redis 过程中遇到的问题记录(转)

    章节目录 mysql redis linux下部署mysql和redis网上的教程很多,这里记录一下我部署.配置的过程中遇到的一些问题和解决办法. mysql ①安装完成后启动的时候报错 Starti ...

  8. 【超酷超实用】CSS3可滑动跳转的分页插件制作教程

    原文:[超酷超实用]CSS3可滑动跳转的分页插件制作教程 今天我要向大家分享一款很特别的CSS3分页插件,这款分页插件不仅可以点击分页按钮来实现分页,而且可以滑动滑杆来实现任意页面的跳转,看看都非常酷 ...

  9. 第4章 建造者模式(Builder Pattern)

    原文 第4章 建造者模式(Builder Pattern) 定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 实用范围 1 当创建复杂对象 ...

  10. SQL Prompt5 破解版+使用说明 [转]

    SQL脚本越写越多,总是觉得编写效率太过于低下,这和打字速度无关.在我个人编写SQL脚本时,至少会把SQL的格式排列成易于阅读的,因为其他人会阅读到你的SQL,无论是在程序中或是脚本文件中,良好的排版 ...