[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)
使いたい画面で上記のような実装をします。
見出しラベルと値表示をセットにしているので、色々と使いやすいと思います。
コメント
コメントを投稿