ios開発をしていく中で、時間がかかる処理を実装した際、ローディング表示をしてユーザーのストレスを軽減したいと思うことがありました。ローディング画面の表示に苦労したので、備忘録として記事にしていきたいと思います。
そもそもなぜロード中表示を実装するのか
時間がかかる処理を行なっている時、処理中であることを画面に表示していないとユーザーはアプリが固まってしまった(処理が止まってしまった)のかなと思ってしまいます。
処理中であることを画面に表示させ、どれくらい処理が進んでいて、あとどれくらいの処理で終わるのかを表示させることでユーザーは待ってくれるようになります。
ユーザーに対してストレスを与え無いようにするため、今どれくらいの処理が進んでいて、後どれくらいで処理が終わるのか、進捗状況を表示するようにしましょう。
ローディング画面を実装するためのポイント
- 処理が止まっていないことを画面に表示する
- どれくらいの処理が終わって、あとどれくらいの処理が残っているのかを表示する
- 可能であれば処理の中断ができるようにする
実装
ローディング画面の実装は、UIKitを使って実装します。
SwiftUIで開発している場合でも、UIKitの併用は可能です。
UIKitを読み込んでいない場合は、UIKitの読み込みを行う必要があります。
import UIKit
UIKitのimportは上記コードを下記コードを記載するファイルの1行目に記載するだけで完了します。コマンド等でインストールする必要はありません。
全体のコード
func hoge() async {
do{
// ローディング表示は、メインスレッドで行う必要がある。
try await Task { @MainActor in
let loadingView = UIView(frame: UIScreen.main.bounds)
loadingView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
let activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
activityIndicator.center = loadingView.center
activityIndicator.color = UIColor.white
activityIndicator.style = UIActivityIndicatorView.Style.large
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
loadingView.addSubview(activityIndicator)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
label.center = CGPoint(x: activityIndicator.frame.origin.x + activityIndicator.frame.size.width / 2, y: activityIndicator.frame.origin.y + 90)
label.textColor = UIColor.white
label.textAlignment = .center
// 画面に表示するメッセージ
label.text = "ただいま処理中です...(1/5)"
loadingView.addSubview(label)
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
windowScene?.windows.first?.addSubview(loadingView)
// ここに時間のかかる処理を記載
// 処理がすぐに終わっても0.1秒だけロード中画面を表示(チカチカ防止のため)
try await Task.sleep(for: .seconds(0.1))
// ロード中画面を非表示にする
loadingView.removeFromSuperview()
}.value
} catch {
// ここに処理が途中で止まってしまった時のエラー処理を記載
// 例:エラーダイアログの表示
}
}
解説
上記コードの解説をします。
ローディング画面の表示処理はメインスレッドで
4行目の下記コードは、メインスレッドのみを使って処理するよう指示したコードとなります。
iosでは画面の更新(UIの更新)はメインスレッド以外のスレッドで実行することは許可されていません。そのため、ローディング画面の表示処理はメインスレッドで行うよう指示をしています。
try await Task { @MainActor in
ローディング画面は画面全体に表示
5行目の下記コードは、処理中に別画面へ遷移できてしまうとデータが壊れたりすることがあります。そのため、遷移できないよう画面サイズは操作している機器の画面サイズとなるように指定します。
let loadingView = UIView(frame: UIScreen.main.bounds)
ローディング画面の背景色設定
6行目の下記コードは、ローディング画面を表示していても直前の画面は透けて見えていた方が良いと考え、透明度50%で設定しています。また、他の操作ができないことを直感的にわかるよう色は黒色にしています。
黒色で透明度50%なのでグレーですね。
透明度はalpha:で指定します。
透明度の指定は、0.0(完全に透明)から1.0(完全に不透明)の間で指定します。
loadingView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
プログレス・インジケーターの表示
iPhoneやiPadでよく見る上記の処理中表示、実はUIKitに用意されています。
8行目から14行目の間でインジケーターの表示に関する設定を行っています。
let activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
activityIndicator.center = loadingView.center
activityIndicator.color = UIColor.white
activityIndicator.style = UIActivityIndicatorView.Style.large
activityIndicator.hidesWhenStopped = true
activityIndicator.startAnimating()
loadingView.addSubview(activityIndicator)
上記コードの詳しい解説は以下のページでおこなっています。
インジケーター下にメッセージ表示
16行目から22行目ではメッセージの表示に関する設定を行っています。
進捗状況をメッセージでお伝えする場合は、このメッセージを更新していく形となります。
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 30))
label.center = CGPoint(x: activityIndicator.frame.origin.x + activityIndicator.frame.size.width / 2, y: activityIndicator.frame.origin.y + 90)
label.textColor = UIColor.white
label.textAlignment = .center
// 画面に表示するメッセージ
label.text = "ただいま処理中です...(1/5)"
loadingView.addSubview(label)
ローディング画面の表示・非表示
ローディング画面の表示は、26行目で行っています。
windowScene?.windows.first?.addSubview(loadingView)
ローディング画面の非表示は、34行目で行っています。
loadingView.removeFromSuperview()
SwiftUIからの呼び出し
SwiftUIから呼び出す場合は、通常の関数を呼び出すような形で記載すれば問題ありません。
import SwiftUI
struct hogeView: View {
var body: some View {
Button {
try {
await hoge()
}
} label: {
Text("重い処理スタート")
}
}
}
コメント