既に回答がついていますが、ちょっと気になったので。
ここで考えられる UI 的な対応は2つあるかと思います。
- ピンをタップした瞬間に FPC で新しいモーダルビューを出し、データが到着した瞬間に更新する
- ピンをタップしたあと、データが到着するまでインジゲータ(HUD, Heads-up display)を出し、到着したら FPC を表示する
前者については、既に実装されている通りかと思います。
不格好に見える理由は、「読み込み中」と「読み込み後」のデザインが異なるからだと思います。
「箱根峠」をタップしたのに初期表示が「知床峠」となっているのは、UI 的には不親切かと思います。
これは、FPC で表示させる初期表示(おそらく、UITableView のヘッダ)を「Now loading...」など、「現在読み込んでいます」というのがわかるような表示にすること、また UITableView のバックグラウンド色を統一する(読み込みの前後で同じ色にする)などで解決するかと思います(色の変化については、あえて変化させるという手法を取る考え方もあります)。
後者については、Takumibooさんがご指摘のとおり、読み込み中のインジゲータを表示することで実現できます。
インジゲータについては色々あるようですが、使いやすいのは PKHUD というフレームワークかもしれません。
ただ、両者について、現在見えている範囲での実装で言えることは、「非同期処理を行なっていないのではないか」ということです。
つまり、DB にアクセスしても情報が届くのは FPC で表示する後になるため、そのタイムラグを考慮した実装にする必要があるということです。
なので、たとえば実装としてはこのような実装が考えられます。
Swift
1import UIKit
2import FloatingPanel
3// MARK: PKHUD
4// https://github.com/pkluz/PKHUD
5import PKHUD
6
7class ViewController: UIViewController {
8 var fpc: FloatingPanelController?
9
10 // 中略
11
12 // マップピンをタップしたと仮定して...
13 @IBAction func buttonPushed(_ sender: UIButton) {
14 guard let customVC = storyboard?.instantiateViewController(identifier: "fpc_content") as? PopupViewController else {
15 print(#function)
16 return
17 }
18
19 // MARK: - 二重起動防止
20 // 既に表示されていれば一度消す
21 if let fpc = fpc {
22 // Inform the panel controller that it will be removed from the hierarchy.
23 fpc.willMove(toParent: nil)
24
25 // Hide the floating panel.
26 fpc.hide(animated: true) {
27 // Remove the floating panel view from your controller's view.
28 fpc.view.removeFromSuperview()
29 // Remove the floating panel controller from the controller hierarchy.
30 fpc.removeFromParent()
31 }
32 }
33
34 fpc = FloatingPanelController()
35
36 // MARK: - HUD を表示
37 HUD.show(.labeledProgress(title: "Loading...", subtitle: nil))
38
39 // MARK: - loadInfo に読み込み完了後に処理したいクロージャを渡す
40 //データを読み込む操作
41 customVC.loadInfo(completionHandler: {
42 // HUD を消す
43 HUD.flash(.success, delay: 0.5)
44
45 // FPC 関連の処理
46 self.fpc?.set(contentViewController: customVC)
47
48 // swipe down
49 self.fpc?.isRemovalInteractionEnabled = true
50
51 self.fpc?.addPanel(toParent: self)
52 self.fpc?.move(to: .half, animated: true)
53 self.fpc?.track(scrollView: customVC.tableView)
54 })
55
56 // MARK: - HUDが消えた後に FPC を出すのであれば...
57 // HUD の completionHandler に FPC 関連の処理を記述する
58 //データを読み込む操作
59// customVC.loadInfo(completionHandler: {
60// // HUD を消す
61// HUD.flash(.success, onView: nil, delay: 0.5) { _ in
62// // FPC 関連の処理
63// self.fpc?.set(contentViewController: customVC)
64//
65// // swipe down
66// self.fpc?.isRemovalInteractionEnabled = true
67//
68// self.fpc?.addPanel(toParent: self)
69// self.fpc?.move(to: .half, animated: true)
70// self.fpc?.track(scrollView: customVC.tableView)
71// }
72// })
73 }
74 // 後略
75}
FPCで表示する側の処理(loadInfo(completionHandler:)
)側の実装はこんな感じです。
Swift
1 // MARK: - クロージャを受け取るように変更
2 func loadInfo(completionHandler: @escaping () -> ()) {
3 // DispatchQueue は非同期処理を模擬しているだけ。実際は DB へのアクセスなどになる
4 DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
5 self.data = [Int](10...30).map { String($0) }
6 self.sectionTitle = "読み込み完了"
7
8 DispatchQueue.main.async {
9 completionHandler()
10 }
11 }
12 }
上記の実装であれば、たとえばデータが読み込めたあとに FPC で表示させることも可能です(もちろん、先に FPC を表示させる場合であっても、非同期処理については考慮する必要があります)。
上記の実装は、あくまでも「データは必ず到着する」という前提での実装ですが、当然ネットワークトラブルなどでデータが到着しない、タイムアウトする、データが壊れているというエラーが発生することも考えられるので、そのような場合には、completionHandler:
に加えて、errorHandler:
も渡してエラー時の処理を記述する方が当然良いかと思います。
###2020/10/07追記
ただ、両者について、現在見えている範囲での実装で言えることは、「非同期処理を行なっていないのではないか」ということです。
表現が適切でない可能性がありましたので訂正します。
『非同期処理の「逐次処理」を行なっていないのではないか』の方が正確かもしれません。
本来の流れとしては、
loadInfo()
で標高データなどを取り込む
- 取り込みが完了した時点で tableView を表示する
- 表示したデータで tableView を更新する
が望ましい形かと思うのですが、おそらく今回の実装では
loadInfo()
で標高データなどを取り込む(かつ、おそらくデータ取得完了の時点で reloadData()
を実行している)
- 仮のデータで tableView を更新する
- 結果として、tableView が表示されたあとに最新情報で更新される
という流れになってしまっているのだと思います。
DispatchQueue
を追加したがうまくいかなかった、というのは、おそらく非同期処理を逐次的に処理させたかったのだと推測されますが、当初の実装ではうまくいっていないのだと思われます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/10/07 10:31
2020/10/07 11:00