110
Media Picker To innity and beyond! Андрей Юткин

Андрей Юткин. Media Picker — to infinity and beyond

Embed Size (px)

Citation preview

Page 1: Андрей Юткин. Media Picker — to infinity and beyond

Media PickerTo infinity and beyond!

Андрей Юткин

Page 2: Андрей Юткин. Media Picker — to infinity and beyond

Demo

Page 3: Андрей Юткин. Media Picker — to infinity and beyond

UIImagePickerController

3

Page 4: Андрей Юткин. Media Picker — to infinity and beyond

UIImagePickerControllerлибо камера, либо галерея

3

Page 5: Андрей Юткин. Media Picker — to infinity and beyond

UIImagePickerControllerлибо камера, либо галерея

должен быть представлен модально

3

Page 6: Андрей Юткин. Media Picker — to infinity and beyond

UIImagePickerControllerлибо камера, либо галерея

должен быть представлен модально

может крэшиться до вызова метода делегата

3

Page 7: Андрей Юткин. Media Picker — to infinity and beyond

Камера: готовые решенияMWPhotoBrowser

DBCamera

FastttCamera

LLSimpleCamera

SKFCamera

ALCameraViewController

TGCameraViewController

MMSCameraViewController

4

Page 8: Андрей Юткин. Media Picker — to infinity and beyond

AVFoundation

5

Page 9: Андрей Юткин. Media Picker — to infinity and beyond

AVFoundation

5

AVCaptureSession

Page 10: Андрей Юткин. Media Picker — to infinity and beyond

AVFoundation

5

AVCaptureSession

AVCaptureDeviceInput

AVCaptureDevice (Camera)

Page 11: Андрей Юткин. Media Picker — to infinity and beyond

AVFoundation

5

AVCaptureSession

AVCaptureStillImageOutput AVCaptureMovieFileOutput

AVCaptureDeviceInput

AVCaptureDevice (Camera)

Page 12: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureConnection AVCaptureConnection

AVFoundation

5

AVCaptureSession

AVCaptureStillImageOutput AVCaptureMovieFileOutput

AVCaptureDeviceInput

AVCaptureDevice (Camera)

Page 13: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSession

6

Page 14: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)

let backCamera = videoDevices?.first { $0.position == .back }

let input = try AVCaptureDeviceInput(device: backCamera)

6

Page 15: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet videoDevices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo)

let backCamera = videoDevices?.first { $0.position == .back }

let input = try AVCaptureDeviceInput(device: backCamera)

let output = AVCaptureStillImageOutput() output.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]

6

Page 16: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet captureSession = AVCaptureSession() captureSession.sessionPreset = AVCaptureSessionPresetPhoto

if captureSession.canAddInput(input) { captureSession.addInput(input) }

if captureSession.canAddOutput(output) { captureSession.addOutput(output) }

captureSession.startRunning()

7

Page 17: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet captureSession = AVCaptureSession() captureSession.sessionPreset = AVCaptureSessionPresetPhoto

if captureSession.canAddInput(input) { captureSession.addInput(input) }

if captureSession.canAddOutput(output) { captureSession.addOutput(output) }

captureSession.startRunning()

7

Page 18: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet captureSession = AVCaptureSession() captureSession.sessionPreset = AVCaptureSessionPresetPhoto

if captureSession.canAddInput(input) { captureSession.addInput(input) }

if captureSession.canAddOutput(output) { captureSession.addOutput(output) }

captureSession.startRunning()

7

Page 19: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet captureSession = AVCaptureSession() captureSession.sessionPreset = AVCaptureSessionPresetPhoto

if captureSession.canAddInput(input) { captureSession.addInput(input) }

if captureSession.canAddOutput(output) { captureSession.addOutput(output) }

captureSession.startRunning()

7

Page 20: Андрей Юткин. Media Picker — to infinity and beyond

Настройка AVCaptureSessionlet captureSession = AVCaptureSession() captureSession.sessionPreset = AVCaptureSessionPresetPhoto

if captureSession.canAddInput(input) { captureSession.addInput(input) }

if captureSession.canAddOutput(output) { captureSession.addOutput(output) }

captureSession.startRunning()

7

В фоновом потоке!

Page 21: Андрей Юткин. Media Picker — to infinity and beyond

Отображение превьюlet layer = AVCaptureVideoPreviewLayer(session: captureSession)

cameraOutputView.layer.addSublayer(layer)

8

Page 22: Андрей Юткин. Media Picker — to infinity and beyond

Отображение превьюlet layer = AVCaptureVideoPreviewLayer(session: captureSession)

cameraOutputView.layer.addSublayer(layer)

9

Page 23: Андрей Юткин. Media Picker — to infinity and beyond

Отображение превьюlet layer = AVCaptureVideoPreviewLayer(session: captureSession)

cameraOutputView.layer.addSublayer(layer)

9

1 AVCaptureSession = 1 AVCaptureVideoPreviewLayer

Page 24: Андрей Юткин. Media Picker — to infinity and beyond

Отображение превьюlet layer = AVCaptureVideoPreviewLayer(session: captureSession)

cameraOutputView.layer.addSublayer(layer)

9

Несколько AVCaptureSession не могут работать одновременно

1 AVCaptureSession = 1 AVCaptureVideoPreviewLayer

Page 25: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureStillImageOutput

Несколько превью

10

AVCaptureSession

Page 26: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureStillImageOutput AVCaptureVideoDataOutput

Несколько превью

10

AVCaptureSession

Page 27: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_:didOutputSampleBuffer:from:)

AVCaptureStillImageOutput AVCaptureVideoDataOutput

Несколько превью

10

AVCaptureSession

Page 28: Андрей Юткин. Media Picker — to infinity and beyond

Рендеринг CMSampleBuffer

11

UIViewCMSampleBuffer

Page 29: Андрей Юткин. Media Picker — to infinity and beyond

iPhone 5s и выше

Рендеринг CMSampleBuffer

11

UIViewCMSampleBuffer

Page 30: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureVideoDataOutputSampleBufferDelegate

func captureOutput( _: AVCaptureOutput?, didOutputSampleBuffer sampleBuffer: CMSampleBuffer?, from _: AVCaptureConnection?) { let imageBuffer: CVImageBuffer? = sampleBuffer.flatMap { CMSampleBufferGetImageBuffer($0) } if let imageBuffer = imageBuffer, !isInBackground { views.forEach { $0.imageBuffer = imageBuffer } } }

var views = [GLKViewSubclass]()

12

Page 31: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureVideoDataOutputSampleBufferDelegate

func captureOutput( _: AVCaptureOutput?, didOutputSampleBuffer sampleBuffer: CMSampleBuffer?, from _: AVCaptureConnection?) { let imageBuffer: CVImageBuffer? = sampleBuffer.flatMap { CMSampleBufferGetImageBuffer($0) } if let imageBuffer = imageBuffer, !isInBackground { views.forEach { $0.imageBuffer = imageBuffer } } }

var views = [GLKViewSubclass]()

12

Page 32: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureVideoDataOutputSampleBufferDelegate

func captureOutput( _: AVCaptureOutput?, didOutputSampleBuffer sampleBuffer: CMSampleBuffer?, from _: AVCaptureConnection?) { let imageBuffer: CVImageBuffer? = sampleBuffer.flatMap { CMSampleBufferGetImageBuffer($0) } if let imageBuffer = imageBuffer, !isInBackground { views.forEach { $0.imageBuffer = imageBuffer } } }

var views = [GLKViewSubclass]()

12

Page 33: Андрей Юткин. Media Picker — to infinity and beyond

AVCaptureVideoDataOutputSampleBufferDelegate

func captureOutput( _: AVCaptureOutput?, didOutputSampleBuffer sampleBuffer: CMSampleBuffer?, from _: AVCaptureConnection?) { let imageBuffer: CVImageBuffer? = sampleBuffer.flatMap { CMSampleBufferGetImageBuffer($0) } if let imageBuffer = imageBuffer, !isInBackground { views.forEach { $0.imageBuffer = imageBuffer } } }

var views = [GLKViewSubclass]()

12

Page 34: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 35: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 36: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 37: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 38: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 39: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 40: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 41: Андрей Юткин. Media Picker — to infinity and beyond

GLKViewSubclass// instance vars: let eaglContext = EAGLContext(api: .openGLES2) let ciContext = CIContext(eaglContext: eaglContext) var imageBuffer: CVImageBuffer?

// draw(_:) implementation: if let imageBuffer = imageBuffer { let image = CIImage(cvPixelBuffer: imageBuffer)

ciContext.draw( image, in: drawableBounds(for: rect), from: sourceRect(of: image, targeting: rect) ) }

13

Page 42: Андрей Юткин. Media Picker — to infinity and beyond

OpenGL и background back in AVCaptureVideoDataOutputSampleBufferDelegate

14

Page 43: Андрей Юткин. Media Picker — to infinity and beyond

OpenGL и background back in AVCaptureVideoDataOutputSampleBufferDelegate

// UIApplicationWillResignActive func handleAppWillResignActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.sync { glFinish() self.isInBackground = true } }

// UIApplicationDidBecomeActive func handleAppDidBecomeActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.async { self.isInBackground = false } }

14

Page 44: Андрей Юткин. Media Picker — to infinity and beyond

OpenGL и background back in AVCaptureVideoDataOutputSampleBufferDelegate

// UIApplicationWillResignActive func handleAppWillResignActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.sync { glFinish() self.isInBackground = true } }

// UIApplicationDidBecomeActive func handleAppDidBecomeActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.async { self.isInBackground = false } }

14

Page 45: Андрей Юткин. Media Picker — to infinity and beyond

OpenGL и background back in AVCaptureVideoDataOutputSampleBufferDelegate

// UIApplicationWillResignActive func handleAppWillResignActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.sync { glFinish() self.isInBackground = true } }

// UIApplicationDidBecomeActive func handleAppDidBecomeActive(_: NSNotification) { captureOutputDelegateBackgroundQueue.async { self.isInBackground = false } }

14

Page 46: Андрей Юткин. Media Picker — to infinity and beyond

Summary: превью камеры

15

Page 47: Андрей Юткин. Media Picker — to infinity and beyond

Summary: превью камеры

15

AVCaptureVideoDataOutput

AVCaptureSession

Page 48: Андрей Юткин. Media Picker — to infinity and beyond

Output Delegate

Summary: превью камеры

15

AVCaptureVideoDataOutput

AVCaptureSession

Page 49: Андрей Юткин. Media Picker — to infinity and beyond

Output Delegate

Summary: превью камеры

15

AVCaptureVideoDataOutput

AVCaptureSession

Page 50: Андрей Юткин. Media Picker — to infinity and beyond

Output Delegate

Summary: превью камеры

15

AVCaptureVideoDataOutput

AVCaptureSession

Page 51: Андрей Юткин. Media Picker — to infinity and beyond

Источники фотографий

16

Page 52: Андрей Юткин. Media Picker — to infinity and beyond

Источники фотографийфайловая система

16

Page 53: Андрей Юткин. Media Picker — to infinity and beyond

Источники фотографийфайловая система

пользовательская галерея

16

Page 54: Андрей Юткин. Media Picker — to infinity and beyond

Источники фотографийфайловая система

пользовательская галерея

сеть

16

Page 55: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?

17

Page 56: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

17

Page 57: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

получить оригинал

17

Page 58: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

получить оригинал

узнать размер

17

Page 59: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

получить оригинал

узнать размер

отменить загрузку

17

Page 60: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

получить оригинал

узнать размер

отменить загрузку

18

Page 61: Андрей Юткин. Media Picker — to infinity and beyond

Что нам нужно от фото?отобразить в UI

получить оригинал

узнать размер

отменить загрузку

18

— асинхронно

Page 62: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UI

19

Page 63: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIlet viewSize: CGSizelet contentMode: ContentMode // enum: aspectFit/aspectFill

19

Page 64: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIlet viewSize: CGSizelet contentMode: ContentMode // enum: aspectFit/aspectFill

let handler = { (image: UIImage?) in imageView.image = image}

19

Page 65: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIlet viewSize: CGSizelet contentMode: ContentMode // enum: aspectFit/aspectFill

let handler = { (image: UIImage?) in imageView.image = image}

let deliveryMode: DeliveryMode // enum: progressive/best

19

Page 66: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIfunc requestImage(

20

viewSize: CGSize, contentMode: ContentMode, deliveryMode: DeliveryMode, handler: @escaping (UIImage?) -> ())

Page 67: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIfunc requestImage(

20

handler: @escaping (UIImage?) -> ()) options: ImageRequestOptions,

struct ImageRequestOptions { let viewSize: CGSize let contentMode: ContentMode let deliveryMode: DeliveryMode }

Page 68: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIprotocol InitializableWithCGImage { init(cgImage: CGImage) }

21

Page 69: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIprotocol InitializableWithCGImage { init(cgImage: CGImage) }

extension UIImage: InitializableWithCGImage {} extension NSImage: InitializableWithCGImage {}

21

Page 70: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIfunc requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (

22

) -> ())T?

Page 71: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIfunc requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (

22

-> ImageRequestId) -> ())T?

Page 72: Андрей Юткин. Media Picker — to infinity and beyond

Отображение в UIfunc requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (

22

-> ImageRequestId

struct ImageRequestResult<T> { let image: T? let degraded: Bool let requestId: ImageRequestId }

) -> ())ImageRequestResult<T>

Page 73: Андрей Юткин. Media Picker — to infinity and beyond

ImageSourceprotocol ImageSource { func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, resultHandler: @escaping (ImageRequestResult<T>) -> ()) -> ImageRequestId

func fullResolutionImageData(completion: @escaping (Data?) -> ())

func imageSize(completion: @escaping (CGSize?) -> ()) func cancelRequest(_: ImageRequestId) }

23

Page 74: Андрей Юткин. Media Picker — to infinity and beyond

ImageSourceprotocol ImageSource { func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, resultHandler: @escaping (ImageRequestResult<T>) -> ()) -> ImageRequestId

func fullResolutionImageData(completion: @escaping (Data?) -> ())

func imageSize(completion: @escaping (CGSize?) -> ()) func cancelRequest(_: ImageRequestId) }

23

Page 75: Андрей Юткин. Media Picker — to infinity and beyond

ImageSourceprotocol ImageSource { func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, resultHandler: @escaping (ImageRequestResult<T>) -> ()) -> ImageRequestId

func fullResolutionImageData(completion: @escaping (Data?) -> ())

func imageSize(completion: @escaping (CGSize?) -> ()) func cancelRequest(_: ImageRequestId) }

23

Page 76: Андрей Юткин. Media Picker — to infinity and beyond

Фотогалерея пользователя

24

Photos.framework

PHPhotoLibrary

PHAssetPHAssetPHAssetPHAsset

Page 77: Андрей Юткин. Media Picker — to infinity and beyond

Фотогалерея пользователя

24

Photos.framework

PHPhotoLibrary

PHAssetPHAssetPHAssetPHAssetPHImageManager

UIImage

Page 78: Андрей Юткин. Media Picker — to infinity and beyond

PHImageManagerfunc requestImage( for: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, resultHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> ()) -> PHImageRequestID

25

Page 79: Андрей Юткин. Media Picker — to infinity and beyond

Наш requestImage для PHAssetpublic func requestImage<T : InitializableWithCGImage>( options: ImageRequestOptions, resultHandler: @escaping (ImageRequestResult<T>) -> ()) -> ImageRequestId { let (phOptions, size, contentMode) = imageRequestParameters(from: options) var downloadStarted = false var downloadFinished = false let startDownload = { (imageRequestId: ImageRequestId) in downloadStarted = true if let onDownloadStart = options.onDownloadStart { dispatch_to_main_queue { onDownloadStart(imageRequestId) } } } let finishDownload = { (imageRequestId: ImageRequestId) in downloadFinished = true if let onDownloadFinish = options.onDownloadFinish { dispatch_to_main_queue { onDownloadFinish(imageRequestId) } } } phOptions.progressHandler = { progress, _, _, info in let imageRequestId = (info?[PHImageResultRequestIDKey] as? NSNumber)?.int32Value ?? 0 if !downloadStarted { startDownload(imageRequestId.toImageRequestId()) } if progress == 1 /* это не reliable, читай ниже */ && !downloadFinished { finishDownload(imageRequestId.toImageRequestId()) } }

let id = imageManager.requestImage(for: asset, targetSize: size, contentMode: contentMode, options: phOptions) { [weak self] image, info in let requestId = (info?[PHImageResultRequestIDKey] as? NSNumber)?.int32Value ?? 0 let degraded = (info?[PHImageResultIsDegradedKey] as? NSNumber)?.boolValue ?? false let cancelled = (info?[PHImageCancelledKey] as? NSNumber)?.boolValue ?? false || self?.cancelledRequestIds.contains(requestId.toImageRequestId()) == true let isLikelyToBeTheLastCallback = (image != nil && !degraded) || cancelled // progressHandler может никогда не вызваться с progress == 1, поэтому тут пытаемся угадать, завершилась ли загрузка if downloadStarted && !downloadFinished && isLikelyToBeTheLastCallback { finishDownload(requestId.toImageRequestId()) } // resultHandler не должен вызываться после отмены запроса if !cancelled { resultHandler(ImageRequestResult( image: (image as? T?).flatMap { $0 } ?? image?.cgImage.flatMap { T(cgImage: $0) }, degraded: degraded, requestId: requestId.toImageRequestId() )) } } return id.toImageRequestId() }

26

Page 80: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запроса

27

Page 81: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запросаPHImageManager

иногда вызывается, иногда — нет

иногда приходит UIImage, иногда — нет

27

Page 82: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запросаPHImageManager

иногда вызывается, иногда — нет

иногда приходит UIImage, иногда — нет

ImageSource для PHAsset

не вызывается

27

Page 83: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запроса Отменен ли запрос?

// внутри resultHandler PHImageManager’а

let cancelled = (info?[PHImageCancelledKey] as? NSNumber)?.boolValue ?? false || cancelledRequestIds.contains(requestId)

if !cancelled { // вызываем "внешний" resultHandler }

28

Page 84: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запроса Отменен ли запрос?

// внутри resultHandler PHImageManager’а

let cancelled = (info?[PHImageCancelledKey] as? NSNumber)?.boolValue ?? false || cancelledRequestIds.contains(requestId)

if !cancelled { // вызываем "внешний" resultHandler }

28

Page 85: Андрей Юткин. Media Picker — to infinity and beyond

resultHandler после отмены запроса Отменен ли запрос?

// внутри resultHandler PHImageManager’а

let cancelled = (info?[PHImageCancelledKey] as? NSNumber)?.boolValue ?? false || cancelledRequestIds.contains(requestId)

if !cancelled { // вызываем "внешний" resultHandler }

28

Page 86: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloud

29

Page 87: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloudclass PHImageRequestOptions { // для PHImageManager var progressHandler: PHAssetImageProgressHandler? // ...}

29

Page 88: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloudclass PHImageRequestOptions { // для PHImageManager var progressHandler: PHAssetImageProgressHandler? // ...}

struct ImageRequestOptions { // для ImageSource var onDownloadStart: ((ImageRequestId) -> ())? var onDownloadFinish: ((ImageRequestId) -> ())? // ...}

29

Page 89: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloudphImageRequestOptions.progressHandler = { progress, _, _, _ in if progress == 1 { callOnDownloadFinish() } }

30

Page 90: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloud// внутри resultHandler: let degraded: Bool = info?[PHImageResultIsDegradedKey]

let looksLikeLastCallback = cancelled || (image != nil && !degraded)

if looksLikeLastCallback { callOnDownloadFinish() }

31

Page 91: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloud// внутри resultHandler: let degraded: Bool = info?[PHImageResultIsDegradedKey]

let looksLikeLastCallback = cancelled || (image != nil && !degraded)

if looksLikeLastCallback { callOnDownloadFinish() }

31

Page 92: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloud// внутри resultHandler: let degraded: Bool = info?[PHImageResultIsDegradedKey]

let looksLikeLastCallback = cancelled || (image != nil && !degraded)

if looksLikeLastCallback { callOnDownloadFinish() }

31

Page 93: Андрей Юткин. Media Picker — to infinity and beyond

Загрузка из iCloud// внутри resultHandler: let degraded: Bool = info?[PHImageResultIsDegradedKey]

let looksLikeLastCallback = cancelled || (image != nil && !degraded)

if looksLikeLastCallback { callOnDownloadFinish() }

31

Page 94: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO

Page 95: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO Эффективное получение размера

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) let width = options?[kCGImagePropertyPixelWidth] as! Int let height = options?[kCGImagePropertyPixelHeight] as! Int let orientation = options?[kCGImagePropertyOrientation] as! Int

if dimensionsSwapped(in: orientation) { return CGSize(width: height, height: width) } else { return CGSize(width: width, height: height) }

33

Page 96: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO Эффективное получение размера

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) let width = options?[kCGImagePropertyPixelWidth] as! Int let height = options?[kCGImagePropertyPixelHeight] as! Int let orientation = options?[kCGImagePropertyOrientation] as! Int

if dimensionsSwapped(in: orientation) { return CGSize(width: height, height: width) } else { return CGSize(width: width, height: height) }

33

Page 97: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO Эффективное получение размера

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) let width = options?[kCGImagePropertyPixelWidth] as! Int let height = options?[kCGImagePropertyPixelHeight] as! Int let orientation = options?[kCGImagePropertyOrientation] as! Int

if dimensionsSwapped(in: orientation) { return CGSize(width: height, height: width) } else { return CGSize(width: width, height: height) }

33

Page 98: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO Эффективное получение размера

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) let width = options?[kCGImagePropertyPixelWidth] as! Int let height = options?[kCGImagePropertyPixelHeight] as! Int let orientation = options?[kCGImagePropertyOrientation] as! Int

if dimensionsSwapped(in: orientation) { return CGSize(width: height, height: width) } else { return CGSize(width: width, height: height) }

33

Page 99: Андрей Юткин. Media Picker — to infinity and beyond

ImageIO Эффективное получение размера

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options = CGImageSourceCopyPropertiesAtIndex(source!, 0, nil) let width = options?[kCGImagePropertyPixelWidth] as! Int let height = options?[kCGImagePropertyPixelHeight] as! Int let orientation = options?[kCGImagePropertyOrientation] as! Int

if dimensionsSwapped(in: orientation) { return CGSize(width: height, height: width) } else { return CGSize(width: width, height: height) }

33

Page 100: Андрей Юткин. Media Picker — to infinity and beyond

Локальные картинки Эффективный ресайзинг

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height), kCGImageSourceCreateThumbnailWithTransform: true ]

return CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

34

Page 101: Андрей Юткин. Media Picker — to infinity and beyond

Локальные картинки Эффективный ресайзинг

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height), kCGImageSourceCreateThumbnailWithTransform: true ]

return CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

34

Page 102: Андрей Юткин. Media Picker — to infinity and beyond

Локальные картинки Эффективный ресайзинг

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height), kCGImageSourceCreateThumbnailWithTransform: true ]

return CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

34

Page 103: Андрей Юткин. Media Picker — to infinity and beyond

Локальные картинки Эффективный ресайзинг

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height), kCGImageSourceCreateThumbnailWithTransform: true ]

return CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

34

Page 104: Андрей Юткин. Media Picker — to infinity and beyond

Локальные картинки Эффективный ресайзинг

let source = CGImageSourceCreateWithURL(fileUrl, nil)

let options: [NSString: Any] = [ kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height), kCGImageSourceCreateThumbnailWithTransform: true ]

return CGImageSourceCreateThumbnailAtIndex(source!, 0, options)

34

Page 105: Андрей Юткин. Media Picker — to infinity and beyond

Images & memory

35

Page 106: Андрей Юткин. Media Picker — to infinity and beyond

Images & memoryдля операций над изображениями — Core Image, ImageIO

35

Page 107: Андрей Юткин. Media Picker — to infinity and beyond

Images & memoryдля операций над изображениями — Core Image, ImageIO

создавайте UIImage минимально необходимого размера

35

Page 108: Андрей Юткин. Media Picker — to infinity and beyond

Images & memoryдля операций над изображениями — Core Image, ImageIO

создавайте UIImage минимально необходимого размера

не храните больше UIImage, чем помещается на экране

35

Page 109: Андрей Юткин. Media Picker — to infinity and beyond

github.com/avito-tech/Paparazzo

Page 110: Андрей Юткин. Media Picker — to infinity and beyond

github.com/avito-tech/[email protected]