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

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

ただいまの
回答率

90.47%

  • Swift

    8942questions

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

画面遷移先でAPIから取得した情報を表示させたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 816
退会済みユーザー

退会済みユーザー

画面遷移先でAPIから取得した情報を表示させたいのですが、今それができません。
MainStoryyboardの配置は
イメージ説明
です。右側の”ここでチェック!”のボタンを押すと、左側の画像に遷移して気温のところにAPIから取得した現在の気温を表示させたいです。
右側の画面にくっつけてある、ViewControllerには

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var weatherImage: UIImageView!


    @IBOutlet weak var checkButton: UIButton!

    let dataManager = WeatherDataManager()

    override func viewDidLoad() {
        super.viewDidLoad() 
        self.dataManager.dataRequest()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func checkButton(_ sender: Any) {
        let storyboard: UIStoryboard = self.storyboard!
        let nextView = storyboard.instantiateViewController(withIdentifier: "next") as! CheckViewController
        self.present(nextView, animated: true, completion: nil)

        if dataManager.weatherData?.weather == "Clouds" {
            weatherImage.image = UIImage(named: "cloud")
        } else if dataManager.weatherData?.weather == "Clear" {
            weatherImage.image = UIImage(named: "sunny")
        } else if dataManager.weatherData?.weather == "Rain" {
            weatherImage.image = UIImage(named: "rain")
        }

        //      print(WeatherDataModel(data:).dynamicTyp)
//        self.checkButton.isHidden = true

    }

}


と書きました。”ここでチェック!”を押したらcheckButtonメソッドが呼ばれ、Storyboardの左側の画面(CheckViewControllerがくっついている)に遷移します。しかし、左側の画像に遷移して気温のところにAPIから取得した現在の気温は表示されず、かつ if dataManager.weatherData?.weather 〜のif-else文が呼ばれず、天気ごとに表示させる画像を変える(表示させる画像はAssets.xcassetsに入っている)という部分ができません。
CheckViewControllerには、

import Foundation
import UIKit

class CheckViewController: UIViewController {

    @IBOutlet weak var tempLabel: UILabel!
    // APIリクエストや、レスポンスデータを利用するため  クラス インスタンス
    let dataManager = WeatherDataManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // ここでAPIリクエストを行う
        self.dataManager.dataRequest()

        // 気温 ラベルに取得した気温を表示させる
        tempLabel.text = dataManager.weatherData?.temp.description

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}


と書きました。

// 気温 ラベルに取得した気温を表示させる
        tempLabel.text = dataManager.weatherData?.temp.description


のコードがあるからラベルに現在の気温を表示させるところはできているはずなのですが、表示されない理由がわかりません。checkButtonメソッドに書いた

let storyboard: UIStoryboard = self.storyboard!
        let nextView = storyboard.instantiateViewController(withIdentifier: "next") as! CheckViewController
        self.present(nextView, animated: true, completion: nil)


のコードを書く場所を間違ったのでしょうか?
WeatherDataManager.swiftには

import Foundation

// 必要なフレームワークをインポートする 
import Alamofire
import SwiftyJSON

// AlamofireによるAPI通信を管理するクラスを定 義する
class WeatherDataManager: NSObject {
    // レスポンスデータをパースするモデルクラス イ ンスタンスを格納するプロパティ
    var weatherData: WeatherDataModel?
    // リクエストするURL
    let url = "http://api.openweathermap.org/data/2.5/forecast?units=metric&q=Tokyo&APPID=2ec06eb3d8b93310aa2773a10f1dafe4"

    // APIリクエストを実行する
    func dataRequest() {
        // AlamofireによるAPI通信 
        Alamofire.request(url).responseJSON { response in
        switch response.result {
        case .success(let value):
            // 通信成功時 処理
            // レスポンスデータをJSON型に変換する
            // これ SwiftyJSON ルール
            let json = JSON(value)
//            print("here")
//            print(json)
            // JSONデータを引数に渡してモデルクラス インスタンスを生成 
            self.weatherData = WeatherDataModel(data: json)
            // デバッグ用 ログ出力を行う
//            print(value)
        case .failure(let error): break // 通信失敗時 処理 // 今回 ログ出力だけ
        print(error)

        }
    }
}
}


と書きました。
WeatherDataModel.swiftには

import Foundation
// 必要なフレームワークをインポートする 
import SwiftyJSON

// SwiftyJSONによるパースを行うクラス 
class WeatherDataModel: NSObject {

    // 今日 天気(晴れ、雨等)を格納するプロパティ 
    var weather: String = ""
    // 気温を格納するプロパティ 
    var temp: Int = 0

    // weatherDataManagerクラスから初期化される 
    init?(data: JSON) {
    // 引数で渡ってきたJSONデータをここでパースする 
    // 今日 天気データを取得して変数に格納する
    self.weather = data["list"][0]["weather"][0]["main"].stringValue
    //気温データを取得して変数に格納する
    self.temp = data["list"][0]["main"]["temp"].intValue
}
}


と書きました。
fuzzballさんのコメントを受け、CheckViewControllerを以下のように書き換えました。

import Foundation
import UIKit


class CheckViewController: UIViewController {
    @IBOutlet weak var weatherImage: UIImageView!

    @IBOutlet weak var tempLabel: UILabel!
    // APIリクエストや、レスポンスデータを利用するため  クラス インスタンス
    let dataManager = WeatherDataManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        // ここでAPIリクエストを行う
        if dataManager.weatherData?.weather == "Clouds" {
            // 「Clouds」だったら「曇り」 画像を表示する
            weatherImage.image = UIImage(named: "cloud")
        } else if dataManager.weatherData?.weather == "Clear" {
            // 「Clear」だったら「晴れ」 画像を表示する
            weatherImage.image = UIImage(named: "sunny")
        } else if dataManager.weatherData?.weather == "Rain" {
            // 「Rain」だったら「雨」 画像を表示する
            weatherImage.image = UIImage(named: "rain")
        }

        self.dataManager.dataRequest()

        print("here")
        print(dataManager.weatherData)
        // 気温 ラベルに取得した気温を表示させる
        tempLabel.text = dataManager.weatherData?.temp.description

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}


このように書き換え実行すると

2017-08-01 15:23:03.412 WeatherApp[9709:864615] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<WeatherApp.ViewController 0x7fecff505a80> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key weatherImage.'


とエラーが出て落ちてしまいました。
WeatherDataManagerも書き換え、

import Foundation

// 必要なフレームワークをインポートする 
import Alamofire
import SwiftyJSON

// AlamofireによるAPI通信を管理するクラスを定 義する
class WeatherDataManager: NSObject {


    // レスポンスデータをパースするモデルクラス イ ンスタンスを格納するプロパティ
    var weatherData: WeatherDataModel?
    // リクエストするURL
    let url = "http://api.openweathermap.org/data/2.5/forecast?units=metric&q=Tokyo&APPID=2ec06eb3d8b93310aa2773a10f1dafe4"

    // APIリクエストを実行する
    func dataRequest() {
        // AlamofireによるAPI通信 
        Alamofire.request(url).responseJSON { response in
        switch response.result {
        case .success(let value):
            // 通信成功時 処理
            // レスポンスデータをJSON型に変換する
            // これ SwiftyJSON ルール
            let json = JSON(value)

            // JSONデータを引数に渡してモデルクラス インスタンスを生成 
            self.weatherData = WeatherDataModel(data: json)
        case .failure(let error): break // 通信失敗時 処理 // 今回 ログ出力だけ
        print(error)

        }
      }
   }
    // 気温 ラベルに取得した気温を表示させる
    tempLabel.text = dataManager.weatherData?.temp.description
}


としましたが、tempLabelがないと言われ(その通りなのですが。
しかし、CheckViewControllerに定義したものを WeatherDataManagerの方に持ってくる方法がわからなかった)ました。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

if dataManager.weatherData?.weather 〜のif-else文が呼ばれず

左側のViewControllerはCheckViewControllerなのですから、そのコードはCheckViewControllerに書かないといけません。

ラベルに現在の気温を表示させるところはできているはずなのですが、表示されない理由がわかりません。

APIリクエストした後、レスポンスを受け取る前にラベルの設定をしているからではないでしょうか?
レスポンスを受け取って、

//WeatherDataManager.swift
self.weatherData = WeatherDataModel(data: json)

が実行されるまでは、weatherDataはnilです。

//CheckViewController.swift
print(dataManager.weatherData)
tempLabel.text = dataManager.weatherData?.temp.description

とprint文を追加してみれば確認できると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/08/01 15:32

    ありがとうございます。質問文に書き換えた結果を追記しました。
    APIリクエストした後、レスポンスを受け取る前にラベルの設定をしているからではないでしょうか?⇨確かにその通りかもしれないと思いました。自分では、CheckViewControllerに書かれている
    let dataManager = WeatherDataManager()
    の後に tempLabel.text = dataManager.weatherData?.temp.description を書いたので、
    レスポンスを受け取った後にラベルの設定をしていたつもりなのですがその認識が間違っていましたか?

    キャンセル

  • 2017/08/01 15:41

    let dataManager = WeatherDataManager() ではなくself.dataManager.dataRequest() のことでしょうかね?というのは置いておいて、

    APIリクエスト処理は非同期で行われていると思いますので、
    ・リクエスト / Alamofire.request(url)...
    ・dataRequest() を抜ける
    ・レスポンス受信 / case .successの中(もしくはcase .failureの中)
    という順番で処理が実行されます。

    print文をあちこちに仕込んで処理の流れを追ってみるといいです。

    キャンセル

  • 2017/08/01 16:40

    色々なところにブレークポイントを置いてみたのですが、ViewController:の
    let dataManager = WeatherDataManager() の部分で落ちてしまっていることがわかりました。この一文を書く場所が悪かったのでしょうか?また、質問文に書いた通りWeatherDataManagerも書き換えましたが、tempLabelのエラーが出てしまいました。APIリクエスト処理後にtempLabel.text = dataManager.weatherData?.temp.descriptionを書くという意味ではなかったのでしょうか?

    キャンセル

  • 2017/08/01 16:56

    リクエストすらしていないのに dataManager.weatherData にアクセスしたら落ちて当然です。
    処理の流れは追ってみたのでしょうか?

    キャンセル

  • 2017/08/01 17:21

    はい、処理の流れは追いました。WeatherDataManager の
    self.weatherData = WeatherDataModel(data: json)
    print("666")
    case .failure(let error): break
    の間にかいた666が最終的に出力されました。でもprint("666")のところにtempLabel.text = dataManager.weatherData?.temp.description をかいてもtempLabelがないと言われ(当たり前なのですが)....。この先はどうやったらいいのでしょうか?

    キャンセル

  • 2017/08/01 17:31

    手っ取り早いのは WeatherDataManager を使わないようにすることです。
    APIリクエスト〜レスポンス受信〜画面の書き換え、全て CheckViewController の中に書きます。

    一旦動くものを作った上で、delegateとかclosuresとかを勉強してみるのもいいかも知れません。

    キャンセル

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

  • ただいまの回答率 90.47%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

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

  • Swift

    8942questions

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