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

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

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

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Xcode

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

Swift

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

Q&A

解決済

1回答

713閲覧

touchesBeganの処理内でoutletCollectionを呼び出してもnilになってしまう。

kageusu

総合スコア2

for

for文は、様々なプログラミング言語で使われている制御構造です。for文に定義している条件から外れるまで、for文内の命令文を繰り返し実行します。

Xcode

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

Swift

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

0グッド

0クリップ

投稿2021/05/09 13:15

編集2021/05/10 00:18

「touchesBegan」の処理で「outletCollection」を使用したい

swiftで、画面をタップする毎に10個のImageViewの画像を順番に変更するシステムを作ろうとしています。
10個のImageViewは、全てOutletCollectionとして管理し、それぞれに違うtagつけています。(0~9)

※追記:処理は、UIViewのXibファイル上で行っています。

しかし、画面をタップするとそのOutletCollectionがnilであるとエラーが表示され、前に進めない状況です。
以下に詳しい状況を記載します。

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

Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

和訳:Thread 1: Fatal error: オプション値のアンラップ中に予期せずnilが見つかった

該当のソースコード

Swift

1//xibファイルになります。 2class AnimationView: UIView { 3 let data = [["画像ファイル名1","こんにちは"], 4 ["画像ファイル名2","こんばんは"]] //本来ある配列内のデータ数は省略 5 6 @IBOutlet var imageViews: [UIImageView]! 7 8 var tagNumber = 0 9 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 10 if tagNumber < 10 { 11               //↓「imageViewsがnilである」というエラーが発生。 12 for img in imageViews { 13 if img.tag == tagNumber { 14 img.image = UIImage(named: data[tagNumber][0]) 15 } 16 } 17 } 18 tagNumber += 1 19 } 20 21}

touchsBegan内でOutletCollectionを呼び出すと、上記のエラーが発生してしまいます。

一方で、override init(frame: CGRect)OutletCollectionを呼び出す分にはしっかりと参照できてしまいます。
######処理が成功する例↓

swift

1 2let data = [["画像ファイル名1","こんにちは"], 3 ["画像ファイル名2","こんばんは"]] //本来ある配列内のデータ数は省略 4 5@IBOutlet var imageViews: [UIImageView]! 6 7override init(frame: CGRect) { 8 super.init(frame: frame) 9 10     //nilのエラーが出ることもなく、しっかりとfor文で処理を回すことができます。 11 for img in imageViews { 12 img.image = UIImage(named: data[img.tag][0]) 13 } 14 15 16 }

試したこと

touchesBegan内の処理を削除し、代わりに** print(imageViews) を記述 ←nilと出力される**

swift

1class AnimationView: UIView { 2 3 @IBOutlet var imageViews: [UIImageView]! 4 5 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 6 print(imageViews) //nil と出力される 7 } 8}

###※追記 省略していたコードの全体を記載します

swift

1class AnimationView: UIView { 2 3 @IBOutlet var imageViews: [UIImageView]! 4 5 // ガチャ結果のデータを呼び出す。 6 let data = [["画像ファイル名1","1"], 7 ["画像ファイル名2","2"], 8 ["画像ファイル名3","3"], 9 ["画像ファイル名4","4"], 10 ["画像ファイル名5","5"], 11 ["画像ファイル名6","6"], 12 ["画像ファイル名7","7"], 13 ["画像ファイル名8","8"], 14 ["画像ファイル名9","9"], 15 ["画像ファイル名10","10"],] 16 17 override init(frame: CGRect) { 18 super.init(frame: frame) 19 loadNib() 20 21 //ここではnilのエラーが出ることもなく、しっかりとfor文で処理を回すことができます。 22 for img in imageViews { 23 img.image = UIImage(named: data[img.tag][0]) 24 } 25 26 print(imageViews.count) //10 と出力される 27 } 28 29 required init(coder aDecoder: NSCoder) { 30 super.init(coder: aDecoder)! 31 } 32 33 func loadNib(){ 34 guard let view = UINib(nibName: "AnimationView", bundle: nil).instantiate(withOwner: self, options: nil).first as? AnimationView else { 35 return 36 } 37 view.frame = self.bounds 38 self.addSubview(view) 39 } 40 41 42 var tagNumber = 0 43 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 44 print(imageViews.count) //nil と出力される 45 if tagNumber < 10 { 46               //↓「imageViewsがnilである」というエラーが発生。 47 for img in imageViews { 48 if img.tag == tagNumber { 49 img.image = UIImage(named: data[tagNumber][0]) 50 } 51 } 52 } 53 tagNumber += 1 54 } 55} 56

OutletCollectionの接続
File's Ownerの方で接続しています

補足情報(FW/ツールのバージョンなど)

ツール:**XCode Version 12.5 **
使用言語:Swift5
シミュレーター:iPhone11ProMax

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

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

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

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

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

hoshi-takanori

2021/05/09 13:42

Storyboard での接続に失敗してるのでは。あと、Storyboard からビューを生成する場合は、init(frame:) ではなく init(coder:) が呼ばれるはず…。
tomato879241

2021/05/09 14:38

viewDidLoadの中で print(imageViews.count) を入れて、imageViewのIBOutletが幾つ配列(imageViews)に入っているか確認してみては?
kageusu

2021/05/09 23:50

ありがとうございます! かなりコードを省略してしまっているのですが、 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! } func loadNib(){ guard let view = UINib(nibName: "AnimationView", bundle: nil).instantiate(withOwner: self, options: nil).first as? AnimationView else { return } view.frame = self.bounds self.addSubview(view) } この二つも記入していて、loadNib()に関してはinit(frame:)内に記述しています。↓ override init(frame: CGRect) { super.init(frame: frame) loadNib() } 接続に関しても、imageViewを一旦全部消して、再度繋げ直して実行してみましたが、うまく行きませんでした。 viewDidLoadに関しては、このクラス自体がViewControllerではなくUIViewのクラスになっているので、 init(frame:)の方でprint(imageViews.count)をしてみましたが、10個分は格納されているようでした。 touchesBegan()でprint(imageViews.count)をすると、相変わらずnilでした...
hoshi-takanori

2021/05/10 00:14

init(frame:) の中で loadNib って、やってることがおかしいと言うか、それだと AnimationView が 2 つ重なりますね。xib から読み込んだ方だけ使えばいいと思いますが…。
kageusu

2021/05/10 00:29

そもそも実装から間違ってた可能性があるのですね...。 init(frame:)からloadNibを削除して実行してみました。(おそらくそういう意図でおっしゃたのではないと思いますが..)↓ override init(frame: CGRect) { super.init(frame: frame) // loadNib() for img in imageViews { img.image = UIImage(named: data[img.tag][0]) } print(imageViews.count) } nilのエラーが発生しました。 再度Xibの実装について調べて、実装し直してみます!
hoshi-takanori

2021/05/10 00:33

xib の File's Owner が AnimationView なら、xib のトップレベルの view を AnimationView じゃなくてただの UIView にすれば init(frame:) から loadNib で大丈夫ですね。
kageusu

2021/05/10 00:50

xibのトップレベルのViewをUIViewにするというのは、クラスをUIViewに指定するということでしょうか?
hoshi-takanori

2021/05/10 01:02

です。xib でクラスを UIView にして、outlet の接続先は File's Owner にして、loadNib では 〜.first as? UIView とすれば良いかと。 現状だと、loadNib の self も view も AnimationView なので、self.addSubview(view) で AnimationView が 2 重になっちゃいますので。
kageusu

2021/05/10 01:08

感動です。できました!!!!! ありがとうございます泣
kageusu

2021/05/10 01:09

コメントをベストアンサーにすることってできますか・・・すいません、質問とは関係ない話で汗
kageusu

2021/05/10 01:13

すいません、ありがとうございます!
guest

回答1

0

ベストアンサー

AnimationView クラスの loadNib ですが、これだと self も view も AnimationView なので、self.addSubview(view) で AnimationView が 2 重になって、どちらかの outlet が nil のためにエラーになってると思います。
ので、xib のトップレベルの view のクラスは UIView にして、outlet の接続先は File's Owner にして、
loadNib では 〜.first as? UIView とすれば良いと思います。

swift

1 func loadNib(){ 2 guard let view = UINib(nibName: "AnimationView", bundle: nil).instantiate(withOwner: self, options: nil).first as? AnimationView else { 3 return 4 } 5 view.frame = self.bounds 6 self.addSubview(view) 7 }

投稿2021/05/10 01:17

編集2021/05/10 01:20
hoshi-takanori

総合スコア7901

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問