ã¡ãã£ã¢ããã©ããã«é¢ããã¹ããŒãªãŒã®æåã®éšå
第1éšã§ã¯ãã¡ãã£ã¢ã¹ããŒã«ãŒã«ãã©ãçããŸã§ã®çµç·¯ãšããã®åã«çµéšãããªãã·ã§ã³ã®æ°ã«ã€ããŠèª¬æããŸãããä»ãã話ãç¶ããŸãããã

åçã®ããŸããŸãªãœãŒã¹
ç§ãã¡ãçŽé¢ãã次ã®ã¿ã¹ã¯ã¯ãMediaPickerã®åçã3ã€ã®ç°ãªããœãŒã¹ããååŸã§ããããšã§ãã
- ã«ã¡ã©ã§æ®åœ±ããåçã¯ãã¢ããªã±ãŒã·ã§ã³ãã©ã«ããŒã®ãã£ã¹ã¯ã«ä¿åãããŸããã
- ãŠãŒã¶ãŒã®ãã©ãã®ã£ã©ãªãŒããåçãéžæããããšãã§ããŸãã
- æåŸã«ãæ¢ã«æçš¿ãããåºåãç·šéçšã«éããšããããã¯ãããã¯ãŒã¯ããååŸãããŸãã
ãã¡ããã3ã€ã§ã¯ãªã1ã€ã®ãšã³ãã£ãã£ãå¿ èŠã§ããããã®ãããåçã§åäœããã³ãŒãã§ã¯ãbranchesããã©ã³ããäœæããå¿ èŠããªããæ°ããããŒã¿ãœãŒã¹ãçªç¶åºçŸããå Žåã«å€æŽããä¿è·ããŸãã
ç»åãæäœãããšãã«å®è¡ããå¿ èŠããã4ã€ã®ã¢ã¯ã·ã§ã³ãç¹å®ããŸããã
- æãäžè¬çãªã¢ã¯ã·ã§ã³ã¯ããã¡ãããã€ã³ã¿ãŒãã§ãŒã¹ã«è¡šç€ºããããšã§ãã ãããŠãåçã®å°ããªãµã ãã€ã«ããšã«ç»é¢ã®ãµã€ãºã«åãããŠèª¿æŽããå¿
èŠããªãããã«ãµã€ãºã®åçã3 x 4000ãã¯ã»ã«ã®ãµã€ãºã§ä¿åããŠãããšããã§ãããã
- 次ã¯ãå
ã®ã€ã¡ãŒãžã®åä¿¡ã§ããããšãã°ããµãŒããŒã«éä¿¡ãããããã£ã¹ã¯ã«ä¿åãããããŸãã ç¹°ãè¿ããŸãããããã§ã¯ã§ããã ãã¡ã¢ãªãããŒãããããªãã®ã§ãNSData圢åŒã®å§çž®è¡šçŸãå¿
èŠã§ããããšãã°ãJPEGããããããããã«ç»åããã³ãŒãããããã«ã·ã¹ãã ãªãœãŒã¹ãè²»ããå¿
èŠã¯ãããŸããã
- ãŸããç»åã®ãµã€ãºã調ã¹ãããšãå¿
èŠãªå ŽåããããŸããããããæé©ãªæ¹æ³ã§ããé©åã«è¡ãå¿
èŠããããŸããã€ãŸããå¯èœã§ããã°ãå®å
šã«ããŠã³ããŒãããããã®ããã ãã«ã¡ã¢ãªã§ãã£ã±ãã«ããªãã§ãã ããã å€ãã®å Žåããµã€ãºã¯ãã¡ã€ã«ã®ã¡ã¿ããŒã¿ããååŸã§ããŸãããŸãã¯ããµãŒããŒã¯URLã®æšªã«ããJSONæ§é ã®åå¥ã®ããããã£ãšããŠãããéä¿¡ã§ããŸãã
- æåŸã«ãç»åããããã¯ãŒã¯ããããŠã³ããŒããããããããæç¹ã§çµ¶å¯Ÿã«å¿
èŠãªãããšãããã£ãå ŽåïŒããšãã°ã衚瀺ãããã¯ãã ã£ãç»é¢ãéããå ŽåïŒãããŠã³ããŒãããã£ã³ã»ã«ã§ãããšäŸ¿å©ã§ãã
ãã¡ãããã€ã¡ãŒãžãå¿ èŠãªãšãã«ããŒã«ã«ã§å©çšã§ããªãå Žåããããããæåã®3ã€ã®ãã€ã³ãã®APIã¯éåæã§ããå¿ èŠããããŸãã ã¡ã¢ãªã«åé·ããŒã¿ãããŒãããã«UIã«ç»åã衚瀺ããã«ã¯ãå¿ èŠãªãµã€ãºãèŠã€ããå¿ èŠããããŸãã
- ãããè¡ãã«ã¯ã衚瀺ãè¡ãããé åã®ãµã€ãºãšãã®äœ¿çšæ¹æ³ãç¥ãå¿
èŠããããŸãïŒç»åãå®å
šã«ãã£ããããããã®ã§ããããããšãäžéšãç ç²ã«ããŠå
éšã«ç©ºãã¹ããŒã¹ããªãããã«ããŸããïŒã³ã³ãã³ãã¢ãŒãã®AspectFitãšåæ§ïŒããã³UIViewã®aspectFillïŒã
- APIã¯éåæã§ããå¿
èŠããããããçµæã®ç»åãUIImageViewã«æž¡ããã³ãã©ãŒãå¿
èŠã§ãã
- ãããã¯ãŒã¯ããåçãã¢ããããŒãããå¿
èŠãããå ŽåããããŸãããåæã«åãã€ã¡ãŒãžã®ãã£ãã·ã¥ããŒãžã§ã³ãããŒã«ã«ã«ãããŸãããããããå°ãããªããŸãã ãããŠããã®çž®å°çãããŒãæã«ãã¥ãŒã«ä»£å
¥ãããšããŠãŒã¶ãŒã¯ããŠã³ããŒããé«éã§ãããšããå°è±¡ãåããããšã«ãªããŸãã
- ãã®ãããããã°ã¬ãã·ãå€ãèšå®ããdeliveryModeãã©ã¡ãŒã¿ãŒãåé¡ãããŸãããèŠæ±ãããç»åã®äžè¯ããŒãžã§ã³ã«å察ã§ã¯ãªããå質ãåäžãããã³ã«ãã³ãã©ãŒãæ°ååŒã³åºãããšãã§ããŸãã æè¯ãšã¯ãæè¯ã®ããŒãžã§ã³ã®ç»åã䜿çšããŠãã³ãã©ãŒã1åã ãåŒã³åºãããšãæå³ããŸãã
ãããã£ãŠããªã¹ãããããã©ã¡ãŒã¿ã䜿çšããŠç»åãèŠæ±ããæ¹æ³ã¯ã次ã®ããã«ãªããŸãã
func requestImage( viewSize: CGSize, contentMode: ContentMode, deliveryMode: DeliveryMode, handler: @escaping (UIImage?) -> ())
æåã®3ã€ã®ãã©ã¡ãŒã¿ãŒã1ã€ã®æ§é ã«çµåããããšã§ççž®ããŸãã ããã«ãããã¡ãœããã·ã°ããã£ãå€æŽããã«ãå¿ èŠã«å¿ããŠä»ã®ãã©ã¡ãŒã¿ãŒãè¿œå ã§ããŸãã
func requestImage( options: ImageRequestOptions, handler: @escaping (UIImage?) -> ()) struct ImageRequestOptions { let viewSize: CGSize let contentMode: ContentMode let deliveryMode: DeliveryMode }
çµæã®ããŒãžã§ã³ã¯ãŸã 確å®ããå¿ èŠããããŸãã ãŸããUIImageåã¯ãã³ãã©ãŒå¥ãã©ã¡ãŒã¿ãŒã§æ瀺çã«æå®ãããŠãããUIKitãåé€ããŠãiOSã ãã§ãªããã®ã¡ãœããã䜿çšã§ããããã«ããŸããã
ãããã£ãŠãUIImageã¯ãåŸã§UIImageã«å€æã§ãããã®ã«çœ®ãæããå¿ èŠããããŸãã ãã®åºæºãæºãããiOSãšmacOSã®äž¡æ¹ã«ååšããã¿ã€ãããããŸã-ããã¯CGImageã§ãã
ãããã£ãŠãInitializableWithCGImageãããã³ã«ãäœæããŸãã
protocol InitializableWithCGImage { init(cgImage: CGImage) }
幞ããªããšã«ãUIImageãšNSImageã«ã¯æ¢ã«ãã®ãããªã€ãã·ã£ã©ã€ã¶ãŒããããŸãã®ã§ããããã®ã¯ã©ã¹ã«ç©ºã®æ¡åŒµæ©èœãè¿œå ãããã®ãããã³ã«ãžã®æºæ ãæ£åŒã«èª¬æããã ãã§ãã
extension UIImage: InitializableWithCGImage {} extension NSImage: InitializableWithCGImage {}
UIImageããã®ãããã³ã«ã«çœ®ãæãããšããã®ãããªã¡ãœããã·ã°ããã£ãååŸãããŸãã
func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (T?) -> ())
æåŸã«ããªã¯ãšã¹ãããã£ã³ã»ã«ãããããã«æ³šæããå¿ èŠããããŸãã ãããè¡ãã«ã¯ãæ»ãå€ImageRequestIdãrequestImageã¡ãœããã«è¿œå ããŸããããã«ããããªã¯ãšã¹ããããã«èå¥ã§ããŸãã
func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (T?) -> ()) -> ImageRequestId
ãã1ã€å°ããªå€æŽãæ®ã£ãŠããŸãã
å ã»ã©ãdeliveryModeãããã°ã¬ãã·ãã«èšå®ãããšããã³ãã©ãŒãæ°ååŒã³åºãããšãã§ãããšè¿°ã¹ãŸããã ãã®ãã³ãã©ãŒå ã§ãã€ã¡ãŒãžã®æçµããŒãžã§ã³ãšäžéããŒãžã§ã³ã®ã©ã¡ãã§åŒã³åºãããããç解ããŠãããšäŸ¿å©ã§ãã ãããã£ãŠãImageRequestResultæ§é äœã«æž¡ããŸãããã®æ§é äœã«ã¯ãç»åèªäœã«å ããŠããªã¯ãšã¹ãã®çµæã«é¢ãããã®ä»ã®æçšãªæ å ±ãå«ãŸããŸãã
func requestImage<T: InitializableWithCGImage>( options: ImageRequestOptions, handler: @escaping (ImageRequestResult<T>) -> ()) -> ImageRequestId struct ImageRequestResult<T> { let image: T? let degraded: Bool let requestId: ImageRequestId }
ãããã£ãŠãã€ã³ã¿ãŒãã§ã€ã¹ã«ç»åã衚瀺ããããã«ç»åãèŠæ±ããæ¹æ³ã®æçµããŒãžã§ã³ã«å°éããŸããã
ä»ã®3ã€ã®ã¡ãœããã¯åçŽã§ããã®ãã¡ã®2ã€ã¯æ¬è³ªçã«åãªãéåæã²ãã¿ãŒã§ãã
protocol 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) }
ãããã£ãŠãããã«ãŒã®ã¢ãã«ãšããŠã®äœ¿çšã«æé©ãªImageSourceãããã³ã«ãååŸããŸããããã£ã¹ã¯ããããã¯ãŒã¯ãããã³ãŠãŒã¶ãŒã®ãã©ãã®ã£ã©ãªãŒããã®åçã®3ã€ã®å Žåã«ã®ã¿å®è£ ããŸãã
ãã©ãã®ã£ã©ãªãŒ
iOS 8以éããã©ãã®ã£ã©ãªãŒãžã®ã¢ã¯ã»ã¹ã¯Photos.frameworkãä»ããŠè¡ãããŸãã ã®ã£ã©ãªãŒèªäœã¯çŽæ¥PHPhotoLibraryãªããžã§ã¯ãã§è¡šãããåçã¯PHAssetãªããžã§ã¯ãã§è¡šãããŸãã

ã€ã³ã¿ãŒãã§ã€ã¹ã«è¡šç€ºã§ããåçã®ãã¥ãŒãååŸããã«ã¯ãUIImageåºåãæäŸããPHImageManagerã䜿çšããå¿ èŠããããŸãã
ãã®å€æãå®è¡ããã¡ãœããã¯æ¬¡ã®ããã«ãªããŸãã
func requestImage( for: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, resultHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> ()) -> PHImageRequestID
ã芧ã®ãšãããããã¯ç¬èªã®ImageSourceãããã³ã«ã§ç»åãååŸããæ¹æ³ã«éåžžã«äŒŒãŠããŸãïŒåãã¿ãŒã²ãããµã€ãºãã³ã³ãã³ãã¢ãŒããããã€ãã®ãã©ã¡ãŒã¿ãŒãéåæçµæãã³ãã©ãŒã
ImageSourceã®æåã®å®è£ ã¯PHAssetã®åãªãã©ãããŒã§ãã£ããããããã¯é©ãããšã§ã¯ãããŸããããããã£ãŠããã®ç¹å®ã®çœ²åã«å€§ããäŸåããŠããŸããã
æ®å¿µãªãããPHImageManagerã®æäœã調æ»ããéçšã§ãããã€ãã®æ»ããããç¹ã«ééãããããç¬èªã®requestImageã¡ãœããã®æ¬äœã¯ããã®æšæºã¡ãœããã®åäžã®åŒã³åºãã§æ§æãããŠããªãã£ãããã§ãã
ãããã®æåã®ãã®ã¯ãã³ã¬ã¯ã·ã§ã³ãã¥ãŒã§åçã衚瀺ãããšããå€å žçãªåé¡ã解決ããããšã«çŸããŸããã
- PHImageManagerã¯ããªã¯ãšã¹ãããã£ã³ã»ã«ãããåŸã«resultHandlerãã©ã®ããã«åŒã³åºããããã«ã€ããŠã¯äžåä¿èšŒããŸããã åŒã³åºãããªãå Žåãããã°ãåŒã³åºãããå ŽåããããŸãããåæã«UIImageãååŸããå Žåãããã°ã代ããã«-nilãååŸããå ŽåããããŸãã æ£ç¢ºã«äœãèµ·ãã£ãã®ããææ¡ããå¿
èŠããªãããã«ãã¯ã©ã€ã¢ã³ãã³ãŒããç°¡çŽ åãããã£ãã®ã§ãã
- ãã®ãããImageSourceã®resultHandlerãåŒã³åºãããã®å³å¯ãªã«ãŒã«ã»ãããç»å ŽããŸããããã®1ã€ã¯ããªã¯ãšã¹ãã®ãã£ã³ã»ã«åŸã«resultHandlerãåŒã³åºãã¹ãã§ã¯ãªããšè¿°ã¹ãŠããŸãã
ãã®åé¡ã®è§£æ±ºçã¯éåžžã«ç°¡åã§ããã resultHandler PHImageManagerã«ã¯ãå ¥åãã2ã€ã®ãã©ã¡ãŒã¿ãŒããããŸãã1ã€ç®ã¯UIImageã2ã€ç®ã¯æ å ±ãã£ã¯ã·ã§ããªãŒã§ããã¹ãŠã®æçšãªæ å ±ãå«ãŸããŠããŸãã
// resultHandler PHImageManager' let cancelled = (info?[PHImageCancelledKey] as? NSNumber)?.boolValue ?? false || cancelledRequestIds.contains(requestId) if !cancelled { // "Ì" resultHandler }
ãã®æ å ±ã®äžã«ã¯ããªã¯ãšã¹ãããã£ã³ã»ã«ããããã©ãããå€æã§ãããã©ã°ããããŸãã ãã ãããã®resultHandleråŒã³åºãããã¥ãŒã«å ¥ã£ãåŸã«ãªã¯ãšã¹ãããã£ã³ã»ã«ãããå Žåããã®ãã©ã°ã¯éä¿¡ãããŸããã ãããã£ãŠããã£ã³ã»ã«ãããrequestIdã®é åãImageSourceå ã«ä¿æãããªã¯ãšã¹ãã®ååšã確èªããå¿ èŠããããŸããã
2çªç®ã®åé¡ã¯ãiCloudããã®åçã«ééãããšãã«çŸããèªã¿èŸŒã¿æã«ã¢ã¯ãã£ããã£ã€ã³ãžã±ãŒã¿ã衚瀺ããå¿ èŠããããŸããã
ãã®ãããªè² è·ã远跡ããå¯äžã®æ¹æ³ã¯ãPHImageRequestOptionsãªããžã§ã¯ãã«ããã°ã¬ã¹ãã³ãã©ãŒãèšå®ããããšã§ããããã¯ãç»åãèŠæ±ããããšãã«PHImageManagerã«æž¡ãããŸãã
class PHImageRequestOptions { // PHImageManager var progressHandler: PHAssetImageProgressHandler? // ... }
ããŠã³ããŒãã®éå§ãšçµäºã®äºå®ã远跡ããã ãã§ãããããèŠæ±ãã©ã¡ãŒã¿ãŒã䜿çšããŠããã®ãããª2ã€ã®ã¯ããŒãžã£ãŒãç¬èªã®æ§é ã«è¿œå ããŸããã ãããŠãprogressHandlerã®æåã®åŒã³åºãã§åçŽã«onDownloadStartããã«ããå ŽåãonDownloadFinishã§ã¯ããã»ã©ç°¡åã§ã¯ãããŸããã§ããã
struct ImageRequestOptions { // ImageSource var onDownloadStart: ((ImageRequestId) -> ())? var onDownloadFinish: ((ImageRequestId) -> ())? }
幞éãªããšã«ãprogressHandlerããç»åã100ïŒ ããŒãããããšå ±åãããå Žåãprogress == 1ã®å€ã«å¯Ÿå¿ãããã®å Žæã§onDownloadFinishãåŒã³åºããŸããã
phImageRequestOptions.progressHandler = { progress, _, _, _ in if progress == 1 { callOnDownloadFinish() } }
ãã ããããã¯çºçããªãå¯èœæ§ããããprogressHandlerã®æåŸã®åŒã³åºãã¯100ïŒ æªæºã®é²è¡ã§çºçããŸãã ãã®å Žåãæ¢ã«resultHandlerå ã§ãããŠã³ããŒããå®äºãããã©ãããæšæž¬ããããšããŠããŸãã
// resultHandler: let degraded: Bool = info?[PHImageResultIsDegradedKey] let looksLikeLastCallback = cancelled || (image != nil && !degraded) if looksLikeLastCallback { callOnDownloadFinish() }
ãã®ã³ãŒã«ããã¯ã§æäŸãããæ å ±ãã£ã¯ã·ã§ããªã«ã¯ãç»åã®æçµããŒãžã§ã³ãšäžéããŒãžã§ã³ã®ã©ã¡ããåãåã£ããã瀺ãIsDegradedãã©ã°ããããŸãã ãã®ããããã®æ®µéã§ã¯ããªã¯ãšã¹ãããã£ã³ã»ã«ããå ŽåããŸãã¯ç»åã®æçµããŒãžã§ã³ãæ¥ãå Žåã«ããŠã³ããŒããå®äºãããšæ³å®ããã®ãè«ççã§ãã
Paparazzoãªããžããªã§ããã£ã¹ã¯ããã³ãããã¯ãŒã¯ããã®åçã®ImageSourceã®å®è£ ã調ã¹ãããšãã§ããŸã ã
ã¡ãã£ã¢ããã«ãŒã¯ã å€åœã®ãªãœãŒã¹ãå«ãiOSéçºè ã®æ³šç®ãéããŠããŸãã 圌ãã¯ããããããã«å²ãåœãŠãããæ©èœãå®å šã«æºãããéåžžã«ãšã¬ã¬ã³ãã§ç°¡åã«å®è£ ãããŠããããšã«æ³šç®ããŠããŸãã ããã§ãèªç±ã«è©ŠããŠããã¹ãããè°è«ã§ããŸãã AvitoããŒã ã¯ããã€ã§ãã質åã«ãçãããŸãã
䟿å©ãªãªã³ã¯ïŒ
- ããŒã1ãžã®ãªã³ã¯
- GitHubã®ããã©ãã
- ã¬ããŒãã®èšé²ã¡ãã£ã¢ããã«ãŒ-ç¡éãžããããŠãã®å ãžïŒCocoaHeads Russia 03/01/2017ïŒ