Xcode-Popover and Check Mark Practice

春麗 S.T.E.M.
10 min readApr 19, 2023

--

目錄

⦿ Popover and Check Mark
⦿ Storyboard
⦿ TableViewController

Popover and Check Mark

之前的文章,我們知道如果使用 UIAlertController 時,可以選擇 .alert.actionSheet 來彈出提示框.alert 是一般常見從中間彈出的提示框,.actionSheet 則是從底部彈出的提示框。

我們稱 alert 這種提示框為 Popup

.alert 的應用場景為提示、警示、創建帳號、輸入密碼等,若要客製化另一種提示框就不是使用 UIAlertController 了,先看到如下:

在 Navigation Bar 的 rightBarButtonItem,我們看到的是一張系統圖plus.square.fill.on.square.fill,是一個加號,若按下 rightBarButton,會彈出另一個提示框,提示框叫做 Popover,提示框的項目中,有兩項已經打勾(Check Mark)。

點選提示框各項,若要能夠打勾取消打勾,應該怎麼做呢?

popoverPresentationController,是的!這種依附在別的元件上的提示框就稱為 Popover

接下來,一步步教大家如何使用 Popover 吧!

繼續閱讀|回目錄

Storyboard

在 Storyboard 中,我們先將 VC 嵌入 Navigation Controller,在 Right Bar Button Items 選擇一個圖片如下圖左:

看到右圖,再從這個 Bar Button 寫一個 IBAction,程式碼列出如下:

@IBAction func rightBarButtonItemTapped(_ sender: UIBarButtonItem) {
let vc = PopoverPresentationTableViewController()
// 設定為提示框形式
vc.modalPresentationStyle = .popover
// 指定提示框的彈出位置為 rightBarButtonItem
vc.popoverPresentationController?.barButtonItem = navigationItem.rightBarButtonItem
// 設定 delegate
vc.popoverPresentationController?.delegate = self
vc.preferredContentSize = CGSize(width: 200, height: 177)
present(vc, animated: true, completion: nil)
}

當按下 button 後,會產生一個 VC,這邊是 PopoverPresentationTableViewController,由於我們還沒加入這個 TableViewController,所以先看到後面的程式碼。

VC 是你的提示框,提示框的形式為 popover,VC 往上找到管理它的 Controller,這個 Controller 的 barButtonItem 將之設為 navigationItem.rightBarButtonItem,意思即是當 VC 彈出來時,會以 rightBarButton 做定位,或說由 rightBarButton 彈出 VC

接著設定 UIPopoverPresentationControllerDelegatedelegate,所以在 VC 還需遵循 UIPopoverPresentationControllerDelegate 這個 protocol。

最後設定 VC 做為提示框時的 Content Size,再 present 出來。

但是!假定你已經創建了這個 VC 的 class,此時按下 rightBarButton 仍然不會是我們要的提示框形式,會出現如下:

竟然呈現便利貼的形式,我們必須還要在 VC 中加入一段程式碼如下:

func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}

這段程式碼大致是說不要因為環境而去改變 VC 的 PresentationStyle,如此才能得到我們想要的結果。

繼續閱讀|回目錄

TableViewController

如果要做 Popover,通常建議是使用 TableViewController,像是在 Storyboard 中加入 Static Cell,再設定 Cell 的數量及內容。

由於前段是以 PopoverPresentationTableViewController() 去創建 VC,所以接下來是程式碼的編寫,首先要注意的是 Check Mark,點擊 Cell Row 沒有問題,我們可用 indexPath 去記錄點了哪一個 Section 的哪一個 Row,若只有一個 Section,indexPath.row 就是你點擊的項目編號

我們先宣告幾個 Properties 如下:

    private var items = ["天干", "地支", "生年", "大運"]
private var selectedItems = Set<Int>()
private let identifier = "checklistCell"

items 就是 Popover 的內容;selectedItems 是一個 Int 型別的 Set,概念是點擊的 Cell Row(即項目編號),把這個編號存起來,再次點擊就把該項目編號移除;由於是程式碼產生 Cell,便在此給予 identifier

所以,我們先看 didSelectRowAt 的編寫:

override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
// 若點擊的位置在該 indexPathRow
if let cell = tableView.cellForRow(at: indexPath) {
// 改變 checkmark 狀態
if cell.accessoryType == .checkmark {
cell.accessoryType = .none
// 記錄 checkmark 狀態
selectedItems.remove(indexPath.row)
} else {
cell.accessoryType = .checkmark
selectedItems.insert(indexPath.row)
}
// 選取 cellRow 反白會消失
tableView.deselectRow(at: indexPath, animated: true)
}
}

tableView.cellForRow 是從 indexPath 去找到 Cell 的 Row,再改變此 Row 的 accessoryType,即在 .none.checkmark 間切換,如果是 checkmark 狀態切換成 none,selectedItems 就要移除indexPath.row(項目編號);反之,則要加入該 indexPath.row

最後 deselectRow,因為預設點擊 TableViewCell 的反應是變灰色,這預設會讓點擊看起來很醜,所以要讓它再從灰色變回白色。

接著是 data source 的部分如下:

override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return items.count
}

override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: identifier,
for: indexPath)
cell.textLabel?.text = items[indexPath.row]

// 確認 cellRow 是否有 checkmark
if selectedItems.contains(indexPath.row) {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}

return cell
}

items.count 即是 Cell Row 的數量;而 dequeReusableCell 必須在 identifier 註冊過的情況下,我們使用預設的 Cell 的形式,去設定它的 textLabel?.text 的項目顯示;最後則是以 selectedItems 去檢查項目編號是否被存進去了,存進去表示有 checkmark,沒存進去,cell 的 accessoryType 則是 .none

最後記得 viewDidLoad() 中:

self.tableView.register(UITableViewCell.self,
forCellReuseIdentifier: identifier)

把自己的 Cell 註冊一個 ID 來 reusable。

完成了!

前面的 Popover 是用程式碼生成的,我們也可以用 Storyboard 去生成這個 TableViewController,這時候要記得將 Cell 設為 Static,而 TableView 的 style 設為 Grouped 或 Inset Grouped 會比較好看,但實測結果還是程式碼生成的顏值高一些。

左、中 Storyboard 分別為 Grouped、Inset Grouped;右圖為程式碼生成

--

--

春麗 S.T.E.M.
春麗 S.T.E.M.

Written by 春麗 S.T.E.M.

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.

No responses yet