iOS 9 Storyboard 教程(二下)

接上鏈接

Add Player控制器在工作

現(xiàn)在你會(huì)忽視Game行,僅僅讓用戶輸入玩家的名字.
當(dāng)用戶點(diǎn)擊Cancel按鈕的時(shí)候,這個(gè)控制器將會(huì)關(guān)閉并且不管你輸了什么數(shù)據(jù)都不會(huì)保存.這個(gè)部分用unwind segue已經(jīng)起作用了.

但是當(dāng)用戶點(diǎn)擊Done按鈕的時(shí)候,你應(yīng)該創(chuàng)建創(chuàng)建一個(gè)新的Player 對(duì)象并且填寫它的屬性和更新?玩家的清單.

每當(dāng)segue將要?jiǎng)?chuàng)建的時(shí)候prepareForSegue(_:sender:)都會(huì)被調(diào)用.在退回(dismiss)這個(gè)視圖的時(shí)候,你需要重寫這個(gè)方法來(lái)存儲(chǔ)你輸入的玩家對(duì)象的數(shù)據(jù).


Note:

你永遠(yuǎn)不會(huì)手動(dòng)調(diào)用prepareForSegue(_:sender:)方法.它是一條從UIKit發(fā)出的信息,讓你知道那個(gè)segue已經(jīng)被觸發(fā)了.


在PlayerDetailsViewController.swift里,首先在類頂部添加一個(gè)屬性來(lái)存儲(chǔ)你添加的玩家的詳細(xì)信息.

var player:Player?

接下來(lái),在PlayerDetailsViewController.swift里添加下面這個(gè)方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
  if segue.identifier == "SavePlayerDetail" { 
    player = Player(name: nameTextField.text!, game: "Chess", rating: 1) 
  }
}

prepareForSegue(_:sender:)使用默認(rèn)的游戲和評(píng)級(jí)變量,創(chuàng)建了一個(gè)新的Player實(shí)例.它只是為帶有SavePlayerDetail標(biāo)識(shí)符的segue起作用.
在Main.storyboard里,在Document Outline找到Add Player的控制器,然后選擇unwind segue,改Identifier為savePlayerDetail.

跳到PlayersViewController,改變這個(gè)unwind segue方法savePlayerDetail(segue:)為下面這樣:

@IBAction func savePlayerDetail(segue:UIStoryboardSegue) { 
  if let playerDetailsViewController = segue.sourceViewController as? PlayerDetailsViewController { 
  //add the new player to the players array 
  if let player = playerDetailsViewController.player { 
    players.append(player) 
    //update the tableView  
    let indexPath = NSIndexPath(forRow: players.count-1, inSection: 0) 
    tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) 
    } 
  }
}

這就得到一個(gè)PlayerDetailsViewController的引用,通過(guò)這個(gè)segue引用,可以傳遞到這個(gè)方法.它用來(lái)往玩家數(shù)組里添加新的Player對(duì)象來(lái)作為數(shù)據(jù)源.然后它會(huì)告訴tableView添加了新的一行(在底部),因?yàn)閠ableView和它的數(shù)據(jù)源始終是同步的.

你可能用tableView.reloadData()完成了書信界面,但是上面的方法伴有動(dòng)畫插入一行的時(shí)候看上去更漂亮.

UITableViewRowAnimation.Automatic會(huì)自動(dòng)地找出合適地動(dòng)畫,取決于你插入新行的位置.非常方便.

試一下,現(xiàn)在你應(yīng)該可以向列表添加新的玩家了!

Paste_Image.png

性能

現(xiàn)在在storyboard中有幾個(gè)viewController,你也許想知道關(guān)于他們的性能.立刻加載整個(gè)storyboard也不是很大的問(wèn)題.storyboard并沒(méi)有馬上實(shí)例化所有的viewController–只有初始viewController是被立即加載的.因?yàn)槟愕某跏紇iewController是一個(gè) TabBarontroller,它所包含的兩個(gè)viewController也被加載了.

直到你segue他們,其他的viewController才被實(shí)例化.當(dāng)你關(guān)閉這些viewController的時(shí)候,他們就立即被釋放了.所以只有使用的ViewController才存在內(nèi)存中.

讓我們?cè)趯?shí)踐中看看吧!在PlayerDetailsViewController中添加一個(gè)初始化方法和一個(gè)反初始化方法:

required init?(coder aDecoder: NSCoder) { 
  print("init PlayerDetailsViewController") 
  super.init(coder: aDecoder)}
deinit { 
  print("deinit PlayerDetailsViewController")
}

你重寫了init?(coder:)和deinit方法,并且讓它們?cè)赬code控制臺(tái)輸出了一條信息.現(xiàn)在再一次運(yùn)行app,然后打開(kāi)Add Player控制器,你應(yīng)該看到這個(gè)viewController沒(méi)有得到分配直到它打開(kāi)的時(shí)候.

當(dāng)你關(guān)閉 Add Player控制器,也點(diǎn)擊了Cancel和Done按鈕的時(shí)候,你應(yīng)該會(huì)看到deinit里print()方法輸出地狀態(tài)信息.如果你再一次打開(kāi)了這個(gè)控制器,你應(yīng)該也會(huì)再一次看到從init?(coder:)輸出的狀態(tài)信息.這就會(huì)是你相信了,ViewController是在使用的時(shí)候才加載的.

Game Picker控制器

在Add Player控制器里點(diǎn)一下Game那一行應(yīng)該會(huì)打開(kāi)一個(gè)新的控制器,可以讓用戶從一個(gè)列表里選擇游戲.也就意味著你將會(huì)添加另一個(gè)tableViewController,然而這一次你需要從導(dǎo)航棧里推出(push)它,而不是從下往上彈出.

拖拽一個(gè)新的 TableViewController到Main.storyboard里.在AddPlayerscene里選擇Game的單元格(確保你選擇的的是整個(gè)單元格,而不是標(biāo)簽)并且按住ctrl并拖線到新的新的TableViewController在它們之前創(chuàng)建一個(gè)segue連線.在出現(xiàn)的彈窗中選擇Selection Segue底下的Show segue,而不是Accessory Action.

選擇這個(gè)新的segue然后在Attributes Inspector設(shè)置它的標(biāo)識(shí)符為PickGame.

在 Document Outline里選擇新的TableViewController,并且在Attributes Inspector里,給這個(gè)控制器的標(biāo)題命名為Choose Game.


設(shè)置單元格的樣式為Basic,然后設(shè)置它的重用標(biāo)識(shí)符為GameCell.你需要為這個(gè)控制器所做的就是這些.



為這個(gè)工程添加一個(gè)新的Swift文件,使用Cocoa Touch Class模板,命名為GamePickerViewController,繼承自UITableViewController.

返回Main.storyboard里你新建的Choose Game控制器然后在Identity Inspector里設(shè)置自定義的類GamePickerViewController.

現(xiàn)在讓我們給這個(gè)新的控制器一些數(shù)據(jù)來(lái)顯示吧.在GamePickerViewController.swift中,把一個(gè)具有硬編碼值的games字符串?dāng)?shù)組添加到頂部:

var games:[String] = [ 
  "Angry Birds", "Chess", 
  "Russian Roulette", 
  "Spin the Bottle", 
  "Texas Hold'em Poker", 
  "Tic-Tac-Toe"
]

現(xiàn)在從模板里替換數(shù)據(jù)源方法:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
  return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
  return games.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) 
  cell.textLabel?.text = games[indexPath.row] 
  return cell
}

你只是使用games數(shù)組設(shè)置了數(shù)據(jù)源并且把字符串的值放到了單元格的textLabel里.

就數(shù)據(jù)源而言應(yīng)該那樣做.運(yùn)行app然后點(diǎn)擊Game行.新的Choose Game控制器將會(huì)滑出來(lái).然而點(diǎn)擊這些行不會(huì)做任何事,那是因?yàn)檫@個(gè)控制器是在導(dǎo)航堆棧上被彈出來(lái)的.但是你卻總可以點(diǎn)擊返回按鈕返回到Add Player控制器.

Paste_Image.png

這很酷,不是嗎?你沒(méi)有寫任何代碼調(diào)用新的控制器.你只是按住ctrl鍵并從靜態(tài)table view cell拖拽出了新的控制器.你寫的唯一的代碼就是填充tableView的內(nèi)容,這通常是更動(dòng)態(tài)的而不是硬編碼列表.

當(dāng)然,如果不發(fā)送任何返回?cái)?shù)據(jù),這個(gè)新的控制器將不是很有用,所以你還需要為它添加一個(gè)新的unwind segue.

在GamePickerViewController類的頂部添加屬性來(lái)保存名字和當(dāng)前選中游戲的索引:

var selectedGame:String? { 
  didSet {  
    if let game = selectedGame { 
      selectedGameIndex = games.indexOf(game)! 
      } 
    } 
} 
var selectedGameIndex:Int?

不管什么時(shí)候selectedGame更新了,didSet將會(huì)在games里定位到游戲字符串并且在表的正確的索引位置自動(dòng)更新selectedGameIndex.

接下來(lái),改變tableView(_:cellForRowAtIndexPath:):

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) 
  cell.textLabel?.text = games[indexPath.row] 
  if indexPath.row == selectedGameIndex { 
    cell.accessoryType = .Checkmark 
  } else { 
    cell.accessoryType = .None 
  } 
return cell
}

這就給包含當(dāng)前選中游戲名稱的單元格設(shè)置了一個(gè)對(duì)號(hào).例如被這個(gè)app的用戶贊賞的一些小的手勢(shì).

現(xiàn)在添加代理方法tableview(_:didSelectRowAtIndexPath:) :

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
  tableView.deselectRowAtIndexPath(indexPath, animated: true) 
  //Other row is selected - need to deselect it 
  if let index = selectedGameIndex { 
    let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0)) cell?.accessoryType = .None 
  } 
  selectedGame = games[indexPath.row] 
//update the checkmark for the current row 
  let cell = tableView.cellForRowAtIndexPath(indexPath) 
  cell?.accessoryType = .Checkmark
}

不管用戶何時(shí)點(diǎn)擊一行,這個(gè)方法都被稱為Table View 的代理.

這個(gè)方法在點(diǎn)擊之后就會(huì)取消選中.那使得它從灰色高亮褪色為正常的白色.然后它就會(huì)從先前選中的單元格移除對(duì)號(hào)標(biāo)記,然后把對(duì)號(hào)放到剛剛點(diǎn)擊的那一行上.

現(xiàn)在運(yùn)行app測(cè)試一下吧.點(diǎn)擊一個(gè)游戲的名稱,那一行就會(huì)顯示一個(gè)對(duì)號(hào).點(diǎn)擊另一個(gè)游戲的名稱,標(biāo)記就會(huì)隨至移動(dòng)到那一行.

Paste_Image.png

只要你點(diǎn)擊一行這個(gè)控制器應(yīng)該就會(huì)消失,但是現(xiàn)在卻不是那樣,因?yàn)槟氵€真正的連接一個(gè)unwind segue.聽(tīng)起來(lái)下一步非常棒!
在PlayerDetailsViewController.swift里,在類的頂部,添加一個(gè)屬性來(lái)保存選中的游戲,那樣你就可以在Player對(duì)象存儲(chǔ)它.給它一個(gè)默認(rèn)的名字”Chess”,那樣你就會(huì)一個(gè)新的玩家始終都會(huì)有衣蛾選中的游戲名字.

var game:String = "Chess" { 
  didSet { 
  detailLabel.text? = game 
  }
}

不管何時(shí)名稱發(fā)生改變,didSet將會(huì)在靜態(tài)表單元格里顯示游戲的名稱.
依然在PlayerDetailsViewController.swift里,添加unwind segue 方法:

@IBAction func unwindWithSelectedGame(segue:UIStoryboardSegue) { 
  if let gamePickerViewController = segue.sourceViewController as? GamePickerViewController, selectedGame = gamePickerViewController.selectedGame { 
    game = selectedGame 
  }
}

一旦用戶從Choose Game控制器里選擇了一個(gè)游戲,上面的代碼就會(huì)執(zhí)行.這個(gè)方法會(huì)更新控制器里的標(biāo)簽以及選中游戲的屬性.unwind segue 也會(huì)將GamePickerViewController從導(dǎo)航棧里彈出.

在Main.storyboard里,按住ctrl把tableview的單元格拖拽到Exit,就想你之前做的一樣,然后從彈框中選擇unwindWithSelectedGame:.


在 Attributes Inspector 里給新的unwind segue的標(biāo)識(shí)符(Identifier)為SaveSelectedGame.

運(yùn)行app檢查它到目前為止的功能.創(chuàng)建一個(gè)新的玩家,選擇玩家的游戲然后選擇一個(gè)游戲.


在Add Player控制器里游戲并沒(méi)有更新!
不幸的是,unwind segue方法在tableView(:didSelectRowAtIndexPath:)之前執(zhí)行,所以selectedGameIndex沒(méi)有更新.
幸運(yùn)的是,你可以重寫prepareForSegue(
:sender:)方法并且在unwind發(fā)生之前完成操作.
在GamePickerViewController里重寫prepareForSegue(_:sender:):

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
  if segue.identifier == "SaveSelectedGame" { 
    if let cell = sender as? UITableViewCell { 
      let indexPath = tableView.indexPathForCell(cell) 
      if let index = indexPath?.row { 
        selectedGame = games[index] 
       } 
    } 
  }
}

prepareForSegue(_:sender:)參數(shù)的發(fā)送者是初始化segue的對(duì)象,在這種情況下就是被選中的游戲單元格.所以在games里,你可以使用單元格的indexPath來(lái)定位選中的游戲,然后設(shè)置selectedGame,這樣的話,它在unwind segue就是可行的了.

現(xiàn)在當(dāng)你運(yùn)行app然后選擇游戲的時(shí)候,它就會(huì)更新與動(dòng)員的游戲了!

Paste_Image.png

接下來(lái),你需要改變PlayerDetailsViewController的prepareForSegue(_:sender:)方法來(lái)返回一個(gè)選中的游戲,而不是硬編碼為”Chess”.當(dāng)你完成添加一個(gè)玩家的時(shí)候,用這種方式,它們實(shí)際的游戲?qū)?huì)顯示在Players控制器里.

在PlayerDetailsViewController.swift里,改變prepareForSegue(_:sender:)如下:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
  if segue.identifier == "SavePlayerDetail" { 
    player = Player(name: nameTextField.text, game:game, rating: 1) 
  }
}

當(dāng)你完成添加Add Player控制器并且按下完成按鈕的時(shí)候,玩家的列表將會(huì)更新為正確的游戲.

還有一件事– 當(dāng)你選擇一個(gè)游戲的時(shí)候,返回到Add Player控制器,然后嘗試再選擇一個(gè)游戲,你之前選中的游戲應(yīng)該會(huì)有一個(gè)對(duì)號(hào)標(biāo)記.解決方案就是當(dāng)你連線(segue)的時(shí)候,通過(guò)選中的游戲存儲(chǔ)在PlayerDetailsViewController到了GamePickerViewController上.

仍然在PlayerDetailsViewController.swift,添加到prepareForSegue(_:sender:)的末尾:

if segue.identifier == "PickGame" { 
  if let gamePickerViewController = segue.destinationViewController as? GamePickerViewController { 
    gamePickerViewController.selectedGame = game 
  }
}

需要注意的是現(xiàn)在你有兩個(gè)if狀態(tài)來(lái)判斷segue.identifier.SavePlayerDetail就是unwind segue將會(huì)返回的Players列表,PickGame就是顯示segue將要繼續(xù)向前到Game Picker 控制器.你添加的代碼將會(huì)在GamePickerViewController里定位到視圖的位置,然后設(shè)置selectedGame.設(shè)置selectedGame將會(huì)自動(dòng)更新table view cell的索引selectedGameIndex,用來(lái)設(shè)置一個(gè)對(duì)號(hào).
非常好!你現(xiàn)在有一個(gè)功能選擇游戲的控制器了!

Paste_Image.png

本教程的所有源代碼:
請(qǐng)到這里下載:下載鏈接
翻譯過(guò)程中,有個(gè)別地方不是十分準(zhǔn)確,希望大家批評(píng)指正有好的建議也可以回復(fù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市伞辛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件学辱,死亡現(xiàn)場(chǎng)離奇詭異丘喻,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)直晨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膨俐,“玉大人勇皇,你說(shuō)我怎么就攤上這事》俅蹋” “怎么了敛摘?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)乳愉。 經(jīng)常有香客問(wèn)我兄淫,道長(zhǎng),這世上最難降的妖魔是什么蔓姚? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任捕虽,我火速辦了婚禮,結(jié)果婚禮上坡脐,老公的妹妹穿的比我還像新娘泄私。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布晌端。 她就那樣靜靜地躺著捅暴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咧纠。 梳的紋絲不亂的頭發(fā)上蓬痒,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音漆羔,去河邊找鬼梧奢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛钧椰,可吹牛的內(nèi)容都是我干的粹断。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嫡霞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瓶埋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起诊沪,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤养筒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后端姚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體晕粪,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年渐裸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巫湘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昏鹃,死狀恐怖尚氛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洞渤,我是刑警寧澤阅嘶,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站载迄,受9級(jí)特大地震影響讯柔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜护昧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一魂迄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惋耙,春花似錦极祸、人聲如沸慈格。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蒜田,卻和暖如春稿械,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冲粤。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工美莫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梯捕。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓厢呵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親傀顾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子襟铭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容