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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

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

Q&A

解決済

1回答

4003閲覧

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

退会済みユーザー

退会済みユーザー

総合スコア0

Swift

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

0グッド

0クリップ

投稿2017/08/01 05:44

編集2017/08/01 07:35

画面遷移先で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の方に持ってくる方法がわからなかった)ました。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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

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

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

swift

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

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

swift

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

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

投稿2017/08/01 06:02

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/08/01 06:32

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

2017/08/01 06:41

let dataManager = WeatherDataManager() ではなくself.dataManager.dataRequest() のことでしょうかね?というのは置いておいて、 APIリクエスト処理は非同期で行われていると思いますので、 ・リクエスト / Alamofire.request(url)... ・dataRequest() を抜ける ・レスポンス受信 / case .successの中(もしくはcase .failureの中) という順番で処理が実行されます。 print文をあちこちに仕込んで処理の流れを追ってみるといいです。
退会済みユーザー

退会済みユーザー

2017/08/01 07:40

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

2017/08/01 07:56

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

退会済みユーザー

2017/08/01 08:21

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

2017/08/01 08:31

手っ取り早いのは WeatherDataManager を使わないようにすることです。 APIリクエスト〜レスポンス受信〜画面の書き換え、全て CheckViewController の中に書きます。 一旦動くものを作った上で、delegateとかclosuresとかを勉強してみるのもいいかも知れません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問