在 Web 應用中,我們常常會看到畫面的右下角會提供一個按鈕,點下去後會展開更多的按鈕,用來提供一些功能(例如分享到 Facebook/Line/Weibo)
這次要在 iOS 中實現這個功能。
ExpandableButtons
藍色的按鈕負責顯示/隱藏其餘的四個按鈕。
SKExpandableView
為了讓這個折疊式按鈕可以被 reuse ,這裡建立一個繼承於 UIView 的 SKExpandableView.
排版的時候我們從上到下依次放入按鈕,功能按鈕通過一個 Array 來保管,藍色按鈕命名為 baseButton
並且給每個按鈕加入 touchUpInside 事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private func setupButtons() { for i in 0..<imageNames.count { let name = imageNames[i] let button = UIButton(frame: CGRect(x: 0, y: 44 * i , width: 44, height: 44)) button.setImage(UIImage(named:name), for: .normal) button.tag = i + 1 button.addTarget(self, action: #selector(buttonTapHandler(button:)), for: .touchUpInside) addSubview(button) buttons.append(button) } baseButton.frame = CGRect(x: 0, y: 44 * 4, width: 44, height: 44) baseButton.setImage(UIImage(named:"icon-base"), for: .normal) baseButton.addTarget(self, action: #selector(buttonTapHandler(button:)), for: .touchUpInside) baseButton.tag = 0 addSubview(baseButton) } |
負責處理每個按鈕 touchUpInside 事件的方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
@objc private func buttonTapHandler(button:UIButton) { DispatchQueue.main.async { switch(button.tag){ case 0: self.switchMenu() case 1: self.delegate?.didTapExpandable(button: .book) case 2: self.delegate?.didTapExpandable(button: .box) case 3: self.delegate?.didTapExpandable(button: .camera) case 4: self.delegate?.didTapExpandable(button: .card) default: break } } } |
當 baseButton 被點下的時候,就會執行 switchMenu 方法,將功能性的按鈕通過動畫來隱藏或顯示。
1 2 3 4 5 6 7 |
private func switchExpandableStackView() { UIView.animate(withDuration: 0.3, animations: { _ = self.buttons.map{ ($0.alpha == 0) ? ($0.alpha = 1) : ($0.alpha = 0) } }) } |
Delegate
通過建立 Delegate 來讓調用者能夠得知哪一個功能性的按鈕觸發了 touchUpInside 事件
1 2 3 4 5 6 7 8 9 10 |
enum SKExpandableButtonType { case card case camera case box case book } protocol SKExpandableViewDelegate { func didTapExpandable(button:SKExpandableButtonType) } |
Example
在 HomeViewController 中加入 SKExpandableView 並設定 delegate 方法
1 2 3 4 5 6 |
private func setupView() { let screenSize = UIScreen.main.bounds let menu = SKExpandableView(frame: CGRect(x: screenSize.width - 50, y: screenSize.height - 400, width: 44, height: 220)) menu.delegate = self view.addSubview(menu) } |
實作 delegate 方法
1 2 3 4 5 6 7 8 9 10 |
extension HomeViewController: SKExpandableViewDelegate { func didTapExpandable(button:SKExpandableButtonType) { switch button { case .card: showMessage("did tap card") case .camera: showMessage("did tap camera") case .box: showMessage("did tap box") case .book: showMessage("did tap book") } } } |
當 SKExpandableView 中的功能性按鈕觸發 touchUpInside 事件的時候跳出提示
1 2 3 4 5 6 |
private func showMessage(_ message:String) { let alertController = UIAlertController(title: "Message", message: message, preferredStyle: .alert) let action = UIAlertAction(title: "OK", style: .cancel, handler: nil) alertController.addAction(action) present(alertController, animated: true, completion: nil) } |
筆記
- 問題:將 UIView 設定 alpha = 0 和 isHidden = true 是相同的嗎?(目前我認為是不同的,alpha = 0 依舊會被畫出來,而 isHidden = true 就不會觸發繪畫)
- TODO:在 SKExpandableView 中 setupButtons 的時候,可以根據拿到的 frame 來放置按鈕,取代 hard code.
參考
- 可以到 Github 上看對應的 Source Code