質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.32%

  • Swift

    7694questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • Xcode

    4353questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

  • iOS

    4191questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • MacOS(OSX)

    2041questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

メインスレッドを停止させて処理を待たせたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 247

pipipipipi

score 28

private func getCategories(){
        Alamofire.request(Constant.Url.categoryUrl, method: .get, encoding: JSONEncoding.default).responseJSON { response in
            switch response.result {
            case .success:
                guard let object = response.result.value else { return }
                let json:JSON = JSON(object)
                json.forEach { (_, json) in
                    self.categories.append(json["name"].stringValue)
                    self.categoriesId.append(json["id"].stringValue)
                }
            case .failure:
                print("error")
            }
        }
    }
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
        var articles: [ArticleViewController] = []
        getCategories()
        for i in 0..<categories.count {
            articles.append(ArticleViewController())
            articles[i].categoryInfo = IndicatorInfo(title: categories[i])
        }
        return articles
    }

上記のようなコードで、getCategoriesはAlamofireを使用してapiを叩いて、非同期でデータを取得しています(Alamofireの仕様で非同期になる)。この処理が全て終わってからfor 以下の処理を実行したいのですが、どのようなコードを書けば良いでしょうか?
responseJSONのブロック内と、その外側が同じスレッドで処理されるため、waitすると、止まってしまう。ということもあり、上手くいっていません…。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

データを非同期で取得しているのだから、データを取得するまでメインスレッドの処理を停止させるのではなく、データを取得したタイミングで処理を呼び出してもらい、そこで受信データをもとにした処理を行う必要があります。

おそらくこの質問は、「XLPagerTabStripで決められているviewControllers()メソッドをoverrideして、ViewControllerの配列を返却しなければならないのだが、非同期にデータを受信したタイミングでViewControllerの配列が決まるのに、どうやったらデータを受信したタイミングで、viewControllers()メソッドの戻りを返却できるのかがわからない」という質問なのだと思います。

その答えを解く鍵は reloadPagerTabStripView()メソッドです。
これを呼び出せば viewControllers()メソッドを呼びだしてもらえるので、
データを受信した状態で最終的に作成されたViewControllerの配列を返せると思います。

具体的には、
ViewControllerの配列である

var articles: [ArticleViewController] = []


をプロパティとして定義し、

responseJSONのクロージャの内でself.categoriesを作成した続きで
(1)ViewControllerの配列(articles)作成
(2)self.reloadPagerTabStripView()呼び出し
を実施し、

viewControllers()メソッドの処理は

return articles


とするだけでよさそうに思います。


(7/2 0:37 サンプルコード追加)

    var articles: [ArticleViewController] = [ArticleViewController()]

    override func viewDidLoad() {
        super.viewDidLoad()
        getCategories()
    }

    private func getCategories() {
        Alamofire.request(Constant.Url.categoryUrl, method: .get, encoding: JSONEncoding.default).responseJSON { response in
            switch response.result {
            case .success:
                guard let object = response.result.value else { return }
                let json:JSON = JSON(object)
                json.forEach { (_, json) in
                    self.categories.append(json["name"].stringValue)
                    self.categoriesId.append(json["id"].stringValue)
                }
                self.articles = []
                for i in 0..<self.categories.count {
                    self.articles.append(ArticleViewController())
                    self.articles[i].categoryInfo = IndicatorInfo(title: self.categories[i])
                }
                self.reloadPagerTabStripView()
            case .failure:
                print("error")
            }
        }
    }

    override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
        return articles
    }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/01 23:26

    回答ありがとうございます。
    reloadPagerTabStripView()は知りませんでした。
    上記のように実装すると、やはり非同期でgetCategories()のメソッドが実行されるので、どうしても return articlesが先に実行されて、空の状態になってしまうと思うのですが…

    キャンセル

  • 2018/07/01 23:46 編集

    このViewControllerが起動されると最初に`viewControllers()`が呼ばれ、そのタイミングでは`return articles`で個数0の配列が返ります。
    しかしその後、responseJSONのクロージャが呼ばれ、その中でarticlesを生成して`self.reloadPagerTabStripView()`を実行すると、再度`viewControllers()`が呼ばれます。そのタイミングで正しい配列が返せるので、それで意図した個数のタブが表示されると思います。データを受信したタイミングでロードし直せばよいということです。

    キャンセル

  • 2018/07/01 23:54

    一度return 0としてしまうとライブラリの設定上エラーが返ってきてしまい、それ以降の処理に行き着きません。
    callbackなどを使用して上手くできないでしょうか?

    キャンセル

  • 2018/07/01 23:55

    どういうエラーがどこに返ってきて、どのようにエラーが表示されているんですか?

    キャンセル

  • 2018/07/02 00:07

    ```
    super.viewDidLoad() //Thread 1: Fatal error: viewControllers(for:) should provide at least one child view controller
    }

    private func getCategories() -> [ArticleViewController] {
    var articles: [ArticleViewController] = []
    Alamofire.request(Constant.Url.categoryUrl, method: .get, encoding: JSONEncoding.default).responseJSON { response in
    switch response.result {
    case .success:
    guard let object = response.result.value else { return }
    let json:JSON = JSON(object)
    json.forEach { (_, json) in
    self.categories.append(json["name"].stringValue)
    self.categoriesId.append(json["id"].stringValue)
    }
    // var articles: [ArticleViewController] = []
    for i in 0..<self.categories.count {
    articles.append(ArticleViewController())
    articles[i].categoryInfo = IndicatorInfo(title: self.categories[i])
    }
    self.reloadPagerTabStripView()
    case .failure:
    print("error")
    }
    }
    return articles
    }

    override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {

    // var articles: [ArticleViewController] = []
    return getCategories()
    }
    }
    ```

    となっています。よろしくお願いします。

    キャンセル

  • 2018/07/02 00:40 編集

    なるほど。
    「viewControllers(for:) should provide at least one child view controller」
    と出ているので、ViewControllerは最低限1個ないとエラーになるのですね。
    だったら、最初は1個だけダミーのArticleViewControllerを入れておいて、
    データを受信したら正しいものに入れ替えたらいいと思います。

    あと、私が説明したことといろいろ違うことをしているので、
    回答にサンプルコードを追加しておきました。

    キャンセル

  • 2018/07/02 22:33

    返信が遅くなってしまい、申し訳ございません。
    サンプルコードまで載せていただき、ありがとうございます。上記をを実行したところ。上手くいきました。本当にありがとうございます。またなにかあればよろしくお願いします。

    キャンセル

同じタグがついた質問を見る

  • Swift

    7694questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • Xcode

    4353questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

  • iOS

    4191questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • MacOS(OSX)

    2041questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。