iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 10
1
Software Development

無中生有-從SWIFT語法學習到iOS APP的開發系列 第 10

Day-10 莊家閒家 (Collection View 應用)

前言

這次要把玩家1當成莊家,玩家2當成閒家,用Collection View 把每局誰贏做成一個圖表,並用Collection View內建的表頭(header)顯示勝率,預期效果如下

所以這次要用到的元件當然就是Collection View,不過我卻卡關了,一開始用Collection View Controller居然會當掉...我只好回到土法煉鋼,自己拉一個View Controller ,讓他符合UICollectionView 的 protocol。
雖然我有找到當掉的原因,但是我目前無法解釋,所以...我還是先用 View Controller 來做 Collection View
(本來是不想再教的啊!!!)

1.先在Main.storyboard把各個元件拉好

  • 先拉近一個View Controller (Title: RecordList)
  • 在RecordList中拉一個Collection View,設定Auto Layout 讓他佔滿整個View Controller,在右側Size屬性頁面中設定
    Cell Size: Width: 100, Height: 100
    Header Size: Width: 300, Height: 30
    Min Spacing: For Cells: 10, For Lines: 10
    Section Insets: Top: 10, Bottom: 10, Left: 20, Right: 20
  • 拉一個Label進Collection Reusable View,並將collection Reusable View 的Identifier: collectionHeader
  • Collection View Cell 的Identifier: collectionCell
  • 最後拉一個Image View 進Collection View Cell裡面,記得Auto Layout讓他佔滿整個Cell。(我圖片預設draw和局,可設可不設)
  • 在rps的View Controller中,拉一個button(Record list)用來連結到RecordList 的View Controller,Segue Identifier: segue_rps_to_collection,

2.新增一個cocoa Touch Class,命名為RecordList,繼承UIViewController,語言Swift

3.在RecordList.swift中自定義一個新的class

命名為RecordListCell,並繼承UICollectionViewCell類別

class RecordListCell: UICollectionViewCell {

}

4.自定義Collection Reusable View(collectionHeader) 和Collection View Cell(collectionCell)的class

就選擇剛剛自定義好的Class RecordListCell
PS:元件要選好,因為很多元件已經重疊,必要的時候可以在樹狀圖選擇

5.把Collection View裡面的Label、Image View加進class RecordListCell

用藍線拖曳進class RecordListCell,分別命名為collctionHeader和recordImage

class RecordListCell: UICollectionViewCell {
    
    @IBOutlet weak var collectionHeader: UILabel!
    @IBOutlet weak var recordImage: UIImageView!
}

6.開啟RecordList.swift,讓class RecordList除了繼承UIViewController ,也繼承UICollectionViewDelegate, UICollectionViewDataSource

記得用逗號分隔

此時編譯會顯示錯誤,因為程式碼中尚未符合UICollectionViewDelegate, UICollectionViewDataSource的protocol

什麼是protocol可以參考

Swift開發指南:Protocols與Protocol Extensions的使用心法

class RecordList: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

//...程式碼省略

}

7.要遵守UICollectionViewDelegate, UICollectionViewDataSource的protocol有三個func要實作

在class RecordList中加入下方三個,缺一不可

  • func numberOfSections:這裡只顯示一個段落,所以直接回傳值1
  • func collectionView (numberOfItemsInSection section):這裡要顯示幾筆資料,是由比賽次數決定,而比賽資料將用recordListInCollectionView的陣列儲存,所以這裡回傳值為"陣列資料數(count)"
  • func collectionView (cellForItemAt indexPath):儲存格(cell)要顯示的內容,這裡要使用前面自訂的RecordListCell,所以要把cell向下轉型
var recordListInCollectionView: [String] = []
var winRate: String? = nil //這是給Header顯示勝率的變數

//共有幾個段落
func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

//每個段落有幾個Item
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return recordListInCollectionView.count
    }

//儲存格(cell)裡要顯示哪些資料
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let collectioncell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! RecordListCell //向下轉型,讓collectioncell可以擁有RecordListCell的屬性
        collectioncell.recordImage.image = UIImage (named:recordListInCollectionView[indexPath.row])
        return collectioncell
    }

此外,因為我們需要利用儲存格的表頭顯示勝率,所以要另外呼叫func collectionView (viewForSupplementaryElementOfKind),程式碼如下

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at IndexPath: IndexPath) -> UICollectionReusableView {
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader , withReuseIdentifier: "collectionHeader", for: IndexPath) as! RecordListCell
        headerView.collectionHeader.text = winRate
        return headerView
    }

8.回到(rps)ViewController.swift中

新增幾個變數

    var winnerList: [String] = []
    var makersNum = 0
    var playersNum = 0
    var drawNum = 0

在switch裡面加入一些判斷

  • 累計makers勝場次數
  • 累計players勝場次數
  • 紀錄winnerList
switch checkWinner {
        case let (x, y) where x == y:
            drawNum += 1
            winnerList.append("draw")
            recordListInRps.append("Game \(gameRoundNum) 平手")
            return winnerLabel.text = "平手"
        case (0, 1):
            makersNum += 1
            winnerList.append("makers")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name1!)")
            return winnerLabel.text = "\(name1!)獲勝"
        case (0, 2):
            playersNum += 1
            winnerList.append("players")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name2!)")
            return winnerLabel.text = "\(name2!)獲勝"
        case (1, 0):
            playersNum += 1
            winnerList.append("players")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name2!)")
            return winnerLabel.text = "\(name2!)獲勝"
        case (1, 2):
            makersNum += 1
            winnerList.append("makers")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name1!)")
            return winnerLabel.text = "\(name1!)獲勝"
        case (2, 0):
            makersNum += 1
            winnerList.append("makers")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name1!)")
            return winnerLabel.text = "\(name1!)獲勝"
        case (2, 1):
            playersNum += 1
            winnerList.append("players")
            recordListInRps.append("Game \(gameRoundNum) winner is \(name2!)")
            return winnerLabel.text = "\(name2!)獲勝"
        case (_, _): return
        }

藉由segue把值傳到Collection View,這邊會用到之前定義的segue identifier: segue_rps_to_collection作為判斷切換的依據

if segue.identifier == "segue_rps_to_collection" {
            let collection_VC = segue.destination as! RecordList
            if winnerList != [] {
                collection_VC.winRate = "makers: \(makersNum * 100 / gameRoundNum)% players: \(playersNum * 100 / gameRoundNum)% draw: \(drawNum * 100 / gameRoundNum)%"
                collection_VC.recordListInCollectionView = winnerList
            } else {
                collection_VC.recordListInCollectionView = []
            }

最後為了讓Table View也能快速看出誰勝誰負(只有文字實在太難非清楚誰勝),所以我們一樣加入圖片

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "segue_rps_to_table" {    //確認我們的動作頁面是切到tableView Controller
            let table_VC = segue.destination as! RecordTable
            if recordListInRps != [] {
                table_VC.recordListInTableView = recordListInRps
                table_VC.winnerListInTableView = winnerList // 這次加入的code
            } else {
                table_VC.recordListInTableView = ["比賽尚未開始"]
            }
        }

當然,相對應的Recordtable.swift也要新增
一個變數陣列來接收資料

var winnerListInTableView: [String] = []

改寫override func tableView(cellForRowAt indexPath:)

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let tablecell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
        tablecell.textLabel?.text =  recordListInTableView[indexPath.row]
        
        //以下是改寫內容
        guard winnerListInTableView != [] else { return tablecell}
        tablecell.imageView?.image = UIImage (named: winnerListInTableView[indexPath.row])
        //以上是改寫內容
        
        return tablecell
    }

最後結果


上一篇
Day9 - 戰績表
下一篇
Day11 - Auto Layout
系列文
無中生有-從SWIFT語法學習到iOS APP的開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0

我要留言

立即登入留言