以下のようなマップの下部にCollectionViewを設置し、リロードボタンを押すとマップのピン、CollectionViewwのセルがリロードされるような画面を現在作っております。
マップ上のピンを更新することは問題なくできているのですが、CollectionViewの更新がうまくいきません。
以下、CollectionViewControllerのコードです。
//sakeMapCollectionViewController.swift
import UIKit
class sakeMapCollectionViewController: UICollectionViewController, mapDelegate{
@IBOutlet weak var sakeMapCollectionView: UICollectionView!
var getCollectionShops: [String] = []
var delegate: mapDelegate?
var ActivityIndicator: UIActivityIndicatorView = UIActivityIndicatorView()
let flowLayout = FlowLayout()
private var cellSize: CGSize {
let width = collectionView.bounds.width * 0.9
let height = width / SakeMapCell.aspectRatio
return CGSize(width: width, height: height)
}
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad()")
if UserDefaults.standard.array(forKey: "collectionShops") != nil {
getCollectionShops = UserDefaults.standard.array(forKey: "collectionShops") as! [String]
}
loadCollectionView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
setActivityIndicator()
}
func loadCollectionView() {
sakeMapCollectionView?.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0) // alpha0で透明
collectionView.decelerationRate = .fast
flowLayout.scrollDirection = .horizontal
flowLayout.minimumLineSpacing = 40
flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
flowLayout.itemSize = cellSize
sakeMapCollectionView.collectionViewLayout = flowLayout
}
func reload() {
print("reloadData()")
sakeMapCollectionView.reloadData() //<---sakeMapCollectionViewがnilでエラー
// loadCollectionView() //<--- Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
override func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
return getCollectionShops.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SakeMapCell", for: indexPath) as! SakeMapCell
cell.shopNameLabel.text = getCollectionShops[indexPath.row]
cell.shopGenreLabel.text = "酒屋"
print("\(indexPath.row)番目の店")
print(cell)
print(indexPath)
return cell
}
}
以下が、MapViewControllerです。
//
// sakeMapViewController.swift
// sakeai_ver1
//
// Created by 新山大地 on 2019/10/22.
// Copyright © 2019 Daichi Niiyama. All rights reserved.
//
import UIKit
import GoogleMaps
import Alamofire
import SwiftyJSON
//UICollectionViewController
class sakeMapViewController: UIViewController{
var googleMap: GMSMapView?
var locationManager = CLLocationManager()
//画面中心の緯度経度
var centerMapCoordinate:CLLocationCoordinate2D!
let marker = GMSMarker()
var markers : [GMSMarker] = []
//取得した周辺のお店配列
var collectionShops: [String] = []
//ContainerViewに表示するためのお店配列
var getCollectionShops: [String] = []
var collectionViewController:sakeMapCollectionViewController = sakeMapCollectionViewController()
//delegateを設定
var delegate: mapDelegate?
let pinIcon = UIImage(named: "pinIcon")
@IBOutlet weak var locationButton: UIButton!
@IBOutlet weak var listButton: UIButton!
@IBOutlet weak var containerView: UIView!
@IBOutlet weak var closeButton: UIButton!
@IBOutlet weak var restartButton: UIButton!
@IBAction func closeButton(_ sender: Any) {
//MapViewに移る前にローカルDBをリセットしておく
UserDefaults.standard.removePersistentDomain(forName: "collectionShops")
self.dismiss(animated: true, completion: nil)
}
//このエリアで再検索
@IBAction func restartButton(_ sender: Any) {
let latitude = self.googleMap?.camera.target.latitude
let longitude = self.googleMap?.camera.target.longitude
centerMapCoordinate = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!)
self.getPlaces(coordinate: centerMapCoordinate)
self.delegate?.reload()
}
override func viewDidLoad() {
super.viewDidLoad()
self.googleMap = GMSMapView()
setupMapView()
setupButton()
self.googleMap?.delegate = self
self.delegate = collectionViewController
}
}
protocol mapDelegate: class {
func reload()
}
restartButtonを押すと、self.getPlaces(coordinate: centerMapCoordinate)で現在地周辺の店をAPIで取得し、
self.delegate?.reload()でCollectionViewをリロードしようとしています。
getPlaceは問題なく実行することができ、店情報は取得できています。
CollectionViewControllerのreload()でprint("reloadData()")を問題なく出力できているため、mapDelegateのreload()も問題なさそうです。
func reload() {
print("reloadData()")
sakeMapCollectionView.reloadData() //<---sakeMapCollectionViewがnilでエラー
// loadCollectionView() //<--- Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
エラーが出ているのは上記コードの部分なのですが、
sakeMapCollectionView.reloadData()でリロードしようとすると、
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
となってしまいます。おそらく、sakeMapCollectionViewがnilになってしまっているとおもうのですが、理由がわかりません。
また、sakeMapCollectionView.reloadData()をコメントアウトし、loadCollectionView()にすると、
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
となってしまいます。loadCollectionView()を実行したから、CollectionViewのセルを更新できるわけではなさそうなのでこちらは
意味ないと思いますが。。
改善方法などご教示願います。
よろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
まず、エラーが発生する直接の原因ですが、
sakeMapViewControllerの中で
var collectionViewController:sakeMapCollectionViewController = sakeMapCollectionViewController()
このようにしてsakeMapCollectionViewControllerのインスタンスを生成して
いますが、この方法だとStroyboardを使用せずにインスタンスを生成します。
そのため、sakeMapCollectionViewControllerの中で
@IBOutlet weak var sakeMapCollectionView: UICollectionView!
このようにしてStoryboardからoutlet接続する形でsakeMapCollectionViewが
定義されていますが、Stroyboardを使用せずにインスタンス生成した場合、
sakeMapCollectionViewはnilになります。
そのため、
sakeMapCollectionView.reloadData()
はnilのため実行できないというエラーになったのだと思います。
Storyboardに貼り付けられたUI部品を読み込んだViewControllerを生成したいのであれば、
instantiateViewControllerWithIdentifier
を使用する必要があります。
そもそも、tyobigorouさんも追記・修正依頼の中で少し触れられていますが、
sakeMapCollectionViewControllerがUICollectionViewControllerを
継承したものであれば、sakeMapCollectionViewをoutlet接続して使用するのではなく
UICollectionViewControllerの中で最初から定義されているcollectionViewを
使用する必要があると思います。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.32%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
退会済みユーザー
2020/01/01 17:04 編集
UICollectionViewControllerは使ったことがあまりないので、解決の糸口になったら程度に聞いてほしいのですが、UICollectionViewControllerのサブクラスでUICollectionViewをOutlet接続すると、numberOfItemsInSectionが更新するのはUICollectionView? UICollectionViewController上のCollectionView?
dacci
2020/01/04 02:43
tyobigorou様
ありがとうございます!
CollectionViewのoutletをやめ、self.collectionViewに変更したところ、
collectionViewController.collectionView.reloadData()でエラーが出ずにうまく行きました!
TakeOne様の言う通り、UICollectionViewControllerの中で最初から定義されているcollectionViewを使用しました。