2017-10-05 72 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的計數。所以我知道數組索引不超出範圍... –