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

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

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

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

Swift

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

Q&A

解決済

1回答

617閲覧

Picker Viewで得た値を他のクラスに渡せない

testyoutatsu

総合スコア29

Xcode

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

Swift

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

0グッド

0クリップ

投稿2018/08/01 03:56

編集2018/08/01 05:48

前提・実現したいこと

プログラミング初心者です。タイトルや内容が見当違いかもしれません。

PickerViewで選択された値を他のクラスへ渡したいのですが、以下のようなエラーが出てしまいます。
どのようにしたら他のクラスに値を渡すことができるでしょうか?
またResultsViewControllerクラスの「var test = SelectController()」を省略した記述の仕方はありますでしょうか?
基本的なことではありますが、回答よろしくお願いします。

質問するためにコードは省略、一部変更してます。

発生している問題・エラーメッセージ

Value of type 'SelectViewController' has no member 'selectedCurrencyPair'

該当のソースコード

Swift

1import UIKit 2 3class SelectViewController: UIViewController, UIPickerViewDelegate,UIPickerViewDataSource { 4 5 @IBOutlet weak var currencyPairPicker: UIPickerView! 6 7 let currencyPair = ["USDJPY","EURJPY","GBPJPY","AUDJPY","EURUSD"] 8 9 10 override func viewDidLoad() { 11 super.viewDidLoad() 12 // Do any additional setup after loading the view, typically from a nib. 13 14 currencyPairPicker.delegate = self; 15 currencyPairPicker.dataSource = self; 16 currencyPairPicker.showsSelectionIndicator = true; 17 18 // はじめに表示する項目を指定 19 currencyPairPicker.selectRow(1, inComponent: 0, animated: true) 20 21 } 22 23 // 表示する列数 24 func numberOfComponents(in pickerView: UIPickerView) -> Int { 25 return 1 26 } 27 28 // アイテム表示個数を返す 29 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 30 return currencyPair.count 31 } 32 33 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 34 // 選択時の処理 35 print(currencyPair[row]) 36 37 let selectedCurrencyPair = currencyPair[row] as String 38 39 40 } 41 42 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 43 // 表示する文字列を返す 44 return currencyPair[row] 45 46 } 47} 48 49 50 51class ResultsViewController: UIViewController { 52 53 override func viewDidLoad() { 54 super.viewDidLoad() 55 56 var test = SelectViewController() 57 58 print(test.selectedCurrencyPair) //ここでエラー 59 60 61 } 62} 63

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

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

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

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

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

xAxis

2018/08/01 04:38

SelectViewControllerの画面遷移先がResultViewControllerということですかね?
fuzzball

2018/08/01 04:51

「クラスの継承」だと全然違う意味になるので、「Picker Viewで得た値を他のクラスに渡せない」とかにして下さい。
testyoutatsu

2018/08/01 05:10

ご指摘ありがとうございます。訂正しました。
testyoutatsu

2018/08/01 05:12

はい、storyboardでSelectViewControllerからボタンでResultViewControllerに遷移させています。
xAxis

2018/08/01 06:50

fullballさんのタイトルの方がより適したタイトルですね。
guest

回答1

0

ベストアンサー

いきなり回答にいきたいところですがいくつか事前知識が必要になるのでその説明から。

スコープ

スコープ(scope)は日本語では「視野」だとか「範囲」だとか訳されます。プログラミング上のスコープとなると参照できる範囲、という感じですかね。上記のコードではselectedCurrencyPairはスコープ外ですね。平たくいうとResultViewControllerからはselectedCurrencyPairが見えない、参照できないんですね。なぜかというとselectedCurrencyPairが関数内にあるからです。言うよりも見た方が早い気がするので例書いてみます。(犬嫌いだったらすいません)下記のカタカナは犬の名前です。

swift

1let rei = 0 2 3func kansuu() { 4 let ichi = 1 5} 6 7print(rei) // 0 8print(ichi) // エラー

こんな感じだったり、

swift

1class Dogs { 2 let goldenRetriever = "ゴールデンレトリバー" 3 private let poodle = "プードル" 4 5 func currentDog() -> String { 6 let Schnauzer = "シュナウザー" 7 return Schnauzer 8 } 9 10 init() { 11 print(goldenRetriever) // ゴールデンレトリバー 12 print(poodle) //プードル 13 print(Schnauzer) //エラー 14 print(currentDog()) //シュナウザー 15 } 16} 17 18class Human { 19 init() { 20 print(goldenRetriever) //エラー 21 let dogs = Dogs() 22 print(dogs.goldenRetriever) //ゴールデンレトリバー 23 print(dogs.poodle) //エラー 24 print(dogs.Schnauzer) //エラー 25 print(dogs.currentDog()) //シュナウザー 26 } 27} 28 29Human() 30

DogsとHumanのinit()内を見比べてみてください。また上記のエラーが出てるのと同じようなものがありませんか?その同じようなものというのは定数Schnauzerです。Schnauzerは関数内で宣言されている定数です。こいつも同じようにスコープ外なのでエラーが出ちゃいます。関数内の変数や定数を呼び出したいならばその変数や定数を返り値にしてあげましょう。

メンバ

エラーを見てみると

Value of type 'SelectViewController' has no member 'selectedCurrencyPair'

とあります。これはSelectViewControllerはメンバselectedCurrencyPairを持っていないよというエラーです。メンバというのはClass内に書かれた定数や変数のことを指します。関数内の定数や変数は指しません。なので質問内容にあるSelectViewControllerのメンバはcurrencyPairPickercurrencyPairの二つですね。スコープの欄に書いた通りselectedCurrencyPairはスコープ外にあるのでメンバではありません。

インスタンス

swift

1var test = SelectViewController()

のようにResultViewControllerSelectViewControllerのインスタンスを新しく生成しています。がしかしSelectViewControllerのインスタンスはSelectViewController内のviewDidLoad()が呼ばれているならすでに存在しているんですね。なのでインスタンスを新たに生成する必要はありません。既存のSelectViewControllerのインスタンスにアクセスしたい場合は別の方法を取る必要があります。

prepareForSegue

とあるviewControllerから別のviewControllerへ画面遷移した際に値を渡したい、ということはよくあることですが(今回の質問もそうですね)、そんな時はよくprepareForSegueという関数を使います。この関数は二つのviewContollerを出発地点、目的地点と捉えると出発地点にある値を目的地点に運んでくれる関数です。画面遷移の際に呼ばれます。なので質問の回答をするにはこの関数をoverrideして使っていきます。

プラスα

またResultsViewControllerクラスの「var test = SelectController()」を省略した記述の仕

方はありますでしょうか?
省略という形ではありません。ただ、SelectViewControllerと書きたい場合、"selvco"と入力すれば予測変換にSelectViewControllerが(多分)一番上に出て来るでしょうからそのように書けば多少楽ができます。

回答例

一例です。

swift

1 2import UIKit 3 4class ViewController: UIViewController { 5 6 @IBOutlet var pickerView: UIPickerView! 7 8 let character = ["あ", "い", "う", "え", "お"] 9 10 override func viewDidLoad() { 11 super.viewDidLoad() 12 } 13 14 override func didReceiveMemoryWarning() { 15 super.didReceiveMemoryWarning() 16 } 17 18 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 19 //画面遷移時に呼ばれます。ここでは直接Labelのtextに値を渡すのではなく 20 //DestinationViewControllerのメンバに値を渡します。 21 //ここでpickerViewで選択したrowを渡したほうが変な挙動になりにくいという私見があるので 22 //こちらを採用しています。 23 let selectedRow = pickerView.selectedRow(inComponent: 0) 24 25 if let destination = segue.destination as? DestinationViewController { 26 destination.labelText = character[selectedRow] 27 } else { 28 fatalError() 29 } 30 31 } 32 33} 34 35//今回はextensionに書いていますがViewControllerのclass内に書いても構いません。 36extension ViewController: UIPickerViewDataSource, UIPickerViewDelegate { 37 func numberOfComponents(in pickerView: UIPickerView) -> Int { 38 return 1 39 } 40 41 func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 42 return character.count 43 } 44 45 func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 46 return character[row] 47 } 48} 49 50class DestinationViewController: UIViewController { 51 52 var labelText = "" //ここで値を受け取ります 53 @IBOutlet var destinationLabel: UILabel! 54 55 override func viewDidLoad() { 56 super.viewDidLoad() 57 } 58 59 override func viewWillAppear(_ animated: Bool) { 60 //Labelに書き込むならviewWillAppearなんかで行うといい具合かと 61 super.viewWillAppear() 62 destinationLabel.text = labelText 63 } 64 65 override func didReceiveMemoryWarning() { 66 super.didReceiveMemoryWarning() 67 } 68 69} 70

とまあだいたいこんな感じです。

投稿2018/08/01 06:50

編集2018/08/01 06:59
xAxis

総合スコア1349

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

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

xAxis

2018/08/01 06:50

コードの修正箇所が多いのでまた疑問点がありましたらどうぞ。
testyoutatsu

2018/08/01 10:10

詳しく分かりやすい回答ありがとうございます!ほぼ理解することができました。 if let destination = segue.destination as? DestinationViewController { destination.labelText = character[selectedRow] } else { fatalError() } しかしこの箇所がまだ理解できていません。Fatal errorになってしまいます。 解説いただければ幸いです。
fuzzball

2018/08/01 10:41

あなたのコードの場合は ResultsViewController だと思いますが、変更されてますか?
testyoutatsu

2018/08/01 12:07

はい、変更してビルドしました。 DestinationViewController→ResultsViewController character[]→currencyPair[]
xAxis

2018/08/01 13:12

このコードでfatalError()に行っちゃうってことはダウンキャストの失敗ですね。なので自分もfuzzballさんと同じ予想してたのですがそうでも無いわけですよね。となるとかなり斜め上の予想ですがstoryboard上のSelectViewControllerに設置してあるはずのUIButtonが実はResultsViewControllerと繋がっていないとかっていうことはありませんか?これでも違うようならば、一旦prepareForSegue関数内のsegue.destinationをprint関数で呼び出してみてください。また上記の部分も若干詳しく追記しておきます。
testyoutatsu

2018/08/01 13:26

回答ありがとうございます。UIButtonの方を繋ぎ直し、回答いただいたコード部分を書き直したところ問題なく実行できました。FatalErrorが発生していた時、segue.destinationをprint関数で呼び出したところ<プロジェクト名.ResultsViewController: 0x7fc10652b0f0>という表示でした。 基本的なことを詳しく解説していただきありがとうございます。分かりやすく理解することができました。お手数おかけしました。
xAxis

2018/08/01 13:32

うーん、となるとsegue.destinationは一応インスタンスとしてあったわけですね。Xcodeではつなぎ直したりしたら動いちゃう、みたいなのもよくありますし(IBOutletの名前ちょっと変えちゃったけど接続し直しまではやってないとか)または実はよく見たら大文字小文字が違ってたとかあるあるですからその辺りかもですねー。何はともあれ解決してよかったです。 ところでオプショナルバインディングとダウンキャストに関してですがその辺りの理解に関しては問題なさそうですか?
testyoutatsu

2018/08/01 14:51

多分そのようなケアレスミスだと思います。 オプショナルバインディングについては理解していますがダウンキャストについては何となくという感じです。これからネットで調べる以外に書籍を買うなどで知識をつけようかと思っています。
xAxis

2018/08/02 03:35

了解しました、では上記回答の追記はせずこのままにしておきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問