Firebase Storage asyn c загрузка изображений в произвольном порядке - PullRequest
2 голосов
/ 28 января 2020

Я пытаюсь загрузить изображения из хранилища Firebase для отображения в ячейках моей коллекции, но изображения продолжают появляться в случайном порядке в ячейках. Каждая ячейка имеет метку, полученную из хранилища базы данных (item1, item2 et c), которая каждый раз отображается в нужной ячейке. Изображения, хранящиеся в хранилище Firebase, имеют дочерний URL-адрес для хранения соответствующего имени элемента в базе данных Firebase.

Я успешно могу получить URL каждого изображения, загрузить все изображения и правильно отобразить их в ячейках, просто они продолжают появляться в случайном порядке каждый раз, когда я открываю приложение, так что изображение делает не соответствует названию предмета.

Я понимаю, что мне нужно асинхронно загружать изображения, поэтому каждое изображение завершает загрузку в нужной ячейке, прежде чем перейти к следующей, но у меня возникают проблемы при этом. Вот мой код на данный момент:

    func downloadImg(completion: @escaping (UIImage?) -> ()) {
    let ref = Database.database().reference().child("somePath")
    ref.observeSingleEvent(of: .value) { (snapshot) in
        for item in snapshot.children {
            let snap = item as! DataSnapshot
            let imageSnap = snap.childSnapshot(forPath: "img/storageUrl")
            if let url = imageSnap.value as? String {
            let someRef = self.storageRef.reference(forURL: url)
                someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
                    if let error = error {
                        print(error)
                    } else {
                        let image = UIImage(data: data!)
                        DispatchQueue.main.async {
                            completion(image)
                        }
                    }
                }
            }
        }
    }
}

Затем я вызываю свою функцию в viewdidload:

    downloadImg { (completion) in
            self.itemPicArray.append(completion!)
            self.collectionView?.reloadData()
        }

Наконец я устанавливаю свой просмотр изображения ячейки на itemPicArray[indexPath.row]

Мне нравится Я сказал, что это работает отлично, за исключением того факта, что изображения продолжают появляться случайно. Помощь очень ценится!

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Ваша проблема в том, что каждый раз, когда появляется изображение, вы перезагружаете весь сборный вид. В зависимости от размеров изображений и состояния сети изображения будут поступать в разном порядке почти каждый раз.

Попробуйте сначала загрузить все изображения, а затем один раз перезагрузить представление коллекции. Если изображений много, рассмотрите возможность разбиения на страницы своих результатов. Вы можете перечислить l oop и отсортировать массив источника данных по этому оригинальному порядку. Я добавил пользовательский объект данных, чтобы помочь с этим.

class CustomObject {

    var image: UIImage?
    let n: Int

    init(image: UIImage?, n: Int) {
        self.image = image
        self.n = n
    }

}

let dispatch = DispatchGroup()

for (n, item) in snapshot.children.enumerated() {

    let object = CustomObject(image: nil, n: n) // init custom object with n (image is still nil)
    dispatch.enter() // enter dispatch

    someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in // download image
        if let error = error {
            print(error)
        } else {
            let image = UIImage(data: data!)
            object.image = image // inject custom object with image
            itemPicArray.append(object) // append to array
        }
        dispatch.leave() // leave dispatch
    }

}

dispatch.notify(queue: .global()) { // dispatch completion

    itemPicArray.sort { $0.n < $1.n } // sort by n (original download order)
    collectionView.reloadData() // reload collection view

}
1 голос
/ 28 января 2020

Использование модели могло бы быть хорошей идеей.

struct Image {
    var imageName: String
    var image: UIImage
}

Таким образом, независимо от порядка, имени элемента (имени изображения) и изображения будет спарено.

Возможно Лучшее решение сейчас - настроить метод downloadImg так, чтобы он принимал imageName в качестве параметра. Затем вы можете вызвать правильный узел, чтобы получить соответствующий storageURL.

func downloadImg(imageName: String, completion: @escaping (Image?) -> ()) {

    // Use the parameter to create your database reference    
    let ref = Database.database().reference().child(imageName)
    ref.observeSingleEvent(of: .value) { (snapshot) in
        for item in snapshot.children {

            let snap = item as! DataSnapshot
            let imageSnap = snap.childSnapshot(forPath: "img/storageUrl")
            if let url = imageSnap.value as? String {
                let someRef = self.storageRef.reference(forURL: url)
                someRef.getData(maxSize: 10 * 10024 * 10024) { data, error in
                    if let error = error {
                        print(error)
                        return
                    } 

                    if let image = UIImage(data: data) {
                        // Create a variable of type Image (your custom model)
                        let imageWithName = Image(imageName: imageName, image: image)
                        completion(imageWithName)
                    }
                }
            }
        }
    }
}

Вызов и обработка могут быть выполнены так:

// Create a variable to hold your item name/image-pairs
var imagesWithNames = [Image]()

let dispatchGroup = DispatchGroup()

// Iterate over your array of item names
for item in itemArray {

    dispatchGroup.enter()

    downloadImg(item) { (imageWithName) in
        self.imagesWithNames.append(imageWithName)
        dispatchGroup.leave()
    }
}

dispatchGroup.notify(queue: .main) { {
    self.collectionView?.reloadData()
}

И для заполнения collectionView вы можете go :

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! YourCustomCell

    // Get the pair at the given index
    let imageWithName = self.imagesWithNames[indexPath.row]

    DispatchQueue.main.async {
        // Set image and item label (example below)
        self.yourImageView.image = imageWithName.image
        self.yourItemLabel.text = imageWithName.imageName
    }

    return cell
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...