MJHD

エモさ駆動開発

UIPickerViewに固定のラベルをつける[複数Component対応][iOS8対応]

よくこんなUIPickerViewを見かける。 f:id:wait0000:20150823201306p:plain iOS標準のタイマーに使われているUIPickerViewなのだが、iOS SDKには画像の「hours」「min」のようなラベルを追加する機能がない。

ならば作ってしまえ、というのが今回の記事。

方法としては、UIPickerViewにUILabelを追加する、という単純だがとても汚い方法。 ラベルの位置などハードコーディングになりがちだが、今回は、AutoLayoutにも対応できるよう工夫した。

まずは、UIPickerView, UIPickerViewDataSource, UIPickerViewDelegateを継承したクラスを作成する。

public class PickerLabelView : UIPickerView, UIPickerViewDataSource, UIPickerViewDelegate {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }
    public required init(coder aDecoder: NSCoder) {
        super.init(coder:aDecoder)
        self.setup()
    }
    
    func setup() {
        self.delegate = self
        self.dataSource = self
    }
    
        // UIPickerViewDataSource protocol
    public func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
        return 3
    }
    
    public func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 5
    }
    
    // UIPickerViewDelegate protocol
    public func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
        return row.description
    }
}

次に、以下のreloadAllComponentLabels関数(名前はなんでもいい)を作成する。

  // ラベルの配置
    private let labelText = ["hours", "mins", "sec"] // ラベルのテキスト
    private var labels :[UILabel] = []
    public func reloadAllComponentLabels() {
        
        let fontSize = UIFont.systemFontSize()
        
        let labelTop = self.bounds.origin.y + self.bounds.height / 2 - fontSize // 選択部分のY座標
        let labelHeight = self.rowSizeForComponent(0).height // 選択部分の高さ
        var labelOffset = self.bounds.origin.x // Componentの右端
        
        // Compoentの数だけ、ラベルを更新
        for i in 0...(self.numberOfComponentsInPickerView(self) - 1) {
        
            // ラベルが初期化されていなければ、作成
            if self.labels.count == i {
                var label = UILabel()
                label.text = labelText[i] // テキストを設定
                label.backgroundColor = UIColor.clearColor()
                label.font = UIFont.boldSystemFontOfSize(fontSize)
                label.sizeToFit()
                
                self.addSubview(label)
                self.labels.append(label)
            }
            
            // ラベルの位置を決める
            let labelWidth = self.labels[i].frame.width
            labelOffset += self.rowSizeForComponent(i).width
            
            self.labels[i].frame = CGRect(x: labelOffset - labelWidth, y: labelTop, width: labelWidth, height: labelHeight)
        
        }
    }

位置決めは、X座標は、Componentの右端座標から文字幅引いたもの、Y座標はUIPickerViewのbounds.heightの半分からフォントサイズを引いたものとなっている。これで大体、どのデバイスでも位置が合う。

最後に、以下のようにlayoutSubviews関数をオーバーライドする。

    // レイアウトの変更時にラベルも更新
    public override func layoutSubviews() {
        super.layoutSubviews()
        self.reloadAllComponentLabels()
    }

これで綺麗なラベル付きUIPickerViewが完成する。

f:id:wait0000:20150823201317p:plain