UISwitchのあの絵のサイズが固定だったので、可変にしたいなぁと思いました。いつも通り、あちこち引っかかったのでメモを残しておきます。
#前提
- swift4.2
- Xcode10.3
- iOS10〜
- InterfaceBuilderを使ってSwitchは配置
- 固定サイズじゃなくて可変サイズで使いたい。
参考にさせていただいたのはここの記事です。
UISwitchを任意のサイズで使いたい!!
#いきなり答えに近いところまで
参考にさせてもらった記事で、UISwitchからクラスを派生して、InterfaceBuilderで置いたスイッチのクラスを差し替えてしまえばいい、と結論が出ていました。
#引っかかったところ
- InterfaceBuilderで設定したSwitchのサイズが取れない
(Constraintsで設定しているからかな) - 参考記事では固定サイズで試されているので、機種で違う画面サイズに合うように比率にしたい
- Transformすると画像の位置がずれるのだけれど、その調整
#Switchのサイズが取れないこと
- Classでself.frame.size.widthや、self.bounds.size.widthではオリジナルのサイズしかとれず、IBで設定しているものが反映されていない。
- ConstraintsのHeightAnchorやWidthAnchorから簡単に値は取れない、と思う。
こうやったらとれるよ、ってあるなら教えてほしいです…。
結論
- 画面サイズから計算することにしました。
#比率での大きさの決定
- IBでConstraintsを整えているのだけれど、固定値では指定していない。
Switch画像のサイズについては51,31。まずはこれは固定値なのは仕方ない。これが固定なのがそもそもの問題なので。
これがオリジナルのサイズということで、計算のベースになる。
- 普段も、Viewから比率で部品を積み上げていくという形にしているので、比率を活かせないか。
結論
- IBでSwitchを貼り付けるときに、最上位のView(画面サイズなので)からPropotional HeightやWidthで大きさを決めることにして、そのMultiplier(倍率)をメモしておきます。
- で、その倍率を画面サイズに掛けることで表示したいサイズを算出します。
値は@IBInspectableを利用してInterfaceBuilder上からその割合を入れるかたちにしました。
ちなみにIBから入力すると、小数点以下が丸められて0.05とか入れても0.1の表記になりますが、実際にPrintするとちゃんと値が取れてるのがわかります
Propotional HeightとPropotional Widthから自動でMultiplier引っ張って来れたらその手間も無いんですが、そもそも値って取れるの?という部分でちょっと時間が足りなかったので諦めました。詳しい方、やれるよ!ということであったらこれまた教えてもらえないかなーと思ってます。
#Transformしたときの位置の補正
これまた引っかかりました。
正直、前にもpinchgestureを使って拡大縮小しようとか思って一週間くらい悩んで位置補正がうまく出来なかった時期があったんですが、このあたりの勘違いが原因な気もしています。
- Transformでサイズを変えると中心が同じままで拡大縮小する
難しく考えてしまったんですかね。
オリジナルサイズでのセンターと表示サイズでの差分とか色々考えて設定してみてなんで駄目なん?となってたんですが。
結論
- (オリジナルサイズ - 表示したいサイズ) ÷ 2 だけオリジナルサイズで表示した状態からずれる
- その分だけ引けばちゃんとセンターに収まる
という結果でした。
#出来上がったUISwitchの派生クラス
名前は可変の意味でVarをつけただけです。
オリジナルサイズは本当はOS単位で変わるらしいので、OSを判定してサイズを渡すようにするのがベストなんだとは思いますが、割愛。
- InterfaceBuilderでUISwitchをViewControllerに貼り付け
- 貼り付けたSwitchのクラスをUISwitchから下記のクラスに変更
- 画面サイズと同じサイズのViewから計算した比率でConstraintsを設定
- Attribute Inspecterの下記のクラスのfScaleに上で設定したのと同じ比率で横と縦の比率を設定する
class VarUISwitch: UISwitch {
// iOS10 標準サイズが元になる
let orgSize: CGSize = CGSize(width: 51.0, height: 31.0)
// 倍率。IB上では丸められちゃうが、値は正しく入ってくる
@IBInspectable var fScale: CGSize = CGSize(width: 1.0, height: 1.0)
override func awakeFromNib() {
// awakeFromNibはIBで設定したもののみイベント発生
super.awakeFromNib()
// 表示サイズ。
let viewWidth = UIScreen.main.bounds.width * fScale.width
let viewHeight = UIScreen.main.bounds.height * fScale.height
// 拡大率 = 表示サイズ÷元のサイズ
let scaleX: CGFloat = viewWidth / orgSize.width
let scaleY: CGFloat = viewHeight / orgSize.height
// Transformすると中心が同じままなので変化した分だけ戻す。
// (元サイズ-表示サイズ)/2が足されるので、その分を引く。
let offsetX = ((orgSize.width - viewWidth) / 2) * -1
let offsetY = ((orgSize.height - viewHeight) / 2) * -1
// transform
let translateAff = CGAffineTransform(translationX: offsetX, y: offsetY)
// 移動が先にしないと駄目らしいので、移動にスケールを足す
self.transform = translateAff.concatenating(CGAffineTransform(scaleX: scaleX, y: scaleY))
}
}
ご参考までに。