iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 4
0
Software Development

iOS Swift x Layout x Animation x Transition系列 第 4

Expandable Buttons - 折疊式按鈕

Expandable Buttons

在 Web 應用中,我們常常會看到畫面的右下角會提供一個按鈕,點下去後會展開更多的按鈕,用來提供一些功能(例如分享到 Facebook/Line/Weibo)

這次要在 iOS 中實現這個功能。


ExpandableButtons

藍色的按鈕負責顯示/隱藏其餘的四個按鈕。
https://ithelp.ithome.com.tw/upload/images/20171223/20107329TQ4hxIPeiU.png

SKExpandableView

為了讓這個折疊式按鈕可以被 reuse ,這裡建立一個繼承於 UIView 的 SKExpandableView.

https://ithelp.ithome.com.tw/upload/images/20171223/20107329E5shhDhgy3.png

排版的時候我們從上到下依次放入按鈕,功能按鈕通過一個 Array 來保管,藍色按鈕命名為 baseButton

並且給每個按鈕加入 touchUpInside 事件

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 事件的方法:

@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 方法,將功能性的按鈕通過動畫來隱藏或顯示。

private func switchExpandableStackView() {
    UIView.animate(withDuration: 0.3, animations: {
        _ = self.buttons.map{
            ($0.alpha == 0) ? ($0.alpha = 1) : ($0.alpha = 0)
        }
    })
}

Delegate

通過建立 Delegate 來讓調用者能夠得知哪一個功能性的按鈕觸發了 touchUpInside 事件

enum SKExpandableButtonType {
    case card
    case camera
    case box
    case book
}

protocol SKExpandableViewDelegate {
    func didTapExpandable(button:SKExpandableButtonType)
}

Example

在 HomeViewController 中加入 SKExpandableView 並設定 delegate 方法

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 方法

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 事件的時候跳出提示

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.

參考


上一篇
DrawingAnimation + CustomLoadingView
下一篇
ImageSlider - 圖片轉換動畫
系列文
iOS Swift x Layout x Animation x Transition30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
海綿寶寶
iT邦大神 1 級 ‧ 2017-12-23 14:41:36

請教一下
各篇文章中的手機畫面(video as .gif)
是用什麼工具/方式製作的?
/images/emoticon/emoticon41.gif

如果你是用 mac 的話,可以試試看 LICEcap 這個工具

我是用 mac
/images/emoticon/emoticon41.gif

0
陳董粉絲
iT邦新手 5 級 ‧ 2018-01-04 17:33:45

個人習慣不需要用到返回值會用foreach
_ = self.buttons.map{
($0.alpha == 0) ? ($0.alpha = 1) : ($0.alpha = 0)
}
2018唯一支持陳董

粉絲大人說得好

我要留言

立即登入留言