Добавление нескольких изображений в представление из библиотеки фотографий — SwiftUI
Я хочу добавить изображения из фотобиблиотеки телефона в макет коллажа, который я сделал. Сначала я сделал макет коллажа в виде отдельного представления в SwiftUI под названием CollageLayoutOne.
import SwiftUI
struct CollageLayoutOne: View {
var uiImageOne: UIImage
var uiImageTwo: UIImage
var uiImageThree: UIImage
var body: some View {
Rectangle()
.fill(Color.gray)
.aspectRatio(1.0, contentMode: .fit)
.overlay {
HStack {
Rectangle()
.fill(Color.gray)
.overlay {
Image(uiImage: uiImageOne)
.resizable()
.aspectRatio(contentMode: .fill)
}
.clipped()
VStack {
Rectangle()
.fill(Color.gray)
.overlay {
Image(uiImage: uiImageTwo)
.resizable()
.aspectRatio(contentMode: .fill)
}
.clipped()
Rectangle()
.fill(Color.gray)
.overlay {
Image(uiImage: uiImageThree)
.resizable()
.aspectRatio(contentMode: .fill)
}
.clipped()
}
}
.padding()
}
}
}
Затем у меня есть отдельное представление (PageView), где я хочу показать представление CollageLayoutOne, и оно также содержит кнопку для доступа к библиотеке изображений.
struct PageView: View {
@State private var photoPickerIsPresented = false
@State var pickerResult: [UIImage] = []
var body: some View {
NavigationView {
ScrollView {
if pickerResult.isEmpty {
} else {
CollageLayoutOne(uiImageOne: pickerResult[0], uiImageTwo: pickerResult[1], uiImageThree: pickerResult[2])
}
}
.edgesIgnoringSafeArea(.bottom)
.navigationBarTitle("Select Photo", displayMode: .inline)
.navigationBarItems(trailing: selectPhotoButton)
.sheet(isPresented: $photoPickerIsPresented) {
PhotoPicker(pickerResult: $pickerResult,
isPresented: $photoPickerIsPresented)
}
}
}
@ViewBuilder
private var selectPhotoButton: some View {
Button(action: {
photoPickerIsPresented = true
}, label: {
Label("Select", systemImage: "photo")
})
}
}
Моя проблема в том, что по какой-то неизвестной причине приложение вылетает каждый раз, когда я выбираю фотографии и пытаюсь их добавить. Если я делаю для всех трех, он работает нормально, но отображает только первую выбранную фотографию во всех трех местах. Также, если я начну со всех 3 как
pickerResult[0]
а затем изменить их на
[0], [1], [2]
во время предварительного просмотра он не падает и отображается правильно.
Я только начинаю со Swift и SwiftUI, так что извините, если это какая-то элементарная ошибка. Ниже я также добавляю свой код для PhotoPicker, который я получил из статьи, которую я нашел.
PhotoPicker.swift:
import SwiftUI
import PhotosUI
struct PhotoPicker: UIViewControllerRepresentable {
@Binding var pickerResult: [UIImage]
@Binding var isPresented: Bool
func makeUIViewController(context: Context) -> some UIViewController {
var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
configuration.filter = .images // filter only to images
if #available(iOS 15, *) {
configuration.selection = .ordered //number selection
}
configuration.selectionLimit = 3 // ignore limit
let photoPickerViewController = PHPickerViewController(configuration: configuration)
photoPickerViewController.delegate = context.coordinator
return photoPickerViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: PHPickerViewControllerDelegate {
private let parent: PhotoPicker
init(_ parent: PhotoPicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.pickerResult.removeAll()
for image in results {
if image.itemProvider.canLoadObject(ofClass: UIImage.self) {
image.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] newImage, error in
if let error = error {
print("Can't load image \(error.localizedDescription)")
} else if let image = newImage as? UIImage {
self?.parent.pickerResult.append(image)
}
}
} else {
print("Can't load asset")
}
}
parent.isPresented = false
}
}
}
1 ответ
image.itemProvider.loadObject
является асинхронной функцией и загружает изображения одно за другим.
Когда первое изображение обработано, вы добавляете его в
pickerResult
и ваш
pickerResult.isEmpty
проверка становится
false
, но ваш массив пока содержит только один элемент.
Безопаснее всего здесь проверить счет:
if pickerResult.count == 3 {
CollageLayoutOne(uiImageOne: pickerResult[0], uiImageTwo: pickerResult[1], uiImageThree: pickerResult[2])
}
Кроме того, в таких случаях рекомендуется дождаться завершения всех асинхронных запросов, прежде чем обновлять пользовательский интерфейс, например, так:
var processedResults = [UIImage]()
var leftToLoad = results.count
let checkFinished = { [weak self] in
leftToLoad -= 1
if leftToLoad == 0 {
self?.parent.pickerResult = processedResults
self?.parent.isPresented = false
}
}
for image in results {
if image.itemProvider.canLoadObject(ofClass: UIImage.self) {
image.itemProvider.loadObject(ofClass: UIImage.self) { newImage, error in
if let error = error {
print("Can't load image \(error.localizedDescription)")
} else if let image = newImage as? UIImage {
processedResults.append(image)
}
checkFinished()
}
} else {
print("Can't load asset")
checkFinished()
}
}