はじめに
iOS 13 がリリースされましたね。
例年になく大きな変更があり,皆さん様々な対応を行ったことと思います。
私も主に個人アプリで対応してギリギリ 9/20 にリリースできました。
今回はその iOS 13 対応中に見つけたクラッシュとその対応の記事になります。
iOS 13 で UIScrollView
の Indicator の仕様が変わった?
UIScrollView
の Indicator ってありますよね。
スクロールした際に右端に表示されるもの(今回の話は Vertical Indicator の方)です。
そのスクロールバーに下記の画像を設定していました。
iOS 12 までは下記のようなコードで動いていました。
(今見ると恐いな・・・😓)
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let verticalScrollIndicator: UIImageView = (scrollView.subviews[(scrollView.subviews.count - 1)] as! UIImageView)
verticalScrollIndicator.image = UIImage(named: "scrollBarImage")
}
}
しかし,このコードだと iOS 13 の端末でスクロールしようとしたらクラッシュします。
クラッシュした際のログは下記の通りです。
Could not cast value of type '_UIScrollViewScrollIndicator' (0x7fff895ad0b0) to 'UIImageView' (0x7fff895cde00).
UIImageView
で受けられなくなっているようで,
View Hierarchy で確認してみると・・・
iOS 12 の場合は,インジケータは UIImageView
になっている。
こいつを取得して画像をセットしている。
iOS 13 端末だと UIView
になっている。
なるほど UIImageView
でキャストできずクラッシュするわけだ。
iOS 13 での対応
- Xcode 11
- iOS 12, 13
サンプルコードは GitHub にあります。気になる方はご覧ください。
https://github.com/MilanistaDev/ScrollBarWithImage
UIView
になっているなら,
単純に UIImageView
を addSubView
すればいいと考えました。
AutoLayout も使って Indicator いっぱいに画像を表示させます。
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// スクロールバーに画像をセット
if let verticalScrollIndicator: UIView = scrollView.subviews.last {
let scrollBarImageView = UIImageView.init(frame: .zero)
scrollBarImageView.contentMode = .scaleToFill
scrollBarImageView.image = UIImage(named: "scrollBarImage")
scrollBarImageView.translatesAutoresizingMaskIntoConstraints = false
verticalScrollIndicator.addSubview(scrollBarImageView)
// AutoLayout で ScrollBar いっぱいに画像を設置
scrollBarImageView.leadingAnchor.constraint(
equalTo: verticalScrollIndicator.leadingAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.bottomAnchor.constraint(
equalTo: verticalScrollIndicator.bottomAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.trailingAnchor.constraint(
equalTo: verticalScrollIndicator.trailingAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.topAnchor.constraint(
equalTo: verticalScrollIndicator.topAnchor,
constant: 0.0
).isActive = true
}
}
}
これでうまくいきますが,このままだと大変なことになっているだろうなと🤔
画面を見てもわからないけど,やはりビーム出してますね。。。
addSubView された画像を都度削除するために,
Indicator の subViews を削除する処理を入れます。
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// スクロールバーに画像をセット
if let verticalScrollIndicator: UIView = scrollView.subviews.last {
// 追加した画像があれば削除
let subviews = verticalScrollIndicator.subviews
for subview in subviews {
subview.removeFromSuperview()
}
let scrollBarImageView = UIImageView.init(frame: .zero)
scrollBarImageView.contentMode = .scaleToFill
scrollBarImageView.image = UIImage(named: "scrollBarImage")
scrollBarImageView.translatesAutoresizingMaskIntoConstraints = false
verticalScrollIndicator.addSubview(scrollBarImageView)
// ScrollBar いっぱいに画像を設置
scrollBarImageView.leadingAnchor.constraint(
equalTo: verticalScrollIndicator.leadingAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.bottomAnchor.constraint(
equalTo: verticalScrollIndicator.bottomAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.trailingAnchor.constraint(
equalTo: verticalScrollIndicator.trailingAnchor,
constant: 0.0
).isActive = true
scrollBarImageView.topAnchor.constraint(
equalTo: verticalScrollIndicator.topAnchor,
constant: 0.0
).isActive = true
}
}
}
これでうまくいきました!
(おまけ)Indicator の BackgroundColor を設定する?
今回は画像を addSubView しているので,
ライトモードでもダークモードでも同じ色になります(私はこうしたかった)。
少し色味を変えたいのであれば,
アセットでライトモード用の画像とダークモード用の画像を用意すればいいですね。
ライトモード | ダークモード |
---|---|
単色などであれば,取得した Indicator の UIView
の
BackgroundColor を設定すれば良いとも思うんですが,
どうも UIScrollViewIndicatorStyle
の設定があるためか綺麗な色が出せません。
単純に設定しない .default
だと
ライトモード では .black
,ダークモード では .white
が設定されているように見えます。
試しに UIColor.red
を設定してみたのが下記になります。
ライトモード | ダークモード |
---|---|
UIScrollView
の Indicator を触るような実装をすることは
ほとんどないとは思いますが,画像で対応したほうが綺麗に対応できると思いました。
おわりに
今回は,iOS 13 対応中に出た UIScrollView の Indicator 周りのバグについて書きました。
iOS 13 単体の不具合が多い気がします。iOS 13.1 のリリースが待たれますね。
こうしたほうが良い,私はこうしてるよー等ありましたらご教示お願いいたします🙇♀️
ご覧いただきありがとうございました。