WWDC15 Session笔记 - 30 分钟开发一个简单的 watchOS 2 app

本文详细介绍了如何使用 watchOS 2 的新特性,从零开始构建一个天气应用程序,涵盖了项目初始化、UI 设计、获取天气数据、实现 Watch App、添加并发症以及使用 Watch Connectivity 的过程。通过实例演示了如何在手表端直接进行计算,展示了 watchOS 2 的强大功能和开发技巧。
摘要由CSDN通过智能技术生成

Apple Watch 和 watchOS 第一代产品只允许用户在 iPhone 设备上进行计算,然后将结果传输到手表上进行显示。在这个框架下,手表充当的功能在很大程度上只是手机的另一块小一些的显示器。而在 watchOS 2 中,Apple 开放了在手表端直接进行计算的能力,一些之前无法完成的 app 现在也可以进行构建了。本文将通过一个很简单的天气 app 的例子,讲解一下 watchOS 2 中新引入的一些特性的使用方法。

本文是我的 WWDC15 笔记中的一篇,在 WWDC15 中涉及到 watchOS 2 的相关内容的 session 非常多,本文所参考的有:

项目简介

作为一个示例项目,我们就来构建一个最简单的天气 app 吧。本文将一步步带你从零开始构建一个相对完整的 iOS + watch app。这个 app 的 iOS 端很简单,从数据源取到数据,然后解析成天气的 model 后,通过一个 PageViewController 显示出来。为了让 demo 更有说服力,我们将展示当前日期以及前后两天的天气情况,包括天气状况和气温。在手表端,我们希望构建一个类似的 app,可以展示这几天的天气情况。另外我们当然也介绍如何利用 watchOS 2 的一些新特性,比如 complications 和 Time Travel 等等。

开始

虽然本文的重点是 watchOS,但是为了完整性,我们还是从开头开始来构建这个 app 吧。因为不管是 watchOS 1 还是 2,一个手表 app 都是无法脱离手机 app 单独存在和申请的。所以我们首先来做的是一个像模像样的 iOS app 吧。

新建项目

第一步当然是使用 Xcode 7 新建一个工程,这里我们直接选择 iOS App with WatchKit App,这样 Xcode 将直接帮助我们建立一个带有 watchOS app 的 iOS 应用。

step-1

在接下来的画面中,我们选中 Include Complication 选项,因为我们希望制作一个包含有 Complication 的 watch app。

step-2

UI

这个 app 的 UI 部分比较简单,我将使用到的素材都放到了这里。你可以下载这些素材,并把它们解压并拖拽到项目 iOS app 的 Assets.xcassets 里去:

step-3

接下来,我们来构建 UI 部分。我们想要使用 PageViewController 来作为 app 的导航,首先,在 Main.StoryBoard 中删掉原来的 ViewController,并新加一个 Page View Controller,然后在它的 Attributes Inspector 中将 Transition Style 改为 Scroll,并勾选上 Is Initial View Controller。这将使这个 view controller 成为整个 app 的入口。

step-4

接下来,我们需要将这个 Page View Controller 和代码关联起来。首先将 ViewController.swift 文件中,将 ViewController 的继承关系从 UIViewController 改为 UIPageViewController

class ViewController: UIPageViewController {  
    ...
}

然后我们就可以在 StoryBoard 文件中将刚才的 Page View Controller 的 class 改为我们的 ViewController 了。

step-5

另外我们还需要一个实际展示天气的 View Controller。创建一个继承自 UIViewController 的WeatherViewController,然后将 WeatherViewController.swift 的内容替换为:

import UIKit

class WeatherViewController: UIViewController {

    enum Day: Int {
        case DayBeforeYesterday = -2
        case Yesterday
        case Today
        case Tomorrow
        case DayAfterTomorrow
    }

    var day: Day?
}

这里仅只是定义了一个 Day 的枚举,它将用来标记这个 WeatherViewController 所代表的日期 (可能你会说把 Day在 ViewController 里并不是很好的选择,没错,但是放在这里有助于我们快速搭建 app,在之后我们会对此进行重构)。接下来,我们在 StoryBoard 中添加一个 ViewController,并将它的 class 改为 WeatherViewController。我们可以在这里构建 UI,对于这个 demo 来说,一个简单的背景,加上表示天气的图标和表示温度的标签就足够了。因为这里并不是一个关于 Auto Layout 或是 Size Class 的 demo,所以就不详细一步步地做了,我随意拖了拖 UI 和约束,最后结果如下图所示。

step-6

接下来就是从 StoryBoard 中把需要的 IBOutlet 拖出来。我们需要天气图标,最高最低温度的 label。完成这些 UI 工作之后的项目可以在 GitHub 的这个 tag 下找到,如果你不想自己完成这些步骤的话,也可以直接使用这个 tag 的源文件来继续下面的 demo。当然,如果你对 AutoLayout 和 Interface Builder 还不熟悉的话,这会是一个很好的机会来从简单的布局入手,去理解对 IB 的使用。关于更多 IB 和 StoryBoard 的教程,推荐 Raywenderlich 的这两篇系列文章:Storyboards Tutorial in Swift 和 Auto Layout Tutoria

然后我们可以考虑先把 Page View Controller 的框架实现出来。在 ViewController.swift 中,我们首先在ViewController 类中加入以下方法:

func weatherViewControllerForDay(day: WeatherViewController.Day) -> UIViewController {

    let vc = storyboard?.instantiateViewControllerWithIdentifier("WeatherViewController") as! WeatherViewController
    let nav = UINavigationController(rootViewController: vc)
    vc.day = day

    return nav
}

这将从当前的 StroyBoard 里寻找 id 为 "WeatherViewController" 的 ViewController,并且初始化它。我们希望能为每一天的天气显示一个 title,一个比较理想的做法就是直接将我们的 WeatherViewController 嵌套在 navigation controller 里,这样我们就可以直接使用 navigation bar 来显示标题,而不用去操心它的布局了。我们刚才并没有为WeatherViewController 指定 id,在 StoryBoard 中,找到 WeatherViewController,然后在 Identity 里添加即可:

step-7

接下来我们来实现 UIPageViewControllerDataSource。在 ViewController.swift 的 viewDidLoad 里加入:

dataSource = self  
let vc = weatherViewControllerForDay(.Today)  
setViewControllers([vc], direction: .Forward, animated: true, completion: nil)  

首先它将 viewController 自己设置为 dataSource。然后设定了初始需要表示的 viewController。对于UIPageViewControllerDataSource 的实现,我们在同一文件中加入一个 ViewController 的 extension 来搞定:

extension ViewController: UIPageViewControllerDataSource {  
    func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
        guard let nav = viewController as? UINavigationController,
                  viewController = nav.viewControllers.first as? WeatherViewController,
                  day = viewController.day else {
            return nil
        }

        if day == .DayBeforeYesterday {
            return nil
        }

        guard let earlierDay = WeatherViewController.Day(rawValue: day.rawValue - 1) else {
            return nil
        }

        return self.weatherViewControllerForDay(earlierDay)
    }

    func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
        guard let nav = viewController as? UINavigationController,
            viewController = nav.viewControllers.first as? WeatherViewController,
            day = viewController.day else {
                return nil
        }

        if day == .DayAfterTomorrow {
            return nil
        }

        guard let laterDay = WeatherViewController.Day(rawValue: day.rawValue + 1) else {
            return nil
        }

        return self.weatherViewControllerForDay(laterDay)
    }
}

这两个方法分别根据输入的 View Controller 对象来确定前一个和后一个 View Controller,如果返回 nil 则说明没有之前/后的页面了。另外,我们可能还想要先将 title 显示出来,以确定现在的架构是否正确工作。在WeatherViewController.swift 的 Day 枚举里添加如下属性:

var title: String {  
            let result: String
            switch self {
            case .DayBeforeYesterday: result = "前天"
            case .Yesterday: result = "昨天"
            case .Today: result = "今天"
            case .Tomorrow: result = "明天"
            case .DayAfterTomorrow: result = "后天"
            }
            return result
        }

然后将 day 属性改为:

var day: Day? {  
    didSet {
        title = day?.title
    }
}

运行 app,现在我们应该可以在五个页面之间进行切换了。你也可以从 GitHub 上对应的 tag 中下载到目前为止的项目。

step-8

重构和 Model

很难有人一次性就把代码写得完美无瑕,这也是重构的意义。重构从来不是一个“等待项目完成后再开始”的活动,而是应该随着项目的展开和进行,一旦发现有可能存在问题的地方,就尽快进行改进。比如在上面我们将 Day 放在了WeatherViewController 中,这显然不是一个很好地选择。这个枚举更接近于 Model 层的东西而非控制层,我们应该将它迁移到另外的地方。同样现在还需要实现的还有天气的 Model,即表征天气状况和高低温度的对象。我们将这些内容提取出来,放到一个 framework 中去,以便使用的维护。

step-9

我们首先对现有的 Day 进行迁移。创建一个新的 Cocoa Touch Framework target,命名为 WatchWeatherKit。在这个 target 中新建 Day.swift 文件,其中内容为:

public enum Day: Int {  
    case DayBeforeYesterday = -2
    case Yesterday
    case Today
    case Tomorrow
    case DayAfterTomorrow

    public var title: String {
        let result: String
        switch self {
        case .DayBeforeYesterday: result = "前天"
        case .Yesterday: result = "昨天"
        case .Today: result = "今天"
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值