有限的API
當你使用WatchKit時你會發現其API比UIKit受到更多限制,這事實上是件好事,畢竟我們是在為適合手腕的設備編程。因為控件裡沒有取值的API,界面元素只能設置值而不可讀取。標簽文本目前必須在代碼裡單獨設置狀態變化跟蹤。另一方面你會發現沒有prepareForSegue方法,然而有contextForSegueWithIdentifier方法。傳給下個視圖控制器的context(上下文)必須是AnyObject類型的單一對象,在這裡我們可以傳數據與代理對象。我(作者)發現一個直觀方法去同時發送數據對象與代理。
Context
讓我們看看WKInterfaceController怎麼獲取自身上下文對象的。
class InterfaceController: WKInterfaceController { override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) } }
注意上面的代碼裡我們在storyboard加載完畢後調用了awakeWithContext方法,以提供AnyObject類型的上下文對象。但看起來不那麼理想畢竟對象必須轉換為我們需要的類型,而且AnyObject類型而不是Any類型導致了結構體沒法使用,必須轉化為類實例。
然而我們希望簡單的將數據對象作為上下文發送給控制器時,卻沒道理讓數據對象包含一個控制器代理屬性。更合適的辦法是包裝它成為一個既有數據又有代理的對象,我們把這個對象類叫做Context。
我們開始吧,這樣會有個問題看你們能發現否
class Context { var object: AnyObject? var delegate: AnyObject? }
然而看起來這雖像個通用解決方案,但有個大缺點。我們不能像習慣的delegates那樣將轉換代理變量為協議(protocol)類型,Any類型也是無法實現的,找到通用的方法相當困難。讓我們試著把Context變為通用協議這樣其它上下文對象可以適配且可以指定具體協議類型。
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? }
這樣上下文提供的信息不僅是模型而又有控制器代理,我們能夠在控制器上下文裡調用它了。
傳值
現在我們看看怎麼將給定上下文對象從一個控制器傳給另一個。這個例子裡我們假設外部控制器維護書的列表而內部控制器是描述某本具體書的。
這是我們書(Book)的模型對象類
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 } }
這是數據行的視圖類
class BookRow: NSObject { @IBOutlet weak var bookTitleLabel: WKInterfaceLabel! @IBOutlet weak var bookAuthorLabel: WKInterfaceLabel! @IBOutlet weak var bookPriceLabel: WKInterfaceLabel! @IBOutlet weak var bookBuyLabel: WKInterfaceLabel! }
這是用於內部控制器通知外部控制器他們買了某書的代理協議
protocol BookControllerDelegate { func didBuyBook(book: Book) }
這是書列表的外部控制器
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方法允許我們建立上下文對象並返回它。現在我們看看內部控制器怎麼獲取並設置代理
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() } }
全部源碼
這裡是全部代碼
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