有限的API
当你使用WatchKit时你会发现其API比UIKit受到更多限制,这事实上是件好事,毕竟我们是在为适合手腕的设备编程。因为控件里没有取值的API,界面元素只能设置值而不可读取。标签文本目前必须在代码里单独设置状态变化跟踪。另一方面你会发现没有prepareForSegue方法,然而有contextForSegueWithIdentifier方法。传给下个视图控制器的context(上下文)必须是AnyObject类型的单一对象,在这里我们可以传数据与代理对象。我(作者)发现一个直观方法去同时发送数据对象与代理。
Context
让我们看看WKInterfaceController怎么获取自身上下文对象的。
1
2
3
4
5
|
class InterfaceController: WKInterfaceController {
override func awakeWithContext(context: AnyObject?) {
super
.awakeWithContext(context)
}
}
|
注意上面的代码里我们在storyboard加载完毕后调用了awakeWithContext方法,以提供AnyObject类型的上下文对象。但看起来不那么理想毕竟对象必须转换为我们需要的类型,而且AnyObject类型而不是Any类型导致了结构体没法使用,必须转化为类实例。
然而我们希望简单的将数据对象作为上下文发送给控制器时,却没道理让数据对象包含一个控制器代理属性。更合适的办法是包装它成为一个既有数据又有代理的对象,我们把这个对象类叫做Context。
我们开始吧,这样会有个问题看你们能发现否
1
2
3
4
|
class Context {
var
object: AnyObject?
var
delegate: AnyObject?
}
|
然而看起来这虽像个通用解决方案,但有个大缺点。我们不能像习惯的delegates那样将转换代理变量为协议(protocol)类型,Any类型也是无法实现的,找到通用的方法相当困难。让我们试着把Context变为通用协议这样其它上下文对象可以适配且可以指定具体协议类型。
1
2
3
4
5
6
|
protocol Context {
typealias DelType
typealias ObjType
var
delegate: DelType? { get set }
var
object: ObjType? { get set }
}
|
这个协议里我们不会指定代理或对象的类型,我们让适配协议的类去做这项工作。
1
2
3
4
5
6
|
class BookControllerContext: Context {
typealias DelType = BookControllerDelegate
typealias ObjType = Book
var
delegate: DelType?
weak
var
object: ObjType?
}
|
这样上下文提供的信息不仅是模型而又有控制器代理,我们能够在控制器上下文里调用它了。
传值
现在我们看看怎么将给定上下文对象从一个控制器传给另一个。这个例子里我们假设外部控制器维护书的列表而内部控制器是描述某本具体书的。
这是我们书(Book)的模型对象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Book {
var
title: String
var
author: String
var
description: String
var
price: Double
var
owned: Bool
init(title: String, author: String, description: String, price: Double, owned: Bool) {
self.title = title
self.author = author
self.description = description
self.price = price
self.owned = owned
}
}
|
这是数据行的视图类
1
2
3
4
5
6
|
class BookRow: NSObject {
@IBOutlet weak
var
bookTitleLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookAuthorLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookPriceLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookBuyLabel: WKInterfaceLabel!
}
|
这是用于内部控制器通知外部控制器他们买了某书的代理协议
1
2
3
|
protocol BookControllerDelegate {
func didBuyBook(book: Book)
}
|
这是书列表的外部控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class BookListController: WKInterfaceController, BookControllerDelegate {
// MARK: BookListController
@IBOutlet
var
table: WKInterfaceTable!
var
books = [
// Pricing based on Amazon.com on Jan 27, 2014
Book(title:
"NSHipster Obscure Topics in Cocoa & Objective-C"
, author:
"Mattt Thompson"
, description:
"To be an NSHipster is to care deeply about the craft of writing code."
, price: 25.29, owned:
false
),
Book(title:
"Functional Programming in Swift"
, author:
"Chris Eidhof, Florian Kugler, Wouter Swierstra"
, description:
"This book will teach you how to use Swift to apply functional programming techniques to your iOS or OS X projects"
, price: 53.07, owned:
false
)
]
func updateDisplay() {
table.setNumberOfRows(books.count, withRowType:
"bookRow"
)
for
i
in
0.. AnyObject? {
var
context = BookControllerContext()
context.object = books[rowIndex]
context.delegate = self
return
context
}
override func willActivate() {
super
.willActivate()
updateDisplay()
}
// MARK: BookControllerDelegate
func didBuyBook(book: Book) {
book.owned =
true
updateDisplay()
}
}
|
注意上面contextForSegueWithIdentifier方法允许我们建立上下文对象并返回它。现在我们看看内部控制器怎么获取并设置代理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class BookListController: WKInterfaceController, BookControllerDelegate {
// MARK: BookListController
@IBOutlet
var
table: WKInterfaceTable!
var
books = [
// Pricing based on Amazon.com on Jan 27, 2014
Book(title:
"NSHipster Obscure Topics in Cocoa & Objective-C"
, author:
"Mattt Thompson"
, description:
"To be an NSHipster is to care deeply about the craft of writing code."
, price: 25.29, owned:
false
),
Book(title:
"Functional Programming in Swift"
, author:
"Chris Eidhof, Florian Kugler, Wouter Swierstra"
, description:
"This book will teach you how to use Swift to apply functional programming techniques to your iOS or OS X projects"
, price: 53.07, owned:
false
)
]
func updateDisplay() {
table.setNumberOfRows(books.count, withRowType:
"bookRow"
)
for
i
in
0.. AnyObject? {
var
context = BookControllerContext()
context.object = books[rowIndex]
context.delegate = self
return
context
}
override func willActivate() {
super
.willActivate()
updateDisplay()
}
// MARK: BookControllerDelegate
func didBuyBook(book: Book) {
book.owned =
true
updateDisplay()
}
}
|
全部源码
这里是全部代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
import WatchKit
/*
* Model
*/
protocol Context {
typealias DelType
typealias ObjType
var
delegate: DelType? { get set }
var
object: ObjType? { get set }
}
class BookControllerContext: Context {
typealias DelType = BookControllerDelegate
typealias ObjType = Book
var
delegate: DelType?
weak
var
object: ObjType?
}
class Book {
var
title: String
var
author: String
var
description: String
var
price: Double
var
owned: Bool
init(title: String, author: String, description: String, price: Double, owned: Bool) {
self.title = title
self.author = author
self.description = description
self.price = price
self.owned = owned
}
}
/*
* View
*/
class BookRow: NSObject {
@IBOutlet weak
var
bookTitleLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookAuthorLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookPriceLabel: WKInterfaceLabel!
@IBOutlet weak
var
bookBuyLabel: WKInterfaceLabel!
}
/*
* Controller
*/
protocol BookControllerDelegate {
func didBuyBook(book: Book)
}
class BookController: WKInterfaceController {
var
delegate: BookControllerDelegate?
var
book: Book!
override func awakeWithContext(context: AnyObject?) {
super
.awakeWithContext(context)
let ctx = context as BookControllerContext
book = ctx.object
delegate = ctx.delegate
}
@IBOutlet weak
var
buyBtn: WKInterfaceButton!
@IBAction func buyPressed() {
delegate?.didBuyBook(book)
popController()
}
}
class BookListController: WKInterfaceController, BookControllerDelegate {
// MARK: BookListController
@IBOutlet
var
table: WKInterfaceTable!
var
books = [
// Pricing based on Amazon.com on Jan 27, 2014
Book(title:
"NSHipster Obscure Topics in Cocoa & Objective-C"
, author:
"Mattt Thompson"
, description:
"To be an NSHipster is to care deeply about the craft of writing code."
, price: 25.29, owned:
false
),
Book(title:
"Functional Programming in Swift"
, author:
"Chris Eidhof, Florian Kugler, Wouter Swierstra"
, description:
"This book will teach you how to use Swift to apply functional programming techniques to your iOS or OS X projects"
, price: 53.07, owned:
false
)
]
func updateDisplay() {
table.setNumberOfRows(books.count, withRowType:
"bookRow"
)
for
i
in
0.. AnyObject? {
var
context = BookControllerContext()
context.object = books[rowIndex]
context.delegate = self
return
context
}
override func willActivate() {
super
.willActivate()
updateDisplay()
}
// MARK: BookControllerDelegate
func didBuyBook(book: Book) {
book.owned =
true
updateDisplay()
}
}
|
结论
你们看到怎么在WatchKit里同时将数据与代理传给视图控制器。我们可以遵循Context协议适配新的上下文,比方我们有个作者视图控制器需要传包含Author(作者)数据与AuthorControllerDelegate(作者控制器代理)的AuthorControllerContext(作者控制器上下文)对象。这是我(作者)目前找到的最好办法来解决WatchKit的有关限制。我(作者)确定还有其它更好的办法来解决问题,如果您也有主意请联系作者Korey Hinton。
译自:http://koreyhinton.com/blog/watchkit-delegates-and-contexts.html