原文:watchOS 2 Tutorial Part 2: Tables
歡迎回到 watchOS 2 系列教程!
在第一部分,你通過開發(fā)第一個界面控制器,學習了 watchOS 2 開發(fā)的基礎知識。
在教程的第二部分,會向你的 app 添加一個 table 來展示航班列表。
在這個過程中,你會學到:
- 如何添加一個新的界面控制器,往控制器中添加一個 table,并且設計原型行。
- 如何創(chuàng)建 WKInterfaceController 的子類來填充這個列表,配置每一行的數據,并且處理選中事件;
- 如何模態(tài)呈現界面控制器和向它傳遞數據來顯示。
介紹了這些,讓我們正式開始吧!
開始
打開 Watch\Interface.storyboard,從對象庫中拖動另一個界面控制器到 storyboard 畫板中,放在已經存在的航班控制器的左邊。
選中新的界面控制器,打開屬性檢查器然后做如下修改:
- 設置 Identifier 為 Schedule;
- 設置 Title 為 Air Aber;
- 勾選 Is Inital Controller;
- 勾選 Display Activity Indicator When Loading
你會注意到截圖的標題是深灰色的,而不是充滿活力的粉紅色。讓我們解決它。

打開文件檢查器然后改變 Globla Tint 為 #FA114F?,F在看起來更好一點了:

下一步,從對象庫中拖動一個 Table 到這個新的界面控制器中:

在文本大綱中選中 Table Row Controller:

使用屬性檢查器設置它的 Identifier 為 FlightRow。使用 identifier 作為行的類型標示來通知列表哪一行應該被實例化,它非常重要,所以需要你去設置。
設計行界面
首先修改行提供的默認布局。從文檔大綱中選擇 table row 中的組,使用屬性檢查器設置組的 Spacing 為6、Height 為 Size To Fit Content。
table row 默認有個標準、固定的高度。然而,大多數時候你會希望能顯示全部添加進去的界面元素,所以總是值得使用這種方式去修改高度屬性。
將一個分隔線從對象庫中拖到行中。你不會真的用它去分割什么,而是向你的行里添加一點視覺上的間隔。選中分割線,使用屬性檢查器做如下修改:
- 設置 Color 為 #FA114F;
- 設置 Vertical alignment 為 Center;
- 設置 Height 為 Relative to Container;
- 設置 Adjustment 為-4。
最后檢查器應該是這樣:

現在是時候填充這一行了!
從對象庫中拖一個Group到到 table row 中,放在分割線的后面。選中組,在屬性檢查器中修改如下屬性:
- 設置 Layout 為 Vertical;
- 設置 Spacing 為0;
- 設置 Width 為 Size To Fit Content。
你可能注意到你經常設置的 Spacing 屬性;它的作用僅僅是收緊組中的界面元素之間的間距讓它們在小屏幕上看起來更清晰。
拖動另一個 Group 到剛才添加的組中,做如下改變:
- 設置 Spacing 為4;
- 設置 Height 為 Fixed,值為32。
往這個新的組中,添加一個 Label、一個 Image然后另一個 Label。這兩個標簽會顯示每個航班的起點和重點。
現在你需要往 image 中添加圖片。下載圖片然后把它添加到 Watch\Assets.xcassets 中。這次創(chuàng)建一個新的叫做Plane的圖片,放在2x槽中:

重新打開 Watch\Interface.storyboard 然后選擇這個 image。使用屬性檢查器,做如下改變:
- 設置 Image 為 Plane;
- 設置 Tint 為 #FA114F;
- 設置 Vertical alignment 為 Center;
- 設置 Width 為 Fixed,值為24;
- 設置 Height 為 Fixed,值為20。
選擇左邊的標簽設置它的文本為 MAN。修改它的 Font 為 System, style 為 Semibold 和20的字體大小。最后設置它的 Vertical alignment 為 Center。
同樣修改右邊的標簽,但是文本修改成 SFO。你的 table row 現在應該是這樣:

界面元素層次結構像下面這樣:

你已經完成大多數 table row 界面的設計;之后需要增加航班號和狀態(tài)了。
從對象庫中拖動另一個 Group 到 table row ,確保它是包含起點終點標簽的那個組的子節(jié)點:

當你繼續(xù)設計這個界面,你可以看到更多使用嵌套組與混合布局來創(chuàng)建復雜布局的例子。根本就沒自動布局的事。
拖動兩個 label 到新的組中。再次使用屬性檢查器對最左邊的標簽做如下的改變:
- 設置 Text 為 AA123;
- 設置 Text Color 為 Light Gray Color;
- 設置 Font 為 Caption 2;
- 設置 Vertical alignment 為 Bottom。
修改右邊的標簽:
- 設置 Text 為 On time;
- 設置 Text Color 為 #04DE71;
- 設置 Font 為 Caption 2;
- 設置 Horizontal alignment 為 Right;
- 設置 Vertical alignment 為 Bottom。
做完這些改變后,最后的 table row 看起來應該像這樣:

列表在 Interface Builder 中開發(fā)完成,是時候填充一些數據了。
填充列表
首先創(chuàng)建一個 WKInterfaceController 的子類為列表提供數據。
在項目導航中右擊 Watch Extension 組選擇New File...。在彈出的對話框中選擇 watchOS\Source\WatchKit Class 然后點擊Next。命名新的類為 ScheduleInterfaceController,確保它是 WKInterfaceController 的子類并且語言設置為 Swift:

點擊 Next,然后 Create。
當在代碼編輯器中打開了新創(chuàng)建的文件,刪除三個空的方法后就剩下重要的語句和類定義了。
重新打開 Watch\Interface.storyboard ,選擇新的界面控制器。在 Identity Inspector,修改 Custom Class\Class 為 ScheduleInterfaceController:

在選中界面控制器的基礎上,打開輔助編輯器確保它顯示的是 ScheduleInterfaceController。然后按住 Ctrl從 Table 往 ScheduleInterfaceController 里面拖拽來創(chuàng)建一個 outlet:

命名 outlet 為 flightTable,確保類型設置為 WKInterfaceTable 然后點擊 Connect。
現在你已經設置好自定義的類并且為 table 創(chuàng)建了一個 outlet,是時候填充一些數據了!
關閉輔助編輯器,打開 ScheduleInterfaceController.swift,在 outlet 的下面添加如下代碼
var flights = Flight.allFlights()
這里你僅僅增加了一個 Flight 對象數組保存所有航班信息。
下一步,增加 awakeWithContext(_:) 的實現:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
flightsTable.setNumberOfRows(flights.count, withRowType: "FlightRow")
}
這段代碼通知 table 為 flights 數組中每個 flight 創(chuàng)建一個 Interface Builder 中的行實例。行的數量等于數組的大小,行的類型就是你在 storyboard 中設置的 identifier 的值。
編譯運行。你會看到列表已經填充幾行數據了:

你應該注意到列表中顯示的是 Interface Builder 中的占位文本。為了修復這種情況,我們通過添加 row controller 分別配置每一行的 label。
添加 Row Controller
在項目導航中右擊 Watch Extension 組然后選擇 New File...。在出現的對話框中選擇 watchOS\Source\WatchKit Class 然后點擊 Next。命名新的類為 FlightRowContorller,確保這個類是 NSObject 的子類并且語言設置為 Swift 了:

點擊 Next,之后 Create。
當新的文件在代碼編輯器中打開了,在類的頂部增加如下代碼:
@IBOutlet var separator: WKInterfaceSeparator!
@IBOutlet var originLabel: WKInterfaceLabel!
@IBOutlet var destinationLabel: WKInterfaceLabel!
@IBOutlet var flightNumberLabel: WKInterfaceLabel!
@IBOutlet var statusLabel: WKInterfaceLabel!
@IBOutlet var planeImage: WKInterfaceImage!
這里僅僅為每個你添加到 table row 中的 label 添加對應的 outlet。稍后會連接它們。
下一步,在這些 outlet 下面添加如下這個屬性和對應的屬性監(jiān)視器:
// 1
var flight: Flight? {
// 2
didSet {
// 3
if let flight = flight {
// 4
originLabel.setText(flight.origin)
destinationLabel.setText(flight.destination)
flightNumberLabel.setText(flight.number)
// 5
if flight.onSchedule {
statusLabel.setText("On Time")
} else {
statusLabel.setText("Delayed")
statusLabel.setTextColor(UIColor.redColor())
}
}
}
}
下面一步步講解是怎么回事:
- 你定義了類型為 Flight 的可選屬性。記住,這個類是在 Flight.swift 中定義的,而 Flight.swift 是你在教程1中添加到 Watch Extension 中的共享代碼的一部分;
- 添加了一個屬性監(jiān)視器,當屬性被賦值的時候會觸發(fā);
- 當 flight 為 nil 的時候會提前退出。因為它是可選的,只有當 Flight 對象有效的時候才去設置標簽的屬性。
- 使用 flight 的相關屬性去設置標簽。
- 如果航班被延誤了,改變標簽的文本顏色,并相應地更新文本。
當 row controller 設置完成,現在需要更新 table row 來使用它。
打開 Watch\Interface.storyboard 然后在文檔大綱中選擇 FlightRow。使用 Identity Inspector,設置 Custom Class\Class 為 FlightRowController。
下一步,在文檔大綱中右擊FlightRow:

連接 planeImage 和 table row 中的 image,separator 與 table row 中的 separator。之后進行如下連線:
- destinationLabel: SFO
- flightNumberLabel: AA123
- originLabel: MAN
- statusLabel: On time
最后一步就是更新 ScheduleInterfaceController 來向列表中每個行控制器傳遞 Flight 對象。
打開 ScheduleInterfaceController.swift 之后在 awakeWithContext(_:) 底下添加如下代碼:
for index in 0..<flightsTable.numberOfRows {
if let controller = flightsTable.rowControllerAtIndex(index) as? FlightRowController {
controller.flight = flights[index]
}
}
這里使用 for 循環(huán)遍歷列表中每一行來訪問指定索引的行控制器。假如你成功設置了 controller 的 flight 屬性,會觸發(fā) didSet 監(jiān)視器并且設置 table row 中所有的標簽。
是時候看看勞動成果了。編譯運行。你會看到 table row 已經被相關的航班信息填充了:

響應行的選中事件
首先去重寫 WKInterfaceController 中主要負責處理 table row 選中事件方法定義。
往 ScheduleInterfaceController 中添加如下代碼:
override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) {
let flight = flights[rowIndex]
presentControllerWithName("Flight", context: flight)
}
這里使用行的索引來檢索出合適的航班信息。之后顯示航班詳情界面,作為 context 屬性傳遞 flight 給這個界面。presentControllerWithName(_:context:) 方法的name參數是你在 storyboard 中設置identifier的值。
現在你需要更新 FlightInterfaceController 來使用 context 的值來設置它的界面。
打開 FlightInterfaceController.swift,找到 awakeWithContext(_:) 。替換這句代碼:
flight = Flight.allFlights().first!
為如下代碼:
if let flight = context as? Flight { self.flight = flight }
這里嘗試轉換 context 為 Flight 對象。如果轉換成功就可以使用它來設置self.flight,這會觸發(fā)屬性監(jiān)視器來設置界面。
最后,編譯運行。如果你點擊某個 table row ,你會看到航班詳情界面模態(tài)彈出,顯示你選中的航班的詳情:

恭喜!你已經完成你的第一個列表并且使用真實數據填充它;太棒了!
總結
這里目前教程的完整實例項目。
在這片教程中你學習到如何往界面控制器中添加一個列表,設計 table row 界面,創(chuàng)建一個行控制器,處理 table row 選中,顯示其他界面控制器,傳遞 contexts。在這20分鐘里面做了很多事情!
如果您對本教程有任何疑問或意見,請參加下面的論壇進行討論!