【翻译】 iOS 8 App Extension: Today Widget

前言:闲在小卖部混日子一段时间,就学习Swift,学学iOS8,碰巧遇上那篇文章,二个上午做下来发现很易懂,就摸索翻译并享受出去。顺便求推荐苏黎世的iOS工作~~~~

点那里看原稿
点那里看 onevcat
大神的学科

以下正文,说美素佳儿下,并不是逐字翻译,同时代风尚程与内容有涂改。

  • 一旦看官太懒了直白看形成结果,能够点这里下载实现后的工程,存放在dropbox,记得自备VPN。
  • 与此同时那篇教程是swift
    1.0的时候写的,打开项目后会报语法错误,按提醒修改就能够。

Step 0:

在iOS
第88中学,苹果公司生产的应用程序扩大Extension技术,可以让您的APP扩张原有效力到其余界面(通告栏,分享拦),或让不一样APP间能够进行多少交互,同时不会相差当前页面。
iOS 8系统有陆个协理增加的系统区域,分别是Today、Share、Action、Photo
艾德iting、Storage Provider、Custom
keyboard。帮助扩充的系统区域也被称呼扩张点。
上边是在IOS的延伸点的列表:

  • Today –
    对于赛事比分,股票、天气、快递那类供给实时获取同时又简约的音讯,能够在通报宗旨的Today视图中创建叁个Today扩充实现。Today扩充又称作Widget。

  • Share –
    在系统的享用拦中创设自定义分享,能够享用内容,分享网站或宣布新闻。

  • Action –
    action在有着扶助的扩张点中增添性最强的一个。它能够兑现转移另一个app上下文中的始末。苹果在WWDC大会上演示了3个Bing翻译动作扩大,它可以将在Safari中选中的公文翻译成差异的语言。

  • Photo 艾德iting – 在iOS
    8从前,假诺你想为你的肖像添加五个独特的滤镜,你必要进入第2方app中,那么些进度是一定麻烦的。在iOS
    第88中学,你能够一向在Photos中行使第③方app,如推特,VSCO
    cam、Aviary提供的Photo
    艾德iting扩大实现对图纸的编纂,而无需离开当前的app。

  • Document Provider – 也叫Storage
    Provider,能够让跨多少个公文存款和储蓄服务时期的治本变得更简便。类似Dropbox、GoogleDrive等储存提供商通过在iOS 第88中学提供2个Storage
    Provider扩大,app直接能够应用那么些扩大检索和存款和储蓄文件而不再供给创造不须求的正片。

  • Custom Keyboard – 自定义键盘输入法能够让用户在全路体系范围内接纳。

而那篇小说,将汇总授课 Today Extention 部分。

必发bifa88手机客服端 1

Extention是怎么工作的?

在我们初阶应用Today
Extensions在此以前,先让大家看看Extensions的部分定义,以便领悟之后的学科。

  • 首先,Extensions不是独自的应用程序。Extensions必须依靠于三个host
    app,下边一律叫作宿主APP,同时他们的生命周期相互独立,并且能够并行接触事件。贰个宿主APP能够分包八个Extensions。

  • 当1个Extensions运维的时候,宿主APP不会同时运转。每种Extensions都有和好的独立进程(相当于二个小的APP)。同时,能够八个Extensions同时运维,互相间不受影响。每一次运营Extensions都是再一次启航的(重复的利用相同Extensions并不会利用相同块内部存储器)。

  • Extensions与宿主APP间不能直接通信,但能够品味通过OpenU奥德赛L()来打开宿主APP或行使NSUserDefaults存款和储蓄和读取通用的数量。

The Today Extension

好了,未来大家准备开创叁个widget,为了节省不须要的最初准备,
这里能够下载到原有状态的工程,记得使用VPN和修改swift
1.2 导致的语法错误。
必发bifa88手机客服端,本条原始工程是一个简短的天气数据展现应用,so……它是亟需互连网连接和2个天候数据得到的API。
而我辈的天气数据将从
forecast.io那些网站取得,它提供天天一千条免费查询,所以在起来写代码前,最好先去注册并得到属于您的APIkey。
Oh,还有还有,要博得天气就需求所在城市的地理地方音信,所以大家还索要假若CoreLocation来赢得地理消息。(原文略过了那步,并提供了一个教程
,但因为太不难所以本人在此处就增进了 (.) )

Step 1:

一切准备好后,大家运营工程后应当会看到下边包车型大巴界面:

必发bifa88手机客服端 2

那些就是宿主APP今后的意况了,城市名跟坐标写死了 (=.=)。
然后,开头首先步咯!

Embedded Frameworks and Code Reuse 嵌入式框架和代码重用

Extensions
的生命周期是单独于宿主APP的,同时Extensions的效率应是宿主的一对效能的延长,Extensions与宿主应有相同的遵从部分,即它们将会选择相似甚至同一的代码与逻辑。
据此,制作Extensions的首先步,便是把通用的代码块放到三个宿主APP与Extensions都能“获得”的地方。
iOS 8的自制Framwork就足以帮到你!

以下是手续:

  1. 在Project
    Navigator中,点击project,然后在产出的面板左下方点击”+”按钮(或选拔Editor,然后AddTarget)。
![](https://upload-images.jianshu.io/upload_images/6775-d0667493f8973805.png)

这里是已经完成了状态
  1. 在开辟的面板中,选拔 iOS > Framework & Library > Cocoa Touch
    Framework,然后您会看到:
![](http://www.appcoda.com/wp-content/uploads/2014/10/weather_data_kit.png)
App Extension - Weather Data Kit



名字叫 WeatherDataKit 不要搞错了,其他按项目填,然后点一下 Finish。

实现地点的步骤后,你会发现三个二个叫WeatherDataKit的folder和target。展开WeatherDataKit,会面到唯有一个WeatherDataKit.h 在那里,那些 .h 文件的是在您的种类还要选择 Obj-C 和
Swift的时候使用的,效能是桥接Obj-C的头文件,当然这么些连串里并非做其余操作。

在这边大家要留意,Extension并不是力所能及统统帮助全部Cocoa Touch
APIs的,比如上边的几点:

  • 语音输入和录制头调用
  • 从AirDrop接收数据(但可以发送数据)
  • 常驻后台(Extensions的可用内存是很低的,同时系统内部存款和储蓄器紧张的时候也首先拿Extensions开刀)
  • 不能够利用其它带有 NS_EXTENSION_UNAVAILABLE 或类似宏定义 的类, 或
    伊芙ntKit 和 HealthKit 那种不帮助Extensions的 framework
  • Access a sharedApplication object, and so cannot use any of the
    methods on that object(不知道那句)

当今我们编译的时候假设使用上述的API将会遇上有个别警告,能够经过勾选”Allow
app extension API
only”来就足以制止那一个警告。(这里有个列表能够参考哪些不能够用在Extension上)

必发bifa88手机客服端 3

纪念勾上

好了,接下去要做的很简短,把原本在 Weather folder 中的 “Weather Data”
移动到 WeatherData基特 folder
中,记得去改被移动文件的target(四个都要)。

必发bifa88手机客服端 4

App Extension – Weather Group

必发bifa88手机客服端 5

正是此处

Extra Part!

还记得上边提过,要博得天气音讯,首先要赢获得地理地方消息。原科目是用固定坐标的,那里吧,大家就融洽写二个。
可能在 WeatherDataKit folder,新建三个叫 WeatherLocation 的 swift
文件。然后,在 WeatherData基特 的 target 里添加 CoreLocation framework。

必发bifa88手机客服端 6

丰裕必要的 framework

尔后就能够写代码啦:

  1. 在 WeatherLocation.swift 里,注解 WeatherLocation 的 class,它一连与
    NSObject,并且会有 CoreLocation 的 delegate(记得先 import
    CoreLocation)。
  2. 接下来表明 CLLocationManager 对象
    ‘manager’,它能够扶助大家一向同时获得到详细的地理新闻。
  3. 随之补上 CLLocationManagerDelegate 的格局:
  • locationManager(manager: CLLocationManager!, didUpdateLocations
    locations: [AnyObject]!)
  • locationManager(manager: CLLocationManager!, didFailWithError error:
    NSError!)
    它们叁个担负更新当前的地理地方音讯,一个担当告诉您出错的内容。
  1. 终极在 init() 方法里让 manager 运维。(记得补上 manager 的装置和钦赐delegate 对象)

有那么不难吗?
本来没有。Apple对于用户的心曲格外珍视,像地理地方这种时刻出卖你的信息,当然要获取用户许可才能选取!
那么我们先回到 Weather 这一个target,找到 info 面板(只怕在 Weather folder
下找到 info.plist)。
接下来添加八个值:(在iOS 7
或事先只要求2个,若是急需协理旧版本则需求做系统版本判断来做分化处理)

  • NSLocationAlwaysUsageDescription
  • NSLocationWhenInUseUsageDescription
    必发bifa88手机客服端 7
    以 info.plist 为例
类型记得要为 String
,内容根据自己需要填。补充完这两项后,当项目启动并在第一次使用地理位置信息的时候,系统就会自动弹出
Alert 要求获取授权,这是上面你填的信息就会显示在这个 Alert
里。(关于这两个 String
的使用场景,可以参考[Develop](https://link.jianshu.com?t=https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html))

好了,接下去贴一下代码:

import Foundation
import CoreLocation

class WeatherLocation : NSObject, CLLocationManagerDelegate {

typealias WeatherLocationCompletionBlock = (latLong: String?, cityName: String?, error: NSError?) -> ()

let manager = CLLocationManager()
var latAndLong : String?
var city : String?
var block : WeatherLocationCompletionBlock?

class var sharedInstance: WeatherLocation {
    struct Singleton {
        static let instance = WeatherLocation()
    }
    return Singleton.instance
}

override init() {

    super.init()

    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.requestAlwaysAuthorization()
}

 // MARK: 看这里看这里
func updateLocation(completion: WeatherLocationCompletionBlock) {
    if CLLocationManager.locationServicesEnabled() {
        manager.startUpdatingLocation()
        block = completion
    }
}

// MARK: CoreLocation
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {

    manager.stopUpdatingLocation()

    var location:CLLocation = locations[0] as! CLLocation
    latAndLong = "\(location.coordinate.latitude),\(location.coordinate.longitude)"

    CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
        if (error != nil) {
            self.block!(latLong: "", cityName: "", error: error)
            println("Reverse geocoder failed with error" + error.localizedDescription)
        } else {
            if placemarks.count > 0 {
                let pm:CLPlacemark = placemarks[0] as! CLPlacemark
                if (pm.locality != nil) {
                    self.city = pm.locality
                    self.block!(latLong: self.latAndLong, cityName: self.city, error: nil)
                }
            } else {
                var otherError : NSError?
                otherError = NSError(domain: "Problem with the data received from geocoder", code: -9999, userInfo: nil)
                self.block!(latLong: "", cityName: "", error: otherError)
                println("Problem with the data received from geocoder")
            }
        }
    })
}

    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
        println("\(error)")
    }
}

好了,大家应该发现,除了在地点列出必需求落到实处的方法和目的之外,笔者还添加了一个新的法子
updateLocation(completion:blockName) 和1个 block
,它的意义是询问地理地点并且通过 block 的格局再次来到结果给调用者。
亟需专注的是 updateLocation
方法并不谨慎,它并没有处理结果的景观重返给调用者,这在实际开发准将会遇上标题(比如你不可能担保用户机器能够每便都询问到结果)。

下一场,在 WeatherDataViewController.swift 文件中添加叁个办法

func loadLocation(completionLocdLocation:(city: NSString?)->()) {
    WeatherLocation.sharedInstance.updateLocation { (latLong, cityName, error) -> () in
        weak var weakSelf = self as WeatherDataViewController
    WeatherLocation.sharedInstance.updateLocation { (latLong, cityName, error) -> () in
        WeatherService.sharedInstance.fetchWeatherData(latLong!, completion: { (data, error) -> () in
            dispatch_async(dispatch_get_main_queue()) {
                weakSelf?.weatherData = data
                weakSelf?.updateData()
                completionLocdLocation(city: cityName)
            }
        })
    }
}

loadLocation
方法里第壹查询地理地方,获得结果后查询天气新闻,获得气象详情后更新数据并把当前都会称号再次回到给调用者,因为嵌套了
block 的关系,记得使用 weak 来啊 self 变成弱引用,幸免 retain cycle
难点。

最后,我们在 ViewController.swift 里测试一下,import WeatherDataKit(WeatherDataViewController 的 target 改变了,必要从外表引入,记得加上
framework),在 viewDidAppear 方法里增进:

weak var weakSelf = self
loadLocation { (city) -> () in
        weakSelf?.locationLabel.text = city as? String
}

运作,等 API 重临结果,UI
更新!(假若模拟器上冒出错误,记得调整模拟器地理地点的选项,大概钦赐贰个地理地点)

必发bifa88手机客服端 8

呵呵呵呵

Step 2:

Creating the Widget

主演登场~~~继续累加 target,然后命名为 Weather Widget 。

必发bifa88手机客服端 9

正是其一

成立实现后,大家会发现多了八个 Weather Widget 的 floder 和多了三个Scheme:

必发bifa88手机客服端 10

连logo都不同

目前我们来探望贰个新创制的 Today widget 会有怎么着事物:

  • TodayViewController.swift
  • MainInterface.storyboard

跟一般的 ViewController 没什区别嘛~~~~但其实 TodayViewController
里已经添加了 NCWidgetProviding 这一个protocol,乖乖的按必要补充完就好。还有久违的 didReceiveMemoryWarning
方法,看来 widget 是个时刻惨死的命,大家想做的花俏的话记得做懒加载和在
didReceiveMemoryWarning 里做释放,不然因为 widget 体验不佳就把 APP
删掉就得不尝试了。

试跳运维一下,弹出来的面板直接选 Today 就能够了,然后会看到上面包车型的士效用:

必发bifa88手机客服端 11

它早已帮您写好 Hello World 了

因为 Extension 和 宿主APP 都要求运用同样的代码,所以它同样要添加
framework:

必发bifa88手机客服端 12

App Extension – 添加 Framework

添加完后就足以在 TodayViewController 里继承的 ViewController 改为
WeatherDataViewController ,那样就能够动用 WeatherDataViewController
的章程了。
接下去,大家要修改 MainInterface.storyboard 里的UI,使 widget
看到的新闻可见跟 宿主APP 的平等。

必发bifa88手机客服端 13

把原本的 Hello World 界面改成那样

根据 Apple Extensions
Guide

的建议,widget最好是相比小的,对于详细音信要有个可适配的惊人去显得或隐蔽。
所以高粱红字体的一对会依照用户的必要来彰显会隐藏,这一部分在 stroyboard
里被1个晶莹剔透的 view 所涵盖,大家把它定义为 MoreDetailsContainer
,等下会用到。
然后回到 TodayViewController ……

非符合规律,漏了一步,大家在后续写代码前,要求对 storyboard 或 xib 里的 UI
做适配。大家那么些类型只帮忙 iOS 8,所以能够放心的应用 AutoLayout 跟
SizeClass。

  1. 点开 MainInterface.storyboard ,点击
    MoreDetailsContainer,我们须求它维持一定的冲天并且连续在底部的岗位,大家得以如此设置:
![](https://upload-images.jianshu.io/upload_images/6775-f5bedeace434ac47.png)

点点这里



![](http://www.appcoda.com/wp-content/uploads/2014/10/container_view_constraints.png)
注意不要勾选 Contrain to margins
  1. 选择 Cupertino, CA
![](http://www.appcoda.com/wp-content/uploads/2014/10/cupertino_constraints.png)
改成这样
  1. 选择 100
![](http://www.appcoda.com/wp-content/uploads/2014/10/temperature_constraint.png)
改成这样
  1. 选取小箭头按钮
![](http://www.appcoda.com/wp-content/uploads/2014/10/caret_constraints.png)
改成这样
  1. 对于 Mostly Cloudy 那种不显著长度的
    label,大家须要让它自动依照文字改变长度,同时要跟 Summy 对齐。
    右键按住 Summy ,然后拖动到 Mostly Cloudy,选拔 Horizontal Spacing。
![](http://www.appcoda.com/wp-content/uploads/2014/10/modify_option_a.png)
这样配置



或者在 Mostly Coudy 的 constraint 面板中,找到 Leading Space to
SUMMARY 的 contraint ,修改为 20。



![](http://www.appcoda.com/wp-content/uploads/2014/10/modify_option_b.png)
modify\_option\_b
  1. 其余一些参考第4步。

好,运转一下。

必发bifa88手机客服端 14

那是假数据

兴许你会发现右边会有一段空白空间,大家得以用下边包车型大巴代码把它填满:

func widgetMarginInsetsForProposedMarginInsets
    (defaultMarginInsets: UIEdgeInsets) -> (UIEdgeInsets) {
    return UIEdgeInsetsZero
}

实际成效要好试试看。

接着大家要实例化 UI 上边的要素:

  1. 回到 TodayViewController,添加一个按钮和按钮方法:
    @IBOutlet weak var showMoreButton: UIButton!
    @IBAction func showMore(sender: UIButton) {
    }
  2. 丰硕三个可变的 contraint:
    @IBOutlet weak var moreDetailsContainerHeightConstraint :
    NSLayoutConstraint!
  3. 末了定义一个 BOOL 变量,标记 moreDetailsContainer 是还是不是出示:
    var widgetExpanded = false

概念完事后,起首写相关逻辑:

  1. Widget 初叶化的时候是不曾数据的,所以一起始 moreDetailsContainer
    应该是东躲山西的情况:
    moreDetailsContainerHeightConstraint.constant = 0

  2. 在 showMore 方法里添加代码:
    @IBAction func showMore(sender: UIButton) {
    if widgetExpanded {
    moreDetailsContainerHeightConstraint.constant = 0
    showMoreButton.transform = CGAffineTransformMakeRotation(0)
    widgetExpanded = false
    } else {
    moreDetailsContainerHeightConstraint.constant = 220
    showMoreButton.transform =
    CGAffineTransformMakeRotation(CGFloat(180.0 * M_PI/180.0))
    widgetExpanded = true
    }
    }
    记得在 sotryboar 里把小箭头按钮跟代码连接起来。

  3. 最终在 ViewDidLoad 方法里增进:
    temperatureLabel.text = “–“
    summaryLabel.text = “–“
    timeLabel.text = “–“
    humidityLabel.text = “–“
    precipitationLabel.text = “–“

     loadLocation { (city) -> () in
         self.locationLabel.text = city as? String
     }
    

如此widget运行的时候就会自动获取音信。

运行吧!

必发bifa88手机客服端 15

原谅这些箭头

后续

首先次翻译还有写教程呢(基本上就在胡说了),没悟出要考虑的事物依然挺多的。
那篇教程最终还有跟 宿主APP 通过 UserDefault
来举行多少交互的一些,但是它都以用写死的数目,那里就不摆了,看看后续能否足够更实用的情势来做。

万分谢谢能旁观最终。

相关文章