[Swift] UISliderをカスタマイズしてみる
iOSの標準コントロールに、UISliderというのがありますが、現在の値を表示したいと思ったことはないでしょうか。
プロパティを探してもそれらしいものが見つけられなかったので、UISliderを継承してオブジェクトを作ってみました。
UISlider
今回作成するオブジェクト
sizeThatFitsをオーバーライドして、計算後の描画サイズを返します。
ここでは、見出しラベルに値がセットされている場合は、ラベルの高さ+上下余白(8 x 2)を高さとして返します。
ラベルがない場合は、加算値が0になるため、変更なしとなります。
trackRectをオーバーライドして、バーの表示エリアのサイズを返します。
ここでは、見出しラベル表示分だけ下にずらしてます。(見出しラベルがなければ何もせずそのまま)
表示幅は、右端の値表示エリア分だけ少なくしてます。(初期値は60)
setup内でスライド値の表示ラベル、見出しラベルを配置してます。
updateLabelでスライダ値をラベルテキストにセットしてます。 スライダ値の表示を少数以下2桁表示に固定するため、フォーマットを設定してます。 また、ステップ値を設定している場合は、スライド値をステップ値単位に丸める計算をしています。
updateLabelは、layoutSubviews(オーバーライド)とvalueChangeのイベントから呼び出してます。
使いたい画面で上記のような実装をします。
見出しラベルと値表示をセットにしているので、色々と使いやすいと思います。
プロパティを探してもそれらしいものが見つけられなかったので、UISliderを継承してオブジェクトを作ってみました。
UISlider
今回作成するオブジェクト
環境
MacOSX 10.14
Xcode 10.1
Swift 4
Target iOS 9.0
実現する内容
- スライダーの値を表示する
- スライダーの上に見出し(タイトル)を表示する
- 増加量(ステップ量)を指定できるようにする
実装
SUISlider
UISliderを継承して、SUISliderを作成します。
ソースの全体像は下記の通りです。
ソースの全体像は下記の通りです。
import UIKit public class SUISlider : UISlider { var labelTitle: UILabel var valueWidth: CGFloat = 60 var stepValue : Float = 0 private var labelValue: UILabel private let SPACING: CGFloat = 8 public override init(frame: CGRect) { labelTitle = UILabel() labelValue = UILabel() labelValue.textAlignment = NSTextAlignment.right super.init(frame: frame) self.addSubview(labelTitle) self.addSubview(labelValue) self.addTarget(self, action: #selector(self.onValueChanged(sender: )), for: .valueChanged) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setup() { let pos = CGPoint(x: self.frame.width - valueWidth - SPACING, y: (self.layer.bounds.origin.y) ) labelValue.frame = CGRect(x: pos.x , y: pos.y , width: valueWidth, height: self.layer.frame.height ) let sz = labelTitle.sizeThatFits(self.frame.size) labelTitle.frame = CGRect(x: SPACING, y: SPACING, width: sz.width, height: sz.height) } override public func sizeThatFits(_ size: CGSize) -> CGSize { var szLayer = super.sizeThatFits(size) let szTitle = labelTitle.sizeThatFits(size) if szTitle.height > 0 { szLayer.height = szLayer.height + SPACING * 2 } szLayer.height = szLayer.height + szTitle.height return szLayer } public override func layoutSubviews() { updateLabel() setup() super.layoutSubviews() } override public func trackRect(forBounds bounds: CGRect) -> CGRect { let sz = labelTitle.sizeThatFits(self.frame.size) var frame = super.trackRect(forBounds: bounds) frame.size.width = frame.width - valueWidth frame.size.height = 5 if sz.height > 0 { frame.origin.y = sz.height + SPACING + (super.bounds.height - frame.size.height - (SPACING * 2 + sz.height))/2 } else { frame.origin.y = super.bounds.midY } return frame } func updateLabel(){ if stepValue > 0 { let intg : Int = Int(self.value / stepValue) self.value = Float(intg) * stepValue } labelValue.text = String(format: "%0.2f", self.value ) } @objc func onValueChanged(sender: SUISlider) { updateLabel() } }
解説
レイアウト
コード
override public func sizeThatFits(_ size: CGSize) -> CGSize { var szLayer = super.sizeThatFits(size) let szTitle = labelTitle.sizeThatFits(size) if szTitle.height > 0 { szLayer.height = szLayer.height + SPACING * 2 } szLayer.height = szLayer.height + szTitle.height return szLayer }
sizeThatFitsをオーバーライドして、計算後の描画サイズを返します。
ここでは、見出しラベルに値がセットされている場合は、ラベルの高さ+上下余白(8 x 2)を高さとして返します。
ラベルがない場合は、加算値が0になるため、変更なしとなります。
override public func trackRect(forBounds bounds: CGRect) -> CGRect { let sz = labelTitle.sizeThatFits(self.frame.size) var frame = super.trackRect(forBounds: bounds) frame.size.width = frame.width - valueWidth frame.size.height = 5 if sz.height > 0 { frame.origin.y = sz.height + SPACING + (super.bounds.height - frame.size.height - (SPACING * 2 + sz.height))/2 } else { frame.origin.y = super.bounds.midY } return frame }
trackRectをオーバーライドして、バーの表示エリアのサイズを返します。
ここでは、見出しラベル表示分だけ下にずらしてます。(見出しラベルがなければ何もせずそのまま)
表示幅は、右端の値表示エリア分だけ少なくしてます。(初期値は60)
func setup() { let pos = CGPoint(x: self.frame.width - valueWidth - SPACING, y: (self.layer.bounds.origin.y) ) labelValue.frame = CGRect(x: pos.x , y: pos.y , width: valueWidth, height: self.layer.frame.height ) let sz = labelTitle.sizeThatFits(self.frame.size) labelTitle.frame = CGRect(x: SPACING, y: SPACING, width: sz.width, height: sz.height) }
setup内でスライド値の表示ラベル、見出しラベルを配置してます。
func updateLabel(){ if stepValue > 0 { let intg : Int = Int(self.value / stepValue) self.value = Float(intg) * stepValue } labelValue.text = String(format: "%0.2f", self.value ) }
updateLabelでスライダ値をラベルテキストにセットしてます。 スライダ値の表示を少数以下2桁表示に固定するため、フォーマットを設定してます。 また、ステップ値を設定している場合は、スライド値をステップ値単位に丸める計算をしています。
updateLabelは、layoutSubviews(オーバーライド)とvalueChangeのイベントから呼び出してます。
使い方
let sld1 = SUISlider() sld1.minimumValue = 0.0 sld1.maximumValue = 1.0 sld1.value = 0.5 sld1.stepValue = 0.1 sld1.backgroundColor = UIColor.lightText sld1.labelTitle.text = "Test" let sz1 = sld1.sizeThatFits((self.view?.bounds.size)!) sld1.frame = CGRect(x:0,y:100, width:self.view.bounds.width, height:sz1.height) self.view.addSubview(sld1)
使いたい画面で上記のような実装をします。
見出しラベルと値表示をセットにしているので、色々と使いやすいと思います。
コメント
コメントを投稿