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
}