CollectionView "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"
CollectionView "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value"
我使用故事板制作了一个 collectionView,一切正常,collectionView 按预期从 firebase 获取数据。当我试图以语法方式制作相同的 collectionView 以便更好地控制布局时,我收到此错误:“致命错误:在隐式展开可选值时意外发现 nil”
self.collectionView.reloadData()
我不知道是什么原因导致这个错误
故事板
import UIKit
import FirebaseFirestore
import FirebaseAuth
import Kingfisher
class storyCollection: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]() {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib(nibName: "colCello", bundle: nil),
forCellWithReuseIdentifier: "colCello")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let user = Auth.auth().currentUser
listener = Firestore.firestore().collection(DatabaseService.usersCollection).document(user!.uid).collection(DatabaseService.profilesCollection).addSnapshotListener({ [weak self] (snapshot, error) in
if let error = error {
DispatchQueue.main.async {
self?.showAlert(title: "Try again later", message: "\(error.localizedDescription)")
}
} else if let snapshot = snapshot {
let profile = snapshot.documents.map { Profile([=11=].data())}
self?.profiles = profile
}
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
listener?.remove()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return profiles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let staticCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCello", for: indexPath) as! colCello
staticCell.profileImageView.image = UIImage(systemName: "plus")!
staticCell.backgroundColor = .systemYellow
staticCell.profileNameLabel.text = "Add Profile"
return staticCell
} else {
let defaultCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCello", for: indexPath) as! colCello
let profile = profiles[indexPath.row]
defaultCell.configureCell(for: profile)
defaultCell.backgroundColor = .systemTeal
return defaultCell
}
}
}
class colCello: UICollectionViewCell {
@IBOutlet weak var profileImageView: UIImageView!
@IBOutlet weak var profileNameLabel: UILabel!
public func configureCell(for profile: Profile) {
profileImageView.kf.setImage(with: URL(string: profile.imageURL))
profileNameLabel.text = profile.profileName
}
}
以编程方式
import UIKit
import FirebaseFirestore
import FirebaseAuth
import Kingfisher
class ProfilesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]()
{
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .white
collectionView.register(colCell6.self, forCellWithReuseIdentifier: "colCell6")
collectionView.isPagingEnabled = false
collectionView.frame = CGRect(x: 5, y: 0, width: view.frame.width-10, height: view.frame.height)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let user = Auth.auth().currentUser
listener = Firestore.firestore().collection(DatabaseService.usersCollection).document(user!.uid).collection(DatabaseService.profilesCollection).addSnapshotListener({ [weak self] (snapshot, error) in
if let error = error {
DispatchQueue.main.async {
self?.showAlert(title: "Try again later", message: "\(error.localizedDescription)")
}
} else if let snapshot = snapshot {
let profile = snapshot.documents.map { Profile([=12=].data())}
self?.profiles = profile
}
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
listener?.remove()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return profiles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let staticCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCell6", for: indexPath) as! colCell6
staticCell.profileImageView.image = UIImage(systemName: "plus")!
staticCell.backgroundColor = .systemBackground
staticCell.profileNameLabel.text = "Add Profile"
return staticCell
}
let defaultCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCell6", for: indexPath) as! colCell6
let profile = profiles[indexPath.row]
defaultCell.backgroundColor = .systemTeal
defaultCell.configureCell(for: profile)
return defaultCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (collectionView.frame.width-5)/2.0, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class colCell6: UICollectionViewCell {
static let identifier = "colCell6"
var profileImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.backgroundColor = .yellow
iv.layer.cornerRadius = 60
return iv
}()
var profileNameLabel: UILabel = {
let nl = UILabel()
nl.contentMode = .scaleAspectFill
nl.clipsToBounds = true
nl.layer.cornerRadius = 10
return nl
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.clipsToBounds = true
contentView.layer.cornerRadius = 10
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 40).isActive = true
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 40).isActive = true
profileImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -40).isActive = true
profileImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40).isActive = true
profileImageView.contentMode = .scaleAspectFill
profileNameLabel.translatesAutoresizingMaskIntoConstraints = false
profileNameLabel.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor,constant: 80).isActive = true
profileNameLabel.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configureCell(for profile: Profile) {
profileImageView.kf.setImage(with: URL(string: profile.imageURL))
profileNameLabel.text = profile.profileName
}
}
您永远不会为 private var collectionView
赋值,您声明了一个语言环境变量 collectionView
,这是一个不同的对象。只需删除 let
替换
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
和
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
此答案与@vadian 的答案相似。
使用
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
而不是
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
代替 CGRect(x: 0, y: 0, width: 0, height: 0)
,您可以只使用 .zero
class ProfilesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]() {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
// Assign UICollectionView to the globle collectionView instead
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .white
collectionView.register(colCell6.self, forCellWithReuseIdentifier: "colCell6")
collectionView.isPagingEnabled = false
collectionView.frame = CGRect(x: 5, y: 0, width: view.frame.width-10, height: view.frame.height)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
}
// Rest of your code
}
我使用故事板制作了一个 collectionView,一切正常,collectionView 按预期从 firebase 获取数据。当我试图以语法方式制作相同的 collectionView 以便更好地控制布局时,我收到此错误:“致命错误:在隐式展开可选值时意外发现 nil”
self.collectionView.reloadData()
我不知道是什么原因导致这个错误
故事板
import UIKit
import FirebaseFirestore
import FirebaseAuth
import Kingfisher
class storyCollection: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]() {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UINib(nibName: "colCello", bundle: nil),
forCellWithReuseIdentifier: "colCello")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let user = Auth.auth().currentUser
listener = Firestore.firestore().collection(DatabaseService.usersCollection).document(user!.uid).collection(DatabaseService.profilesCollection).addSnapshotListener({ [weak self] (snapshot, error) in
if let error = error {
DispatchQueue.main.async {
self?.showAlert(title: "Try again later", message: "\(error.localizedDescription)")
}
} else if let snapshot = snapshot {
let profile = snapshot.documents.map { Profile([=11=].data())}
self?.profiles = profile
}
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
listener?.remove()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return profiles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let staticCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCello", for: indexPath) as! colCello
staticCell.profileImageView.image = UIImage(systemName: "plus")!
staticCell.backgroundColor = .systemYellow
staticCell.profileNameLabel.text = "Add Profile"
return staticCell
} else {
let defaultCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCello", for: indexPath) as! colCello
let profile = profiles[indexPath.row]
defaultCell.configureCell(for: profile)
defaultCell.backgroundColor = .systemTeal
return defaultCell
}
}
}
class colCello: UICollectionViewCell {
@IBOutlet weak var profileImageView: UIImageView!
@IBOutlet weak var profileNameLabel: UILabel!
public func configureCell(for profile: Profile) {
profileImageView.kf.setImage(with: URL(string: profile.imageURL))
profileNameLabel.text = profile.profileName
}
}
以编程方式
import UIKit
import FirebaseFirestore
import FirebaseAuth
import Kingfisher
class ProfilesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]()
{
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .white
collectionView.register(colCell6.self, forCellWithReuseIdentifier: "colCell6")
collectionView.isPagingEnabled = false
collectionView.frame = CGRect(x: 5, y: 0, width: view.frame.width-10, height: view.frame.height)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let user = Auth.auth().currentUser
listener = Firestore.firestore().collection(DatabaseService.usersCollection).document(user!.uid).collection(DatabaseService.profilesCollection).addSnapshotListener({ [weak self] (snapshot, error) in
if let error = error {
DispatchQueue.main.async {
self?.showAlert(title: "Try again later", message: "\(error.localizedDescription)")
}
} else if let snapshot = snapshot {
let profile = snapshot.documents.map { Profile([=12=].data())}
self?.profiles = profile
}
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
listener?.remove()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return profiles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let staticCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCell6", for: indexPath) as! colCell6
staticCell.profileImageView.image = UIImage(systemName: "plus")!
staticCell.backgroundColor = .systemBackground
staticCell.profileNameLabel.text = "Add Profile"
return staticCell
}
let defaultCell = collectionView.dequeueReusableCell(withReuseIdentifier: "colCell6", for: indexPath) as! colCell6
let profile = profiles[indexPath.row]
defaultCell.backgroundColor = .systemTeal
defaultCell.configureCell(for: profile)
return defaultCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: (collectionView.frame.width-5)/2.0, height: 200)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 5
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
class colCell6: UICollectionViewCell {
static let identifier = "colCell6"
var profileImageView: UIImageView = {
let iv = UIImageView()
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.backgroundColor = .yellow
iv.layer.cornerRadius = 60
return iv
}()
var profileNameLabel: UILabel = {
let nl = UILabel()
nl.contentMode = .scaleAspectFill
nl.clipsToBounds = true
nl.layer.cornerRadius = 10
return nl
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.clipsToBounds = true
contentView.layer.cornerRadius = 10
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 40).isActive = true
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 40).isActive = true
profileImageView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -40).isActive = true
profileImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -40).isActive = true
profileImageView.contentMode = .scaleAspectFill
profileNameLabel.translatesAutoresizingMaskIntoConstraints = false
profileNameLabel.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor,constant: 80).isActive = true
profileNameLabel.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func configureCell(for profile: Profile) {
profileImageView.kf.setImage(with: URL(string: profile.imageURL))
profileNameLabel.text = profile.profileName
}
}
您永远不会为 private var collectionView
赋值,您声明了一个语言环境变量 collectionView
,这是一个不同的对象。只需删除 let
替换
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
和
collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
此答案与@vadian 的答案相似。
使用
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
而不是
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: 0, height: 0), collectionViewLayout: layout)
代替 CGRect(x: 0, y: 0, width: 0, height: 0)
,您可以只使用 .zero
class ProfilesViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private var collectionView: UICollectionView!
private var listener: ListenerRegistration?
private var profiles = [Profile]() {
didSet {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
// Assign UICollectionView to the globle collectionView instead
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .white
collectionView.register(colCell6.self, forCellWithReuseIdentifier: "colCell6")
collectionView.isPagingEnabled = false
collectionView.frame = CGRect(x: 5, y: 0, width: view.frame.width-10, height: view.frame.height)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
}
// Rest of your code
}