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

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

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

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Xcode

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

Swift

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

Q&A

0回答

764閲覧

TableViewのセルをタップすると、Fatal error: Index out of rangeとエラーになる。

apeirogon0813

総合スコア117

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Xcode

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

Swift

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

0グッド

0クリップ

投稿2020/11/02 09:12

編集2020/12/16 00:10

TableView上のFirebaseから読み取ったデータのセルをタップすると

import random import networkx as nx import matplotlib.pyplot as plt import copy import sys #要素が重複しているか def has_duplicates(seq): return len(seq) == len(set(seq)) def check(v): vmax = 0 for i in range(N-1): if v[i] + v[i+1] > vmax: vmax = v[i] + v[i+1] number = i #print("インデックス = " + str(number)) #print(v[number]) return number def create_volumes(n): #重複しないレコードの作成 V = random.sample(range(5,500), k=n) Min = min(V) ##print("最小値 = " + str(Min)) V.remove(Min) V.insert(0,Min) if check(V) < n/2: return create_volumes(n) #print("V2 = " + str(V)) #観測されるボリューム集合の生成 W = copy.copy(V) for i in range(N-1): W.append(V[i]+V[i+1]) return V, W #候補解から生成されるボリュームを観測したボリュームと比較 def compare_volumes(S, W): T = set() #観測されるボリューム集合の生成 for s in S: w = copy.copy(s) for i in range(N-1): w.append(s[i]+s[i+1]) if set(w) == set(W): #print(tuple(s)) T.add(tuple(s)) return T #1つ以上重複しているボリュームを作成(レコードは重複していない) def create_duplicates(n): data = create_volumes(n) if has_duplicates(data[1]): #重複していないならば return create_duplicates(n) else: return data def create_no_duplicates(n): data = create_volumes(n) #print(data[1]) if has_duplicates(data[1]): return data else: return create_duplicates(n) Ans = [] N = 20 #攻撃者は既知 result = create_volumes(N) V = result[0] #復元したいレコード #print("V = " + str(V)) #print("W = " + str(result[1])) W = list(set(result[1])) #攻撃者が観測するボリュームの集合 #print("生成完了") #要素を昇順に W.sort() #print("昇順W = " + str(W) + " ( |W| = " + str(len(W)) +", 重複数" + str(39-len(W)) + " )") G = nx.Graph() #step1 2頂点の和がボリュームに含まれているのならエッジを追加 #連結しているて頂点のみをエッジごと抽出 n = len(W) for i in range(n-2): for j in range(i+1,n-1): if W[i] + W[j] in W: G.add_edge(W[i], W[j]) #print("グラフ") #print("存在する枝(" + str(len(G.edges)) + ") = " + str(G.edges)) #print("抽出されたボリューム(" + str(len(G.nodes)) +"/" + str(len(W)) +") = " + str(G.nodes)) #print(list(G.neighbors(W[0]))) #step3 確定レコードである頂点を通る経路長=N-1である経路 S = [] T = [] t = [] #//集合の中にリストの集合 l = list(G.neighbors(W[0])) #最大値を生成するボリューム初期解 for w in W: if W[-1] - w in W: if w not in t: T.append([w, W[-1]-w]) t.append(W[-1]-w) #print("maxx = " + str(T)) #l2 = list(G.neighbors(W[-1])) #最小ボリュームの初期解 for i in l: #一次元配列だとループが回せないため、二次元配列にしておく S.append([W[0],i]) def right_shifts(S,G): T = [] for s in S:#各候補解ごとに right_num = len(s) - 1 l = list(G.neighbors(s[right_num]))#一番右の要素と隣合うノードを取得 for node in l: if node not in s: #同じレコードは存在しないかの判定 C = copy.copy(s) C.append(node) if C not in T: T.append(C) return T def left_shifts(S,G): T = [] for s in S:#各候補解ごとに left_num = 0 l = list(G.neighbors(s[left_num]))#一番右の要素と隣合うノードを取得 for node in l: if node not in s: #同じレコードは存在しないかの判定 C = copy.copy(s) C.insert(0,node) if C not in T: T.append(C) return T def right_shift(S,G): #print("S = " + str(S)) for i in range(N-2): #解の長さがNになるまで #print("現時点の解の長さ = " + str(i)) T = [] #j = 1 for s in S: #候補解の数 #print("候補解の " + str(j) + " 番目") #j += 1 if len(s) != N: right_num = len(s) - 1 l = list(G.neighbors(s[right_num]))#一番右の要素と隣合うノードを取得 for node in l: if node not in s: #同じレコードは存在しないかの判定 C = copy.copy(s) C.append(node) if C not in T: T.append(C) #T.append(s)#右に拡張できるがそのノードが終点である可能性があるため #print(T) S = copy.copy(T) #print("S = " + str(S)) return S #S = right_shift(S) #左に拡張 def left_shift(S,G): for i in range(N-2): T = [] for s in S: #print("s = " + str(s)) if len(s) != N: l = list(G.neighbors(s[0])) for node in l: if node not in s: #同じレコードは存在しないかの判定 C = copy.copy(s) C.insert(0,node) #先頭に要素を追加 if C not in T: T.append(C) T.append(s) S = copy.copy(T) #print("s = " + str(S)) return S #step4 確定コードが解に含まれているか 現段階では確定レコードを通る効率的なアルゴリズムが考えられていないため def confirm(S): T = [] for s in S: if len(s) == N: for i in range(len(RNodes)): if RNodes[i] not in s: break if i == len(RNodes)-1: T.append(s) return T def uni(R,L): S = R for l in L: flag = 0 for r in R: print("l = " + str(l)) print("r = " + str(r)) if l == r: flag = 1 break if flag == 0: S.append(l) return S def uni2(R,L): S = R for l in L: S.append(l) return S #これまでは、最小のボリュームが真ん中である場合を考慮して両方に拡張しないといけなかった #T = copy.copy(S) #T = right_shift(T,G) #T = left_shift(T,G) #S = left_shift(S,G) SR = [] SL = [] TR = [] TL = [] #print(N/2) for i in range(3, int(N/2) + 1): print("i = " + str(i), file=sys.stderr) SR = right_shifts(S,G) SL = left_shifts(S,G) TR = right_shifts(T,G) TL = left_shifts(T,G) S = uni2(SR,SL) T = uni2(TR,TL) print("候補解数S = " + str(len(S)), file=sys.stderr) print("候補解数T = " + str(len(T)), file=sys.stderr) #print("Smin = " + str(len(S))) #print(S) #print("Tmax = " + str(len(T))) #print(T) SS = [] for s in S: for t in T: flag = 0 for i in s: if i in t: flag = 1 break if flag == 0: if s[0] + t[0] in W: SS.append(list(reversed(s)) + t) if s[0] + t[-1] in W: SS.append(t + s) if s[-1] + t[0] in W: SS.append(s + t) if s[-1] + t[-1] in W: SS.append(t + list(reversed(s))) #print(SS) #print("解候補 = " + str(T)) #print("解の数1 = " + str(len(SS))) #T = confirm(T) #S = confirm(S) #print("解候補選別T = " + str(T)) #print("解候補選別S = " + str(S)) #print("解の数2 = " + str(len(S))) #step5 解からボリュームを計算し比較 #G1 = compare_volumes(T, W) #print("G1 = " + str(G1)) G2 = compare_volumes(SS, W) #print("G2 = " + str(G2)) #G3 = G1|G2 #print("候補解(" + str(len(G2)) + "個) = " + str(G2)) #print("真の解 = " + str(V)) if len(G2) == 1: print("unique") elif len(G2) > 1: print("Ambiguous") else: print("候補解の数がゼロ")

didSelectRowAt内に
detailVC.idString = dataSets[indexPath.row].docIDとすると
エラー:Fatal error: Index out of rangeとなります。
(どのセルをタップしてもエラーになります。)

numberOfRowsInSectionでは、
return dataSets.countとしてます。
なおその前にprintでdataSets.countを出力すると0ではなく, 適切な数字が出力されていました。

また、didSelectRowAt内でテスト出力してみると
firebaseからのデータを格納している構造体を格納する配列dastaSetsが空[]になっておりました。
indecPath.rowはクリックしたセルの番号が正しく出力されていました。
loadData()によって画面のセルにはFireStoreから読み込んだデータが読み込まれているのに、
dataSetsが[]である原因わかりません。

ご教示願います。

イメージ説明
![イメージ説明
イメージ説明

追記:
どうやらセルをタップした時に、loadData()が呼ばれているのか、if文のerror != nilの
print("erorrrrrr")が出力されたので、これによって、dataSetsが空のままになっていることがわかりました。

loadData()はviewWillAppearのみに記載しており、viewWillAppearは始めの画面生成時にのみよばれることから、セルタップ時にloadData()が呼ばれているのは

addSnapshotListnerのトリガーのようなものが関係しているのでしょうか。

ご教示願います。
イメージ説明

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

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

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

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

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

cateye

2020/11/02 09:58 編集

画像では検証が出来ません、コンパイルできるソースを提示して下さい。
apeirogon0813

2020/11/02 10:12

すみません、承知致しました。 Gitに載せましたのでそちらからくろーんしてみてください。
ichi888

2020/11/02 10:46

createPostsListブランチにloadDataというメソッドがない気がします。 pushしていますでしょうか?
apeirogon0813

2020/11/02 10:49 編集

確認したところpushできていました。 以下のファイルにございますのでご確認ください。 LoveJudgement/LoveJudgement/PostsListViewController.swift
ichi888

2020/11/02 11:30

コードを拝見しました。添付ありがとうございます。 セルをタップするとloadData()が呼ばれるという部分をもう少し細かくお伺いしたいのです。 例えば、以下のように`loadData()`にprintを入れると、タップするたびに何が出力されますか? ``` func loadData() { print("loadData呼ばれた") //user/***/Postsのdocumentたちを引っ張ってくる→postData新しい順に db.collectionGroup("myPosts").order(by: "postDate").addSnapshotListener { (snapShot, error) in print("drawing") ``` セルをタップ > `didSelectRowAt`が呼ばれる > `DetailViewController`に遷移 > `PostsListViewController`に戻った時に`viewWillAppear`が呼ばれる > loadData()が呼ばれる は考えられるかと思いますが、セルタップのみでloadData()が呼ばれることないように思えました。
apeirogon0813

2020/11/02 11:55

ご回答ありがとうございます。 >例えば、以下のように`loadData()`にprintを入れると、タップするたびに何が出力されますか? この場合は loadData呼ばれた drawing となります。ちなみにセルをタップされた場合、 if error != nil { print("eroorrrrrrrrrrrr") return } のerooorrrrが出力されました。 私もichi888さんのように、loadData()が呼ばれる理由がわからなかったのですが、 おそらく、addSnapshotListener のリスナー?によって、タップした時も呼び出されているような気がします。 それで、特にセルのデータ変更はないので、error構文に入ってしまっているかと思いました。 なので、 if error != nil { print("eroorrrrrrrrrrrr") return } print("drawing") self.dataSets = [] というように、error処理の後にdataSetsを初期化することによって、無事タップしてもエラーは生じませんでした。 addSnapshotListnerが原因ぽいです。
ichi888

2020/11/02 12:17

そうでしたか。失礼しました。 対応としては、dataSetsの空にするタイミングを変更したら大丈夫でそうですね! 個人的にloadData()が呼ばれてしまう理由はまだしっくりこないのですが。。
apeirogon0813

2020/11/02 12:55

すみません、collectionGroupのクエリを用いる際にFirebaseでセキュリティルールを追加する必要があるようでして、もろもろ設定を行ったところ、loadData()がセルをタップした際に呼ばれることがなくなりました。。。
hameji001

2020/12/08 15:36 編集

通りすがりですが、コードを見ていて思ったので追加までに、、、 こっちの方が今回の問題の核心だと思います。 self.dataSets = []の記載位置に問題があると思います。 お示しされたコードを考えると、 もしerrorが返ってきた時(エリア外とかで通信できない時(?)などかな?)、 先にdataSetsが空配列にされてしまいます。 tableViewのreloadDataはデータが返ってきたブロックの中にあるので、 表示自体は更新されないので、tableViewに表示されている rowの値のdataが存在しないけど、押せる状態となってしまいます。 アプリがクラッシュする致命的なエラーとなります。 回避するには、self.dataSets = []をデータが存在し返ってきた時の中に入れるか、didSelectRowAtでdataSets.countとindexPath.rowに条件を追加するかですね。 通常は前者の対応がいいと思います。 あと、printがいいかは別問題ですが、errorが存在する場合、 print(error.localizedDescription)と書いておくと、 errorの理由が表示されるようになり、手がかりを得ることができます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問