Ios 如何正确地将数据从Firebase传递到集合视图的NSObject类
如何正确地将数据发送到集合视图的自定义数据对象类?我的变量总是返回为零 我在它自己的视图控制器中有一个启动屏幕。当我想要加载的所有数据都从firebase加载完毕后,我通过Ios 如何正确地将数据从Firebase传递到集合视图的NSObject类,ios,swift,firebase,firebase-realtime-database,uicollectionview,Ios,Swift,Firebase,Firebase Realtime Database,Uicollectionview,如何正确地将数据发送到集合视图的自定义数据对象类?我的变量总是返回为零 我在它自己的视图控制器中有一个启动屏幕。当我想要加载的所有数据都从firebase加载完毕后,我通过performsgue(带标识符:)进入应用程序的主屏幕,以下是SplashScreenViewController中有问题的代码的代码: func getDatabaseReference(){ let d = DispatchGroup() d.enter() let encodedURL =
performsgue(带标识符:)
进入应用程序的主屏幕,以下是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
,在HomeViewController类中,我有一个集合视图,它由一个名为DemoSource
的自定义类(NSObject类)帮助(如上所示)我正在使用它来分配我刚刚在该类的一个变量中得到的新数据。此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为零,即使我在splashscreenviewcontroller中使用url设置它?这个DemoSource
类get在变量被填充之前就被实例化了,当我用断点进行一些调试时
所以我的问题是,在所有这些解释之后。。。为什么
DemoSource
类中的url变量不仍然为零,为什么只有在从Firebase获取数据后才调用使用该类的视图时,才执行该类 您已经将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"))
}
}
你不需要初始化器
您确实需要一个函数将数据添加到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"))
}
}
然后,在您的getDatabaseReference
-
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)
}
}
DemoSource.shared
在getDatabaseReference
中实例化。当初始化器运行其URL
属性时,其初始值为“”。由于它是一个单例实例,因此init不会再次运行,您也不会将实际URL添加到demoData
数组中。如果您想向模型对象(DemoData)添加项,您需要向模型对象(DemoData)中至少添加一个函数。您能在答案或类似的内容中介绍一下吗,比如将该函数添加到我的数据类代码中,并将其放入您的答案中?我想我已经试过你解释的了,但这只会让事情变得更糟,可能是因为我做错了。。。?我尝试将firebase逻辑添加到的类中,但因为它只是一个NSObject类,所以我不能。我对他们不太熟悉,也不太熟悉……不过谢谢你,这是我两周来收到的最新评论了!在我的集合视图使用HomeViewcontroller类获取数据时,我是否需要更改HomeViewcontroller类中的任何内容?目前情况并非如此,因为您正在使用调度组来确保在获取数据之前不会显示HomeViewcontroller。但是,如果在demoData
数组中需要多个元素,则会出现问题,因为更新数组时需要重新加载集合视图。您可以在模型对象上使用通知
或委派模式,或者为模型提供更新闭包,甚至可以使用合并
通知相关方模型数组的更改。因此,我的脚本可以工作并运行getDatabaseReference()
函数,从for循环中获取当时数据库中的任何内容。它可以是一个url,也可以是10个url,现在如果它是多个url,那么集合视图中的所有视频都是相同的,我猜这就是你关注的问题?是的。您可以将调度组enter
放在正确的位置,以便为每个循环输入,然后notify
仅在调用leave
的次数相同时触发。如果您的循环在getDatabaseReference
之外,那么调度组也需要在该函数之外。是的,我需要重写并清理代码,因为只要填充了一个url,就会调用getDatabaseReference
。所以我会尝试这样做,这样它只会运行一次for循环已经完全完成。