Search code examples
iosswiftuiimagepickercontrolleravplayeruiimagepickercontrollermediatype

Why is imagePickerController.mediaTypes = [kUTTypeMovie as String] including Live Photos as Bounce or Loop Effect as Video?


I'm using the UIImagePicker to pick only videos from my library and I set my media type to:

imagePicker.mediaTypes = [kUTTypeMovie as String]

I noticed that when the library is presented, even though I expect only videos, if there is a live photo in the library with a Bounce or Loop effect, it shows up also. If I take that same photo and switch it's effect to Live or Long Exposure it will no longer show up in the library. If I switch it back to Bounce or Loop it reappears.

I've tried both the .photoLibrary and the .savedPhotosAlbum and the same thing happens.

The reason I ask is because I want to keep them out of the library and I only want videos of xxx secs or more to be picked.

  1. Why do these effect types show up in the image picker library even though I'm picking video only?
  2. Why does only Loop and Bounce appear but Long Exposure and Live doesn't?
  3. How do I keep them out?

Maybe this would be a separate question but since it pertains to the same Live and Bounce photo issue I added it. I noticed that if I put a url that was derived from a Live or Bounce photo inside an AVPlayer, if I go to the background and come back there is a brief freeze when the app is back in the foreground, and it only happens with those 2 effects (which shouldn't be available). When the app goes to the background I remove the AVPlayerLayer correctly using this method Remove AVPlayerLayer but it's still briefly frozen (app is unresponsive) in the foreground when a url from those to effect types are initialized inside the player.

code:

import MobileCoreServices
import AVFoundation

class ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate{

var playerItem: AVPlayerItem?
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
var currentPlayTime: CMTime?
let imagePicker = UIImagePickerController()

override func viewDidLoad() {
    super.viewDidLoad()

    imagePickerController.delegate = self

    NotificationCenter.default.addObserver(self, selector: #selector(appHasEnteredBackground), name: Notification.Name.UIApplicationWillResignActive, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: Notification.Name.UIApplicationWillEnterForeground, object: nil)
}

override func viewDidLayoutSubviews() {
    // I have a UIView Outlet for the AVPlayerLayer named viewForPlayerLayer.

    guard let playerLayer = playerLayer else { return }
    playerLayer.videoGravity = .resizeAspectFill
    viewForPlayerLayer.layer.insertSublayer(playerLayer, at: 0)
    playerLayer.frame = viewForPlayerLayer.bounds
    playerLayer.masksToBounds = true

}

@IBAction fileprivate func showLibraryButtonTapped(_ sender: UIButton) {

    imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary // .savedPhotosAlbum also tried 
    imagePicker.mediaTypes = [kUTTypeMovie as String]
    imagePicker.videoMaximumDuration = 60
    present(imagePicker, animated: true, completion: nil)
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    if let url = info[UIImagePickerControllerMediaURL] as? URL{

          let asset = AVAsset(url: url)
          playerItem = AVPlayerItem(asset: asset)
          player = AVPlayer(playerItem: playerItem!)
          playerLayer = AVPlayerLayer(player: player)
          player?.play()
    }
    imagePicker.dismiss(animated: true, completion: nil)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    imagePicker.dismiss(animated: true, completion: nil)
}

@objc fileprivate func appHasEnteredBackground() {

    currentPlayTime = player.currentTime()

    if player.isPlaying{
        player.pause()
    }

    playerLayer = nil
}

@objc fileprivate func appWillEnterForeground(){

    if let player = player, let currentPlayTime = currentPlayTime{

        playerLayer = AVPlayerLayer(player: player)

        player.seek(to: currentPlayTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
    }
}

}

Solution

  • You cannot prevent loop/bounce live photo videos appearing in the picker if you have asked to show videos, because they are videos. However, you can detect one if the user chooses one, in the delegate method. For this purpose, the UIImagePickerControllerMediaType is insufficiently fine-grained. Instead, get photo library authorization and get the PHAsset returned by the UIImagePickerControllerPHAsset key, and examine its playbackStyle. This will draw the distinctions you want.

    https://developer.apple.com/documentation/photos/phasset.playbackstyle

    .videoLooping is a bounce or zoom live photo.