代码之家  ›  专栏  ›  技术社区  ›  Nat

如何正确地将数据从Firebase传递到集合视图的NSObject类

  •  2
  • Nat  · 技术社区  · 4 年前

    如何正确地将数据发送到集合视图的自定义数据NSObject类?我的变量总是以零返回。

    我在它自己的视图控制器中有一个启动屏幕。当我要加载的所有数据都从firebase加载完毕后,我通过 performSegue(withIdentifier:) SplashScreenViewController :

       func getDatabaseReference(){
        let d = DispatchGroup()
        d.enter()
        let encodedURL = (postIDRefDic["post1"]! + "/postURL")
        ref.child(encodedURL).observe(.value, with: { (snapshot) in
            let newUrl = snapshot.value as! String
            DemoSource.shared.url = newUrl
            d.leave()
        })
        d.notify(queue: .main){
            self.performSegue(withIdentifier: "showHome", sender: nil)
        }
    }
    

    在上面的代码中,您可以看到我正在切换到下一个视图控制器, HomeViewController DemoSource (上面也显示了)我正在使用它来分配我刚刚在该类中的变量中获得的新数据。这个 类是类型为的自定义数据类 NSObject :

    import UIKit
    import Firebase
    
    struct DataObj {
        var image: UIImage?
        var play_Url: URL?
        var title = ""
        var content = ""
    }
    
    class DemoSource: NSObject {
    static let shared = DemoSource()
    var demoData = [DataObj]()
    var url = ""
    
    override init() {
    
        demoData += [
                      DataObj(image: #imageLiteral(resourceName: "ss-1") , play_Url: URL(string: url), title: "title ", content: "Description")
        ]
    }
    

    我将此类与HomeViewController一起用于集合视图:

    import UIKit
    import AVKit
    import Firebase
    import MMPlayerView
    
    
    class HomeViewController: UIViewController {
        var offsetObservation: NSKeyValueObservation?
        lazy var mmPlayerLayer: MMPlayerLayer = {
        let l = MMPlayerLayer()
        l.cacheType = .memory(count: 5)
        l.coverFitType = .fitToPlayerView
        l.videoGravity = AVLayerVideoGravity.resizeAspect
        l.replace(cover: CoverA.instantiateFromNib())
        l.repeatWhenEnd = true
        return l
    }()
    
    @IBOutlet weak var playerCollect: UICollectionView!
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // remove previous download fails file
        MMPlayerDownloader.cleanTmpFile()
        self.navigationController?.mmPlayerTransition.push.pass(setting: { (_) in
    
        })
        offsetObservation = playerCollect.observe(\.contentOffset, options: [.new]) { [weak self] (_, value) in
            guard let self = self, self.presentedViewController == nil else {return}
            NSObject.cancelPreviousPerformRequests(withTarget: self)
            self.perform(#selector(self.startLoading), with: nil, afterDelay: 0.2)
        }
        playerCollect.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 200, right:0)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
            self?.updateByContentOffset()
            self?.startLoading()
        }
    
            mmPlayerLayer.getStatusBlock { [weak self] (status) in
                switch status {
                case .failed(let err):
                    let alert = UIAlertController(title: "err", message: err.description, preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                    self?.present(alert, animated: true, completion: nil)
                case .ready:
                    print("Ready to Play")
                case .playing:
                    print("Playing")
                case .pause:
                    print("Pause")
                case .end:
                    print("End")
                default: break
                }
            }
            mmPlayerLayer.getOrientationChange { (status) in
                print("Player OrientationChange \(status)")
            }
        }
    
    deinit {
    
        offsetObservation?.invalidate()
        offsetObservation = nil
        print("ViewController deinit")
    }
    
    @IBAction func profileButtonTap(_ sender: Any) {
        let uid = (Auth.auth().currentUser?.uid)!
        let Splash = SpalshScreenViewController()
        Splash.GetProfilePicture(uid: uid)
        Splash.GetUsername(uid: uid)
        Splash.GetName(uid: uid)
        Splash.GetClipsNumber(uid: uid)
        Splash.GetFollowersNumber(uid: uid)
        Splash.GetFollowingsNumber(uid: uid)
        performSegue(withIdentifier: "showProfile", sender: nil)
    }
    }
    
    extension HomeViewController: MMPlayerFromProtocol {
     func backReplaceSuperView(original: UIView?) -> UIView? {
        guard let path = self.findCurrentPath(),
            let cell = self.findCurrentCell(path: path) as? PlayerCell else {
            return original
        }
        return cell.imgView
    }
    
    // add layer to temp view and pass to another controller
    var passPlayer: MMPlayerLayer {
        return self.mmPlayerLayer
    }
    func transitionWillStart() {
    }
    // show cell.image
    func transitionCompleted() {
        self.updateByContentOffset()
        self.startLoading()
    }
    }
    extension HomeViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let m = min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
        return CGSize(width: m, height: m*0.75)
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
       DispatchQueue.main.async { [unowned self] in
        if self.presentedViewController != nil || self.mmPlayerLayer.isShrink == true {
                //self.playerCollect.scrollToItem(at: indexPath, at: .centeredVertically, animated: true)
                //self.updateDetail(at: indexPath)
            } else {
                self.presentDetail(at: indexPath)
            }
        }
    }
    
    fileprivate func updateByContentOffset() {
        if mmPlayerLayer.isShrink {
            return
        }
    
        if let path = findCurrentPath(),
            self.presentedViewController == nil {
            self.updateCell(at: path)
            //Demo SubTitle
            if path.row == 0, self.mmPlayerLayer.subtitleSetting.subtitleType == nil {
               }
            }
        }
    }
    
    fileprivate func presentDetail(at indexPath: IndexPath) {
        self.updateCell(at: indexPath)
        mmPlayerLayer.resume()
    
        if let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DetailViewController") as? DetailViewController {
            vc.data = DemoSource.shared.demoData[indexPath.row]
            self.present(vc, animated: true, completion: nil)
        }
    }
    
    fileprivate func updateCell(at indexPath: IndexPath) {
        if let cell = playerCollect.cellForItem(at: indexPath) as? PlayerCell, let playURL = cell.data?.play_Url {
            // this thumb use when transition start and your video dosent start
            mmPlayerLayer.thumbImageView.image = cell.imgView.image
            // set video where to play
            mmPlayerLayer.playView = cell.imgView
            mmPlayerLayer.set(url: playURL)
        }
    }
    
    @objc fileprivate func startLoading() {
        self.updateByContentOffset()
        if self.presentedViewController != nil {
            return
        }
        // start loading video
        mmPlayerLayer.resume()
    }
    
    private func findCurrentPath() -> IndexPath? {
        let p = CGPoint(x: playerCollect.contentOffset.x + playerCollect.frame.width/2, y: playerCollect.frame.height/2)
        return playerCollect.indexPathForItem(at: p)
    }
    
    private func findCurrentCell(path: IndexPath) -> UICollectionViewCell? {
       return playerCollect?.cellForItem(at: path)
    }
    }
    
    extension HomeViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return DemoSource.shared.demoData.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
            cell.data = DemoSource.shared.demoData[indexPath.row]
            return cell
        }
        return UICollectionViewCell()
    }
    }
    

    我第一次实例化 Demosource 等级为:

    extension HomeViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return DemoSource.shared.demoData.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlayerCell", for: indexPath) as? PlayerCell {
            cell.data = DemoSource.shared.demoData[indexPath.row]
            return cell
        }
        return UICollectionViewCell()
    }
    }
    

    当我运行我的应用程序时,它显然崩溃了,数据类中的url是nil,即使我在splashscreenviewcontroller中设置了url?这个 演示源 类get在变量被填充之前就被实例化了,我用断点进行了一些调试。

    所以我的问题是,在所有这些解释之后。。。为什么 类仍然为nil,为什么类在使用该类的视图仅在从Firebase获取数据后调用时才被执行?

    0 回复  |  直到 4 年前
        1
  •  1
  •   Paulw11    4 年前

    你已经实现了 DemoSource 作为单例,这意味着它在您第一次引用 DemoSource.shared getDatabaseReference . 当它被实例化时 url 它的初始值(“”),所以这就是添加到 demoData 阵列。

    您确实需要一个函数将数据添加到 解调器

    class DemoSource: NSObject {
        static let shared = DemoSource()
        var demoData = [DataObj]()
    
        add(urlString: String) {
            demoData.append(DataObj(image: #imageLiteral(resourceName: "ss-1") , play_Url: URL(string: urlString), title: "title ", content: "Description"))
        }
    }
    

    然后,在你的 获取数据库引用

    func getDatabaseReference(){
        let d = DispatchGroup()
        d.enter()
        let encodedURL = (postIDRefDic["post1"]! + "/postURL")
        ref.child(encodedURL).observe(.value, with: { (snapshot) in
            if let newUrl = snapshot.value as? String {
               DemoSource.shared.add(urlString: newUrl)
            }
            d.leave()
        })
        d.notify(queue: .main){
            self.performSegue(withIdentifier: "showHome", sender: nil)
        }
    }