#ios #swift #loops
#iOS #swift #циклы
Вопрос:
Я пытаюсь получить набор изображений, выбранных пользователем в PHPicker, и сохранить их в массиве. Я повторяю массив результатов PHPicker и добавляю на каждой итерации загруженное изображение в переменную. По какой-то причине код, который следует за этой итерацией, вызывается до завершения итерации. Я пробовал циклы while и for, удалил DispatchQueue.main.async, кажется, ничего не меняет порядок вызовов. Вероятно, очень очевидно, что я делаю неправильно, но я действительно этого не понимаю.
var itemProviders: [NSItemProvider] = []
var iterator: IndexingIterator<[NSItemProvider]>?
var images = [UIImage]()
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
itemProviders = results.map(.itemProvider)
iterator = itemProviders.makeIterator()
while let itemProvider = iterator?.next(), itemProvider.canLoadObject(ofClass: UIImage.self) {
print("n Entered while")
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
guard let image = image as? UIImage else { return }
self.images.append(image)
print("Image: (image)")
print("Images: (self.images)")
}
}
}
displayAndStoreImages()
}
func displayAndStoreImages(){
print("n Entered displayAndStoreImages()")
print("Images: (images) n")
(...)
}
Это результат выбора 2 изображений в PHPicker. выполняются 2 итерации, но не код внутри блока итерации. Вместо этого выполняется следующий код, и только после этого вызывается код внутри блока итерации. Почему код вне цикла выполняется во время цикла? TIA
Entered while Entered while Entered displayAndStoreImages() Images: [] Image: <UIImage:0x6000010ec3f0 anonymous {4288, 2848}> Images: [<UIImage:0x6000010ec3f0 anonymous {4288, 2848}>] Image: <UIImage:0x6000010d4630 anonymous {3000, 2002}> Images: [<UIImage:0x6000010ec3f0 anonymous {4288, 2848}>, <UIImage:0x6000010d4630 anonymous {3000, 2002}>]
Комментарии:
1. Поскольку вы выполняете асинхронные вызовы при загрузке изображений, функция displayAndStoreImages() вызывается до загрузки файлов и выполнения закрытия
2. @JoakimDanielson Я получаю те же результаты без DispatchQueue.main.async, я пробовал это перед публикацией.
3.
itemProvider.loadObject
является асинхронной функцией… так вот почему вы получаете его с / безDispatchQueue.main.async
.4. @NewDev Спасибо за дополнительные разъяснения, теперь я понял!
5. @NewDev Каковы альтернативы синхронному вызову? Я пробовал несколько альтернативных решений, но, похоже, ничего не работает. Я просто хочу получить выбранные изображения в исходном размере.
Ответ №1:
Для такого рода асинхронной работы вам нужно что-то вроде DispatchGroup
.
Отредактировал ваш пример, чтобы использовать его
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
itemProviders = results.map(.itemProvider)
iterator = itemProviders.makeIterator()
let dispatchGroup = DispatchGroup()
while let itemProvider = iterator?.next(), itemProvider.canLoadObject(ofClass: UIImage.self) {
dispatchGroup.enter()
print("n Entered while")
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
guard let image = image as? UIImage else { return }
self.images.append(image)
print("Image: (image)")
print("Images: (self.images)")
dispatchGroup.leave()
}
}
}
dispatchGroup.notify(queue: DispatchQueue.main) {
self.displayAndStoreImages()
}
}
func displayAndStoreImages(){
print("n Entered displayAndStoreImages()")
print("Images: (images) n")
}
Комментарии:
1. Извините за поздний ответ! Спасибо за ваш ответ, полезно знать о DispatchGroup. Решение работает, но по какой-то причине между закрытием средства выбора и отображением изображений проходит несколько секунд.
2. Задержка не связана с предлагаемым решением, моя ошибка.