UIPickerView (ドラムみたいなやつ) を下からピュッと出す


要旨

 UIViewに載せてUIView.animateWithDurationすれば、下からでも上からでも出せます。
 なお実際にやってみたら面倒だったのでサブクラス化しました。

UIPickerView

 複数の選択肢を選ぶときに使う、iOSユーザーおなじみのドラムみたいなやつ。あれがピッカービューです (以下ドラム)。

 UITextFieldやUILabelをタップするとドラムが下から出てきて選択できる… というものを作ろうと思ったのですが、いざやってみると思ったより面倒くさいです。なぜならiOSには「ドラムを下からピュッと出す機能」はないので自力で作らねばならないのです。

 実際にやるべき内容は「1つのUIViewに動かしたいものを載せてアニメーションで動かす」というシンプルなものですが、実際に書いてみると相応に面倒くさいです。このままでは次回また使うときに泣きながら再度調べることになりかねないので、ひとまとめにしてUIViewのサブクラスにしました。本稿はその使い方と、詰まったところのメモです。

PopUpPickerView

説明

「キャンセルボタンを押すとリセットされる」「完了ボタンを押すとデリゲートを使う」の二点以外は、UIViewに部品を載せただけのクラスです。特に便利でもありませんし、凝ったことはできないので適当に改修してください。

用法

 インスタンスを適当なUIViewにaddSubviewすれば動きます。ただし、UIPickerViewを使うのでデリゲートの設定が常に必要です。
 ViewControllerにこれだけ書けば動く… はずです。たぶん。

class ViewController: UIViewController, PopUpPickerViewDelegate {
    var pickerView: PopUpPickerView!
    let array = ["test1", "test2", "test3", "test4", "test5"]

    override func viewDidLoad() {
        super.viewDidLoad()

        pickerView = PopUpPickerView()
        pickerView.delegate = self
        if let window = UIApplication.sharedApplication().keyWindow {
            window.addSubview(pickerView)
        } else {
            self.view.addSubview(pickerView)
        }

        let button = UIButton.buttonWithType(UIButtonType.ContactAdd) as UIButton
        button.frame = CGRectMake(50, 100, 30, 30);
        button.addTarget(self, action: "showPicker", forControlEvents: UIControlEvents.TouchDown)
        UIApplication.sharedApplication().keyWindow!.addSubview(pickerViewTester)`
    }
    func showPicker() {
        pickerView.showPicker()
    }
    // for delegate
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 3
    }
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return array.count
    }
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        return array[row]
    }
    func pickerView(pickerView: UIPickerView, didSelect numbers: [Int]) {
        println(numbers)
    }
}

メモ

UIPickerViewDataSource を継承してないのに動く

 デリゲートだけ設定しているのに、なぜか

  • func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
  • func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int

が動きます。内部でデリゲート・データソースに同じ操作を試行しているんでしょうか…?

アニメーションは括弧の外に書ける (ことがある)

 swiftでは、最後の引数がクロージャなら括弧の外に書けるというルールがあります。これを使えれば、アニメーションを書くのがちょっとだけ楽になります。

UIView.animateWithDuration(0.2) {
    self.frame = CGRectMake(0, screenSize.height, screenSize.width, 260.0)
}

無理にStoryboardを使わない

 コードで書くほうが楽なこともありますね、ということで。