2017-10-05 150 views
1

我知道我错过了一些简单的事情,但我花了数小时试图弄清楚。我的所有代码都能正常工作,并且运行正常,但没有图像。它是空白的。为什么?Firebase存储图像回来空白

func loadMembers4(completion: @escaping ([User]) ->()) { 
    var usersArray = [User]() 
    ref.child("members").observeSingleEvent(of: .value, with: { (snapshot) in 
     for item in snapshot.children { 
      if let snap = item as? FIRDataSnapshot { 
       if let value = snap.value as? [String : Any] { 
        let birthday = value["birthday"] as! Int 
        let childParent = value["childParent"] as! String 
        let firstName = value["firstName"] as! String 
        let gender = value["gender"] as! String 
        let passcode = value["passcode"] as! Int 
        let profileImageUrl = value["profileImageUrl"] as! String 

        // get image 

        self.loadMemberProfilePict4(userProfileImageUrl: profileImageUrl, completion: { (userImage) in 
         let user = User(profilePhoto: userImage, userFirstName: firstName, userBirthday: birthday, userPasscode: passcode, userGender: gender, isUserChildOrParent: childParent) 
         usersArray.append(user) 
         usersArray.sort(by: {$0.birthday > $1.birthday}) 
        }) 
       } 
      } 
     } 
     completion(usersArray) 
    }) 
} 

func loadMemberProfilePict4(userProfileImageUrl: String, completion: @escaping (UIImage) ->()) { 
    var userImage = UIImage() 
    let storageRef = FIRStorage.storage().reference(forURL: userProfileImageUrl) 
    print(userProfileImageUrl) 
    storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (imageData, error) in 
     let profileImage = UIImage(data: imageData!) 
     userImage = profileImage! 
    }) 
    completion(userImage) 
} 

而且ViewDidLoad方法:

loadMembers4 { (usersArray) in 
     print(usersArray.count) 
     self.currentUserName = usersArray[1].firstName 
     self.instructionsLabel.text = "Choose daily and weekly job assignments for \(usersArray[1].firstName)." 
     self.userImage.image = usersArray[1].photo 
    } 

我知道的影像从火力地堡越来越牵强,因为当我在loadMemberProfilePict4功能打印userProfileImageUrl,它打印出正确的火力地堡的存储位置。但是当我在ViewDidLoad中调用它时,图像显示为空白。空空如也。

任何想法?再次,我确信这是一件简单的事,我可以忽略,只需要一双新的眼睛来指出它。谢谢!

编辑#1:

如果我打印出来这是怎么回事在loadMembersProfilePict4方法,它显示以下内容:

func loadMemberProfilePict4(userProfileImageUrl: String, completion: @escaping (UIImage) ->()) { 
    var userImage = UIImage() 
    let storageRef = FIRStorage.storage().reference(forURL: userProfileImageUrl) 
    print("UserProfileURL: ",userProfileImageUrl)  // returns 4 strings of https://firebasestorage.google... (all correct) 

    storageRef.data(withMaxSize: 1024 * 1024) { (imageData, error) in 
     print("ImageData: ",imageData ?? "no data found!")   // returns 21061 bytes, 7668 bytes, 44887 bytes, 8995 bytes (is this right?) 

     let profileImage = UIImage(data: imageData!) 
     userImage = profileImage! 
     print("UserImage: ",userImage.images ?? "no images found!")  // returns "no images found!" 
    } 
    completion(userImage) 
} 

所以它似乎是莫名其妙的形象是没有得到正确加载?我不明白。我在我的项目的另一部分使用完全相同的代码,并且它加载用户图像就好了。到底是怎么回事? AARGH!太令人沮丧了。

+0

如果您在'userImage = profileImage!'之后移动此语句:'completion(userImage)',那么看看是什么行为,我认为这将解决问题 – 3stud1ant3

+0

我已经试过这个代码。它给了我一个致命的错误:索引超出范围错误。 –

+0

我认为它与'loadMemberProfilePict4'方法中的完成处理程序有关。不知何故,它不会被调用,直到它太晚...因为如果我允许它完成加载,它会快速循环所有的图像,然后降落在一个随机的... –

回答

0

因此,在花了两天的时间试图找出解决方案后,我得出结论,由于某些原因,Swift不允许我制作嵌入式@escaping关闭。我可以创建单独的@escaping关闭,它们都可以工作,但是如果我将其中一个放入另一个,则第二个将无法正确执行。

所以这是我落得这样做:

功能#1:获取用户数据火力地堡,包括URL字符串存储在火力地堡存储用户的照片。

func loadMembers(completion: @escaping ([UserClass]) ->()) { 
    var usersArray = [UserClass]() 
    ref.child("members").observeSingleEvent(of: .value, with: { (snapshot) in 
     for item in snapshot.children { 
      if let snap = item as? FIRDataSnapshot { 
       if let value = snap.value as? [String : Any] { 
        let birthday = value["birthday"] as! Int 
        let childParent = value["childParent"] as! String 
        let firstName = value["firstName"] as! String 
        let gender = value["gender"] as! String 
        let passcode = value["passcode"] as! Int 
        let profileImageUrl = value["profileImageUrl"] as! String 

        let user = UserClass(userProfileImageURL: profileImageUrl, userFirstName: firstName, userBirthday: birthday, userPasscode: passcode, userGender: gender, isUserChildOrParent: childParent) 
        usersArray.append(user) 
        usersArray.sort(by: {$0.birthday > $1.birthday}) 
       } 
      } 
     } 
     completion(usersArray) 
    }) 
} 

功能#2:从第一函数采用URL字符串并且使用它来从火力地堡加载用户的图像。但是,这并没有给我完整的图像阵列,因为它一次递增一个地加载它们。而不是给我count: 4,它会给我count: 1, count: 2, count: 3, count: 4

因此,当我加载视图控制器时,图像会循环通过每个用户,然后登陆到正确的一个。不理想。

// WORKS BEST, BUT IMAGES CYCLE THRU ALL USERS 
func loadMemberProfilePict(userImageURL: String, userFirstName: String, userBirthday: Int, userPasscode: Int, userGender: String, userChildParent: String, completion: @escaping ([User]) ->()) { 
    let storageRef = FIRStorage.storage().reference(forURL: userImageURL) 
    storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in 
     let pic = UIImage(data: data!) 
     let user = User(profilePhoto: pic!, 
         userFirstName: userFirstName, 
         userBirthday: userBirthday, 
         userPasscode: userPasscode, 
         userGender: userGender, 
         isUserChildOrParent: userChildParent) 
     self.users.append(user) 
     self.users.sort(by: {$0.birthday > $1.birthday}) 
     completion(self.users) 
    }) 
} 

功能#3:导致这个功能,我用一个简单的'if'声明等到所有用户都在视图中显示它们之前被加载。

ViewDidLoad

loadMembers { (usersArray) in 
     var memberImageCount = 0 

     for user in usersArray { 
      self.loadMemberProfilePict(userImageURL: user.imageURL, userFirstName: user.firstName, userBirthday: user.birthday, userPasscode: user.passcode, userGender: user.gender, userChildParent: user.childParent, completion: { (usersIntermediateArray) in 
       memberImageCount += 1 
       if memberImageCount == usersArray.count { 
        self.users = usersIntermediateArray 
        self.userImage.image = self.users[3].photo 
        self.currentUserName = self.users[3].firstName 
        self.instructionsLabel.text = "Choose daily and weekly job assignments for \(self.users[3].firstName)." 
        self.navigationItem.title = self.users[3].firstName 
       } 
      }) 
     } 
    } 

我不知道这是不是最好的做法,但它的功能,我希望它可以帮助别人,将来谁可能有同样的问题。

+0

这就像一个穷人的逃脱关闭。哈哈哈! –

2

I simply put two line for userName and image of viewDidLoad() method into completion block of respective method. Remove that lines from viewDidLoad().

func loadMemberProfilePict4(userProfileImageUrl: String, completion: @escaping (UIImage) ->()) { 
    var userImage = UIImage() 
    let storageRef = FIRStorage.storage().reference(forURL: userProfileImageUrl) 
    print(userProfileImageUrl) 
    storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (imageData, error) in 
     let profileImage = UIImage(data: imageData!) 
     userImage = profileImage! 
     self.userImage.image = profileImage 
    }) 
    completion(userImage) 
} 

For Username put other line in completion block of loadMembers4()

func loadMembers4(completion: @escaping ([User]) ->()) { 
    var usersArray = [User]() 
    ref.child("members").observeSingleEvent(of: .value, with: { (snapshot) in 
     for item in snapshot.children { 
      if let snap = item as? FIRDataSnapshot { 
       if let value = snap.value as? [String : Any] { 
        let birthday = value["birthday"] as! Int 
        let childParent = value["childParent"] as! String 
        let firstName = value["firstName"] as! String 
        let gender = value["gender"] as! String 
        let passcode = value["passcode"] as! Int 
        let profileImageUrl = value["profileImageUrl"] as! String 

        // get image 

        self.loadMemberProfilePict4(userProfileImageUrl: profileImageUrl, completion: { (userImage) in 
         let user = User(profilePhoto: userImage, userFirstName: firstName, userBirthday: birthday, userPasscode: passcode, userGender: gender, isUserChildOrParent: childParent) 
         **self.currentUserName = user.firstName** 
         self.instructionsLabel.text = "Choose daily and weekly job assignments for \(user.firstName)." 
         usersArray.append(user) 
         usersArray.sort(by: {$0.birthday > $1.birthday}) 
        }) 
       } 
      } 
     } 
     completion(usersArray) 
    }) 
} 
+0

感谢您的代码,但它不起作用。通过将'userName'和图像放入'loadMemberProfilePict'方法中,屏幕上的图像可以快速循环遍历所有用户配置文件,并且每次都随机地登录。诚然,现在有一个图像,但它是错误的。它在所有用户配置文件图像中循环的原因是因为函数本身是for..in循环的一部分。 –

+0

是的,这是问题,因为你的代码是for循环,这个方法是异步调用,所以你的第一个用户数据提取仍然在队列中,第二个用户的数据提取是开始。这是图像中随机性的原因。 –

+0

非常感谢您的帮助。只是通过谈话给我想法尝试。至于你对'for ... in'循环的评论,是的,关于如何克服这个问题的想法? –

0

这就是为什么你的代码是空白:

// #1 
var userImage = UIImage() 
// #2 
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (imageData, error) in 
    // #4 
    let profileImage = UIImage(data: imageData!) 
    userImage = profileImage! 
}) 
// #3 
completion(userImage) 

有一种竞争状态,因为下载的数据是异步的。这意味着您在图像下载之前返回空图像。通过这样做来解决它:

// #1 
var userImage = UIImage() 
// #2 
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (imageData, error) in 
    // #3 
    completion(UIImage(data: imageData!)) 
}) 

应该解决问题。

+0

感谢您的想法。我也认为这是有道理的。但是当我实现你的代码时,我得到一个错误:'致命错误:索引超出范围'。思考? –

+0

在我看来,我无法在转义完成中调用转义完成。我不知道为什么。也许还有另外一种方法可以解决这个问题?我尝试了所有我能想到的。也许你有一些想法可以让我迷失方向? –

+0

错误是:在'self.currentUserName = usersArray [1] .firstName'行的'ViewDidLoad'中发生'致命错误:索引超出范围'。如果代码编译正确,应该有四个用户,并且用户[1]的呼叫不应超出范围。而当我打印'usersArray.count'时,我得到了4的计数。所以我知道数组索引不超出范围... –