У вас есть разрешение на чтение видеофайлов, но нет файлов изображений в Swift? - PullRequest
0 голосов
/ 03 июня 2018

Я написал средство выбора изображений ( NOT UIImagePickerController) в Swift, после нажатия «Готово» я возвращаю URL-адреса выбранных PHAssets (изображения или видео или оба), а затем загружаю их в Firebaseхранилище.

URL-адрес изображения выглядит следующим образом: file:///var/mobile/Media/DCIM/101APPLE/IMG_1239.PNG

URL-адрес видео выглядит следующим образом: file:///var/mobile/Media/DCIM/101APPLE/IMG_1227.MOV

Затем я отображаю PHAsset.Не было проблем с воспроизведением видео актива:

let player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = view.frame
playerLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(playerLayer)
player.play()

Но когда я пытаюсь отобразить изображение, используя его URL, я получаю сообщение об ошибке «нет разрешения на чтение файла»:

do {
    let data = try Data(contentsOf: url)
    imageView.image = UIImage(data: data)
} catch {
    print(error)
}

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

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

-------------- ОБНОВЛЕНИЕ --------------

Вот так я получаю URL-адреса PHAsset

extension PHAsset {
    func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)) {
        if self.mediaType == .image {
            let options = PHContentEditingInputRequestOptions()
            options.canHandleAdjustmentData = { (adjustmeta: PHAdjustmentData) -> Bool in
                return true
            }
            self.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) in
                completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
            })
        } else if self.mediaType == .video {
            let options = PHVideoRequestOptions()
            options.version = .original
            PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) in
                if let urlAsset = asset as? AVURLAsset {
                    let localVideoUrl: URL = urlAsset.url as URL
                    completionHandler(localVideoUrl)
                } else {
                    completionHandler(nil)
                }
            })
        }
    }
}

1 Ответ

0 голосов
/ 04 июня 2018

Приложения в iOS работают в режиме «песочницы», который запрещает доступ к файлам за пределами собственной области.
URL-адрес, такой как:

file: /// var / mobile / Media /DCIM / 101APPLE /...

не входит в сферу вашего приложения и, следовательно, не доступен напрямую через Data(contentsOf:).

AVPlayer, однако может получить доступ извнеurl s, потому что у него есть специальное право, и поэтому вы смогли загрузить видео активы с помощью url.

Чтобы загрузить изображение с url, мы бы использовали этот url вPHAssets.fetchAssets(withALAssetURLs:options:) чтобы вернуть PHAsset для загрузки, но этот метод устарел в iOS 11.

В качестве альтернативы, PHAssets.fetchAssets(withLocalIdentifiers:options:) доступен, но для его использования вы будетенеобходимо предоставить localIdentifier.
. Для этого вместо сохранения url необходимо сохранить PHAsset s localIdentifier s.


. Резюме:

  1. Получите PHAsset s
  2. Сохраните asset.localIdentifier s вместо их url s
  3. При необходимости верните PHAssetиспользуя [1] :

    PHAssets.fetchAssets(withLocalIdentifiers:options:)
    
  4. Обработайте PHAsset в соответствии с mediaType

    • для изображения [2] :

      PHImageManager().requestImageData(for:options:resultHandler:)
      
    • для видео [3] :

      PHImageManager().requestAVAsset(forVideo:options:resultHandler:)
      

Общий пример:

Предупреждение: fatalError() был использован для преднамеренного завершения работы приложения, если что-то не так в вашей настройке.
В случае сбоя проверьте ваш Info.plist для NSPhotoLibraryUsageDescription и / или статус разрешения для библиотеки фотографий через PHPhotoLibrary.requestAuthorization(_:)

  1. Демонстрационная функция для получения последнего актива (изображение / видео;Вы решаете)

    func getLatestAsset(for type: PHAssetMediaType) {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate",
                                                         ascending: false)]
        fetchOptions.fetchLimit = 2
    
        let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: type,
                                                             options: fetchOptions)
    
        guard let asset = fetchResult.firstObject else { fatalError("Unable to fetch photos") }
    
        //save `localIdentifier` for later use (app relaunch or whatever)
        let assetIdentifier = asset.localIdentifier
    
        print(assetIdentifier)
    
        //Just a demo that you can get the same asset based on a local identifier
        loadAsset(identifier: assetIdentifier)
    }
    
  2. Функция загрузки актива на основе localIdentifier

    func loadAsset(identifier: String) {
        guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier],
                                              options: nil).firstObject
            else {
                fatalError("No Asset found with identifier: \(identifier)")
        }
    
        print(asset.localIdentifier)
    
        //Do something with the asset now
        if asset.mediaType == .image {
            loadImage(asset: asset)
        }
        else if asset.mediaType == .video {
            loadVideo(asset: asset)
        }
    }
    
  3. Загрузка изображения из PHAsset

    func loadImage(asset: PHAsset) {
        PHImageManager().requestImageData(for: asset, options: nil) { (data, string, orientation, userInfo) in
            guard let data = data else { fatalError("Image Data is nil") }
    
            DispatchQueue.main.async {
                self.imageView.image = UIImage(data: data)
            }
        }
    }
    
  4. Загрузка видео с PHAsset

    func loadVideo(asset: PHAsset) {
        PHImageManager().requestAVAsset(forVideo: asset, options: nil) { (avAsset, audioMix, userInfo) in
            guard let avAsset = avAsset else { fatalError("AVAsset is nil") }
    
            let playerItem = AVPlayerItem(asset: avAsset)
            let player = AVPlayer(playerItem: playerItem)
            let playerLayer = AVPlayerLayer(player: player)
            playerLayer.frame = self.view.frame
            playerLayer.videoGravity = .resizeAspectFill
    
            DispatchQueue.main.async {
                self.view.layer.addSublayer(playerLayer)
                player.play()
            }
        }
    }
    

PS: вам больше не потребуетсяgetURL(completionHandler:) потому что вся ваша логика будет основана на localIdentifier с вместо url с, и я скажу, что это намного лучше.
Url с просто, вроде, переменчивы, поэтому давайте просто избегать их здесь, по крайней мере.

...