南鑫林 5 anni fa
parent
commit
ddbc813132
32 ha cambiato i file con 3410 aggiunte e 10 eliminazioni
  1. 106 0
      RainbowPlanet/RainbowPlanet.xcodeproj/project.pbxproj
  2. 3 1
      RainbowPlanet/RainbowPlanet/Manager/AliyunManager/AliyunOSSManager/AliyunOSSManager.swift
  3. 4 2
      RainbowPlanet/RainbowPlanet/Modules/CircleModule/Circle/ViewController/CircleViewController.swift
  4. 63 0
      RainbowPlanet/RainbowPlanet/Modules/CircleModule/CirclePublishMessage/View/CirclePublishMessageTitleCollectionViewCell.swift
  5. 124 4
      RainbowPlanet/RainbowPlanet/Modules/CircleModule/CirclePublishMessage/ViewController/CirclePublishMessageViewController.swift
  6. 0 2
      RainbowPlanet/RainbowPlanet/Modules/PublishModule/PublishEdit/View/PublishEditAddImgCollectionCell.swift
  7. 5 0
      RainbowPlanet/RainbowPlanet/Router/BrowsePictureRouterModuleType.swift
  8. 22 0
      RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/Contents.json
  9. BIN
      RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/btn_photograph@2x.png
  10. BIN
      RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/btn_photograph@3x.png
  11. 42 1
      RainbowPlanet/RainbowPlanet/Tools/BrowsePictures/ViewController/BrowsePicturesViewController.swift
  12. 54 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/SynchronizedDictionary.swift
  13. 81 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAlbumPopView.swift
  14. 68 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAssetCollection+Extension.swift
  15. 370 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAssetsCollection.swift
  16. 29 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLBundle.swift
  17. 25 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLCollectionTableViewCell.swift
  18. 60 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLCollectionTableViewCell.xib
  19. 178 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoCollectionViewCell.swift
  20. 144 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoCollectionViewCell.xib
  21. 282 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoLibrary.swift
  22. 71 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomCell_Instagram.swift
  23. 104 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomCell_Instagram.xib
  24. 183 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomTLPhotoPickerViewController.swift
  25. BIN
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/arrow.png
  26. BIN
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/camera@3x.png
  27. BIN
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/insertPhotoMaterial@3x.png
  28. BIN
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/pop_arrow.png
  29. BIN
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/video.png
  30. 17 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotopickerDataSourcesProtocol.swift
  31. 1154 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotosPickerViewController.swift
  32. 221 0
      RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotosPickerViewController.xib

+ 106 - 0
RainbowPlanet/RainbowPlanet.xcodeproj/project.pbxproj

@@ -166,6 +166,24 @@
 		A747D8092359B937007F4E33 /* AliyunOSSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D8082359B937007F4E33 /* AliyunOSSManager.swift */; };
 		A747D80C2359C2BE007F4E33 /* ConfigOSSStsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D80B2359C2BE007F4E33 /* ConfigOSSStsModel.swift */; };
 		A747D80E235C64AB007F4E33 /* Extension+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D80D235C64AB007F4E33 /* Extension+Data.swift */; };
+		A747D811235C6E48007F4E33 /* CirclePublishMessageTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D810235C6E48007F4E33 /* CirclePublishMessageTitleCollectionViewCell.swift */; };
+		A747D81F235C8FA8007F4E33 /* TLAlbumPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D815235C8FA6007F4E33 /* TLAlbumPopView.swift */; };
+		A747D820235C8FA8007F4E33 /* TLAssetCollection+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D816235C8FA6007F4E33 /* TLAssetCollection+Extension.swift */; };
+		A747D821235C8FA8007F4E33 /* TLPhotoLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D817235C8FA6007F4E33 /* TLPhotoLibrary.swift */; };
+		A747D822235C8FA8007F4E33 /* TLPhotosPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D818235C8FA6007F4E33 /* TLPhotosPickerViewController.swift */; };
+		A747D823235C8FA8007F4E33 /* SynchronizedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D819235C8FA7007F4E33 /* SynchronizedDictionary.swift */; };
+		A747D824235C8FA8007F4E33 /* TLBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D81A235C8FA7007F4E33 /* TLBundle.swift */; };
+		A747D825235C8FA8007F4E33 /* TLPhotopickerDataSourcesProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D81B235C8FA7007F4E33 /* TLPhotopickerDataSourcesProtocol.swift */; };
+		A747D826235C8FA8007F4E33 /* TLAssetsCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D81C235C8FA7007F4E33 /* TLAssetsCollection.swift */; };
+		A747D827235C8FA8007F4E33 /* TLPhotoCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D81D235C8FA7007F4E33 /* TLPhotoCollectionViewCell.swift */; };
+		A747D828235C8FA8007F4E33 /* TLCollectionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D81E235C8FA7007F4E33 /* TLCollectionTableViewCell.swift */; };
+		A747D82A235C8FB5007F4E33 /* TLPhotoPickerController.bundle in Resources */ = {isa = PBXBuildFile; fileRef = A747D829235C8FB5007F4E33 /* TLPhotoPickerController.bundle */; };
+		A747D82E235C8FC9007F4E33 /* TLPhotoCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A747D82B235C8FC8007F4E33 /* TLPhotoCollectionViewCell.xib */; };
+		A747D82F235C8FC9007F4E33 /* TLPhotosPickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A747D82C235C8FC8007F4E33 /* TLPhotosPickerViewController.xib */; };
+		A747D830235C8FC9007F4E33 /* TLCollectionTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A747D82D235C8FC9007F4E33 /* TLCollectionTableViewCell.xib */; };
+		A747D833235C908F007F4E33 /* CustomTLPhotoPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D832235C908F007F4E33 /* CustomTLPhotoPickerViewController.swift */; };
+		A747D836235CA628007F4E33 /* CustomCell_Instagram.swift in Sources */ = {isa = PBXBuildFile; fileRef = A747D834235CA628007F4E33 /* CustomCell_Instagram.swift */; };
+		A747D837235CA628007F4E33 /* CustomCell_Instagram.xib in Resources */ = {isa = PBXBuildFile; fileRef = A747D835235CA628007F4E33 /* CustomCell_Instagram.xib */; };
 		A74D9D572327B54100F05C14 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74D9D562327B54100F05C14 /* NotificationService.swift */; };
 		A74D9D5B2327B54100F05C14 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A74D9D542327B54100F05C14 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 		A74DF74922EAAF17007FB505 /* MJDIYFullScreenHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74DF74822EAAF17007FB505 /* MJDIYFullScreenHeader.swift */; };
@@ -877,6 +895,24 @@
 		A747D8082359B937007F4E33 /* AliyunOSSManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AliyunOSSManager.swift; sourceTree = "<group>"; };
 		A747D80B2359C2BE007F4E33 /* ConfigOSSStsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigOSSStsModel.swift; sourceTree = "<group>"; };
 		A747D80D235C64AB007F4E33 /* Extension+Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Data.swift"; sourceTree = "<group>"; };
+		A747D810235C6E48007F4E33 /* CirclePublishMessageTitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CirclePublishMessageTitleCollectionViewCell.swift; sourceTree = "<group>"; };
+		A747D815235C8FA6007F4E33 /* TLAlbumPopView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLAlbumPopView.swift; sourceTree = "<group>"; };
+		A747D816235C8FA6007F4E33 /* TLAssetCollection+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TLAssetCollection+Extension.swift"; sourceTree = "<group>"; };
+		A747D817235C8FA6007F4E33 /* TLPhotoLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLPhotoLibrary.swift; sourceTree = "<group>"; };
+		A747D818235C8FA6007F4E33 /* TLPhotosPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLPhotosPickerViewController.swift; sourceTree = "<group>"; };
+		A747D819235C8FA7007F4E33 /* SynchronizedDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedDictionary.swift; sourceTree = "<group>"; };
+		A747D81A235C8FA7007F4E33 /* TLBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLBundle.swift; sourceTree = "<group>"; };
+		A747D81B235C8FA7007F4E33 /* TLPhotopickerDataSourcesProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLPhotopickerDataSourcesProtocol.swift; sourceTree = "<group>"; };
+		A747D81C235C8FA7007F4E33 /* TLAssetsCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLAssetsCollection.swift; sourceTree = "<group>"; };
+		A747D81D235C8FA7007F4E33 /* TLPhotoCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLPhotoCollectionViewCell.swift; sourceTree = "<group>"; };
+		A747D81E235C8FA7007F4E33 /* TLCollectionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TLCollectionTableViewCell.swift; sourceTree = "<group>"; };
+		A747D829235C8FB5007F4E33 /* TLPhotoPickerController.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = TLPhotoPickerController.bundle; sourceTree = "<group>"; };
+		A747D82B235C8FC8007F4E33 /* TLPhotoCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TLPhotoCollectionViewCell.xib; sourceTree = "<group>"; };
+		A747D82C235C8FC8007F4E33 /* TLPhotosPickerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TLPhotosPickerViewController.xib; sourceTree = "<group>"; };
+		A747D82D235C8FC9007F4E33 /* TLCollectionTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TLCollectionTableViewCell.xib; sourceTree = "<group>"; };
+		A747D832235C908F007F4E33 /* CustomTLPhotoPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTLPhotoPickerViewController.swift; sourceTree = "<group>"; };
+		A747D834235CA628007F4E33 /* CustomCell_Instagram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomCell_Instagram.swift; sourceTree = "<group>"; };
+		A747D835235CA628007F4E33 /* CustomCell_Instagram.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomCell_Instagram.xib; sourceTree = "<group>"; };
 		A74D9D542327B54100F05C14 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		A74D9D562327B54100F05C14 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
 		A74D9D582327B54100F05C14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -2447,6 +2483,7 @@
 		A747D8032359925F007F4E33 /* CirclePublishMessage */ = {
 			isa = PBXGroup;
 			children = (
+				A747D80F235C6E22007F4E33 /* View */,
 				A747D80423599283007F4E33 /* ViewController */,
 			);
 			path = CirclePublishMessage;
@@ -2477,6 +2514,56 @@
 			path = AliyunOSSManager;
 			sourceTree = "<group>";
 		};
+		A747D80F235C6E22007F4E33 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				A747D810235C6E48007F4E33 /* CirclePublishMessageTitleCollectionViewCell.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
+		A747D813235C8F7C007F4E33 /* TLPhotoPicker */ = {
+			isa = PBXGroup;
+			children = (
+				A747D82D235C8FC9007F4E33 /* TLCollectionTableViewCell.xib */,
+				A747D82B235C8FC8007F4E33 /* TLPhotoCollectionViewCell.xib */,
+				A747D82C235C8FC8007F4E33 /* TLPhotosPickerViewController.xib */,
+				A747D819235C8FA7007F4E33 /* SynchronizedDictionary.swift */,
+				A747D815235C8FA6007F4E33 /* TLAlbumPopView.swift */,
+				A747D816235C8FA6007F4E33 /* TLAssetCollection+Extension.swift */,
+				A747D81C235C8FA7007F4E33 /* TLAssetsCollection.swift */,
+				A747D81A235C8FA7007F4E33 /* TLBundle.swift */,
+				A747D81E235C8FA7007F4E33 /* TLCollectionTableViewCell.swift */,
+				A747D81D235C8FA7007F4E33 /* TLPhotoCollectionViewCell.swift */,
+				A747D817235C8FA6007F4E33 /* TLPhotoLibrary.swift */,
+				A747D81B235C8FA7007F4E33 /* TLPhotopickerDataSourcesProtocol.swift */,
+				A747D818235C8FA6007F4E33 /* TLPhotosPickerViewController.swift */,
+			);
+			name = TLPhotoPicker;
+			path = ..;
+			sourceTree = "<group>";
+		};
+		A747D814235C8F96007F4E33 /* TLPhotoPicker */ = {
+			isa = PBXGroup;
+			children = (
+				A747D831235C8FF6007F4E33 /* Custom */,
+				A747D829235C8FB5007F4E33 /* TLPhotoPickerController.bundle */,
+				A747D813235C8F7C007F4E33 /* TLPhotoPicker */,
+			);
+			name = TLPhotoPicker;
+			path = TLPhotoPicker/TLPhotoPicker;
+			sourceTree = "<group>";
+		};
+		A747D831235C8FF6007F4E33 /* Custom */ = {
+			isa = PBXGroup;
+			children = (
+				A747D832235C908F007F4E33 /* CustomTLPhotoPickerViewController.swift */,
+				A747D834235CA628007F4E33 /* CustomCell_Instagram.swift */,
+				A747D835235CA628007F4E33 /* CustomCell_Instagram.xib */,
+			);
+			path = Custom;
+			sourceTree = "<group>";
+		};
 		A74D9D552327B54100F05C14 /* NotificationService */ = {
 			isa = PBXGroup;
 			children = (
@@ -3127,6 +3214,7 @@
 		A77F2CBC2232022A001BD3F6 /* Tools */ = {
 			isa = PBXGroup;
 			children = (
+				A747D814235C8F96007F4E33 /* TLPhotoPicker */,
 				A784F2AF2350123200E49140 /* CopyLabel */,
 				A784F29B234C36D300E49140 /* PhotoAlbumUtil */,
 				A768939F233A0BB000819EC3 /* WSDrawCircleProgress */,
@@ -5777,24 +5865,29 @@
 				A7D77DE922DDBEF70048D5F6 /* RedemptionAreaModule.xcassets in Resources */,
 				A75B787A22E07A51007B986A /* messageModuleTabbar.json in Resources */,
 				A78A5F7622FAA03800DD1764 /* share_iSpt.gif in Resources */,
+				A747D82E235C8FC9007F4E33 /* TLPhotoCollectionViewCell.xib in Resources */,
 				BDD5483422C31752005BBE19 /* AlivcShortVideoImage.bundle in Resources */,
 				A7CC74DE22703B4A003C4F38 /* MineModule.xcassets in Resources */,
+				A747D837235CA628007F4E33 /* CustomCell_Instagram.xib in Resources */,
 				A7EF3E102303DAEE001E4D26 /* LaunchScreen.storyboard in Resources */,
 				BDD54A2522C31D5F005BBE19 /* music.json in Resources */,
 				A76068E922E4922F008DF18F /* Skittles.json in Resources */,
 				BDD54A2722C31D60005BBE19 /* tail.png in Resources */,
 				A76068E422E47E0E008DF18F /* RegisterLoginModule.xcassets in Resources */,
 				BD61229B22C3605C00D3F513 /* AliyunEffectFilterCell.xib in Resources */,
+				A747D82A235C8FB5007F4E33 /* TLPhotoPickerController.bundle in Resources */,
 				A7190167227543DB00104A50 /* baidu_cityid_rel.json in Resources */,
 				A75B787D22E07A51007B986A /* MineModuleTabbar.json in Resources */,
 				A77F2C682231FB4A001BD3F6 /* Assets.xcassets in Resources */,
 				BDD54A2822C31D60005BBE19 /* LocalFilter.json in Resources */,
 				A75B787B22E07A51007B986A /* communityModuleTabbar.json in Resources */,
+				A747D830235C8FC9007F4E33 /* TLCollectionTableViewCell.xib in Resources */,
 				A72C01232275404A0065E0C3 /* province_city_area.json in Resources */,
 				BDD54A2622C31D60005BBE19 /* watermark.png in Resources */,
 				BD108C9B22A60C3300837DAB /* HGImage.storyboard in Resources */,
 				A7464B752338B2C6003CE3A0 /* CommunityModule.xcassets in Resources */,
 				A7FF1577228C824300A85748 /* OrderModule.xcassets in Resources */,
+				A747D82F235C8FC9007F4E33 /* TLPhotosPickerViewController.xib in Resources */,
 				A72E684F22F2BBB30063D967 /* login_video.mp4 in Resources */,
 				BD4B50AC22BC7DEA0073B516 /* FilterResource.bundle in Resources */,
 				A76068EB22E49BC1008DF18F /* collect.json in Resources */,
@@ -5901,6 +5994,7 @@
 				BDF45F0D228C00B8004E2682 /* DefaultContactInfoModel.swift in Sources */,
 				A7811CA5231FB13700C2D8DE /* KSTriangleIndicatorButton.m in Sources */,
 				BD13B6DA22BA03BC008BB323 /* PublishAddAddressController.swift in Sources */,
+				A747D81F235C8FA8007F4E33 /* TLAlbumPopView.swift in Sources */,
 				A72A72BA22321DE000B21995 /* Extension+String.swift in Sources */,
 				BDAF83B222B3B67D0004BCC3 /* RecommendCommentFooter.swift in Sources */,
 				A7EE6E102305487700628D39 /* ThumbnailsManager.swift in Sources */,
@@ -5991,6 +6085,7 @@
 				BD10FC0822C748DA0096A34E /* AlivcAlertView.m in Sources */,
 				BD12B6AC22B502C200AEB10B /* KSVideoPlayerBaseView.m in Sources */,
 				BD2FCBE422B244250006D974 /* RecommendDetailContentCell.swift in Sources */,
+				A747D823235C8FA8007F4E33 /* SynchronizedDictionary.swift in Sources */,
 				A73D7C682268A032002A4CE3 /* SwiftyStarRatingView.swift in Sources */,
 				BD13B6B922BA0327008BB323 /* PublishEditController.swift in Sources */,
 				A71AA5102272156A008FF1A5 /* ExpressAddressListViewController.swift in Sources */,
@@ -6016,6 +6111,7 @@
 				A7811CA6231FB13700C2D8DE /* KSIndicatorLabelControl.m in Sources */,
 				A7C3DD1C226422D200FA262E /* SwiftMoyaNetWorkServiceSMS.swift in Sources */,
 				A72623D322C1FFD100AEF875 /* CommunityPostMyModel.swift in Sources */,
+				A747D820235C8FA8007F4E33 /* TLAssetCollection+Extension.swift in Sources */,
 				A770E5F522D5BC2000CBD0A4 /* ShareCommonStr.swift in Sources */,
 				A72A7386223396CB00B21995 /* SwiftMoyaNetWorkManager.swift in Sources */,
 				BD10121122DEBAF0008DF528 /* CommunityRecommendMusicListModel.swift in Sources */,
@@ -6026,6 +6122,7 @@
 				BDF7C5DB22D72405007F3B79 /* CommunityVideoSubCommentController.swift in Sources */,
 				A7F2D6C122B0D1CB0093000B /* CommunityFollowTableHeaderView.swift in Sources */,
 				A7C0FE0022B66E5A00BC1E86 /* CommunityFeaturedTopicsViewController.swift in Sources */,
+				A747D825235C8FA8007F4E33 /* TLPhotopickerDataSourcesProtocol.swift in Sources */,
 				BDD9377522DEF371002D11B3 /* PublishMusicListCell.swift in Sources */,
 				A76893A2233A0BB000819EC3 /* DrawCircleProgressButton.m in Sources */,
 				A7A98E12227ECA11005306E9 /* ProductSearchModel.swift in Sources */,
@@ -6092,6 +6189,7 @@
 				A72A72B822321DE000B21995 /* Extension+UserDefaults.swift in Sources */,
 				A719016B22757A5A00104A50 /* ProvinceCityAreaTableViewCell.swift in Sources */,
 				BD12B6AD22B502C200AEB10B /* KSVideoLayer.m in Sources */,
+				A747D811235C6E48007F4E33 /* CirclePublishMessageTitleCollectionViewCell.swift in Sources */,
 				BD27F0B222CC9E2A00A6514D /* CommunityVideoPlayView.swift in Sources */,
 				BD6122B122C3638300D3F513 /* AliyunEffectFilterInfo.m in Sources */,
 				A729B5B72267270B004AE098 /* PasswordLoginView.swift in Sources */,
@@ -6232,6 +6330,7 @@
 				A71738AD228AB7B2000AEA6A /* ProductCommentListModel.swift in Sources */,
 				A7C0FDF822B6671C00BC1E86 /* PopularVideoCollectionViewCell.swift in Sources */,
 				BD61229522C3605C00D3F513 /* AliyunEffectModelTransManager.m in Sources */,
+				A747D824235C8FA8007F4E33 /* TLBundle.swift in Sources */,
 				A7F304C32333683600A4850F /* PushNotificationSettingsOneSectionHeaderView.swift in Sources */,
 				A7E19FA822BA2CDF009BCCE1 /* SearchProductListViewController.swift in Sources */,
 				BD4B50AE22BC815F0073B516 /* PublishFilterCollectionCell.swift in Sources */,
@@ -6267,6 +6366,7 @@
 				BD13B6DD22BA03BC008BB323 /* PublishAddTopicController.swift in Sources */,
 				A770E60722D64CD700CBD0A4 /* UIView+SwCapture.swift in Sources */,
 				BD6122AE22C3638300D3F513 /* AliyunEffectMvInfo.m in Sources */,
+				A747D827235C8FA8007F4E33 /* TLPhotoCollectionViewCell.swift in Sources */,
 				A71901752275F71F00104A50 /* BaiduToCityModel.swift in Sources */,
 				A77FAEEF23583949002A1D08 /* CircleCommentListViewController.swift in Sources */,
 				BD12203F22AF8E190051C7C2 /* MessagePlanetNotiController.swift in Sources */,
@@ -6325,6 +6425,7 @@
 				A7D5F25022BC7B3700F8E9AF /* WebViewJavascriptBridgeManager.swift in Sources */,
 				A7F2D6D922B250EF0093000B /* CardContentPicVideoTableViewCell.swift in Sources */,
 				A77BB45F2329EE3500DCAE32 /* NXLPermissionLocation.swift in Sources */,
+				A747D836235CA628007F4E33 /* CustomCell_Instagram.swift in Sources */,
 				BDBC0AF622DDEA4E00CA788E /* PublishRecommendMusicController.swift in Sources */,
 				A784F2AB234ED15600E49140 /* DatePickerManager.swift in Sources */,
 				A7C2568022CD9D8E00420828 /* RecommendSimilarHeaderView.swift in Sources */,
@@ -6363,6 +6464,7 @@
 				A77BB4672329EF7300DCAE32 /* NXLPermissionContacts.swift in Sources */,
 				A7C2567622CCB38D00420828 /* HeightModel.swift in Sources */,
 				A7D5F26122C0C2F300F8E9AF /* UserFollowsModel.swift in Sources */,
+				A747D828235C8FA8007F4E33 /* TLCollectionTableViewCell.swift in Sources */,
 				A72A72D022321E2700B21995 /* FontMacro.swift in Sources */,
 				A74322A022B8D1F30017C367 /* MyFollowAndFanViewController.swift in Sources */,
 				BD2FCBE622B2586C0006D974 /* CommunityTagCollectionCell.swift in Sources */,
@@ -6385,6 +6487,7 @@
 				A74DF74922EAAF17007FB505 /* MJDIYFullScreenHeader.swift in Sources */,
 				BD108C9722A60C2100837DAB /* HGImagePickerController.swift in Sources */,
 				BD2E5D4922D87ED900534603 /* MXSlider.m in Sources */,
+				A747D826235C8FA8007F4E33 /* TLAssetsCollection.swift in Sources */,
 				BDA45BFF22E45EBC009DE548 /* PublishUploadProgressView.swift in Sources */,
 				A7BA1902230EA80000E3B969 /* TGCaculateNumberTool.m in Sources */,
 				A7811CB523220C8C00C2D8DE /* AliyunBeautyLevelCollectionViewCell.m in Sources */,
@@ -6447,6 +6550,7 @@
 				A7284440224DFACD00F82F30 /* InfoModel.swift in Sources */,
 				A7F6890923571B9D000C313F /* CommunityCircleMessagesModel.swift in Sources */,
 				A72A72B522321DE000B21995 /* Extension+UITextView.swift in Sources */,
+				A747D822235C8FA8007F4E33 /* TLPhotosPickerViewController.swift in Sources */,
 				A7DAB33922DC2B83005B964E /* ShareRouterModuleType.swift in Sources */,
 				BDD9377722DF3B47002D11B3 /* PublishMusicListController.swift in Sources */,
 				BD0FAA6522C4C35E00DDFB37 /* AliyunCycleProgressView.m in Sources */,
@@ -6460,6 +6564,7 @@
 				A77BB4542329EBAA00DCAE32 /* NXLPermission.swift in Sources */,
 				BD13B6E422BA0546008BB323 /* PublishAddressPOICell.swift in Sources */,
 				BDE376E922C22D4E0055E2EA /* AlivcUIConfig.m in Sources */,
+				A747D833235C908F007F4E33 /* CustomTLPhotoPickerViewController.swift in Sources */,
 				A7C2567C22CD8EEE00420828 /* CommunityPostCommentsModel.swift in Sources */,
 				A7541502224C5ECB002480B5 /* BaiduMapManager.swift in Sources */,
 				A773D02F229FDC3B007A5751 /* DIYEmptyView.swift in Sources */,
@@ -6480,6 +6585,7 @@
 				BD50F47022E157F50077D4BF /* PublishRecordMusicView.swift in Sources */,
 				A72A72B922321DE000B21995 /* Extension+Array.swift in Sources */,
 				A74DF74D22EAEF60007FB505 /* MJRefreshManager.swift in Sources */,
+				A747D821235C8FA8007F4E33 /* TLPhotoLibrary.swift in Sources */,
 				A71901692275464000104A50 /* ProvinceCityAreaView.swift in Sources */,
 				BD12204422AF996E0051C7C2 /* MessageListController.swift in Sources */,
 				A73A56D922DC8378004920FE /* UpdateVersionView.swift in Sources */,

+ 3 - 1
RainbowPlanet/RainbowPlanet/Manager/AliyunManager/AliyunOSSManager/AliyunOSSManager.swift

@@ -198,10 +198,12 @@ class AliyunOSSManager: NSObject {
                 put.objectKey = PathManager.randomString() ?? ""
                 
                 // 图片url
+//                NSString *encodingString = [[NSString stringWithFormat:@"http://kachamao.oss-cn-shanghai.aliyuncs.com/%@",objectName]stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
+//                [callBackNames addObject: encodingString];
                 imageUrls.append("图片url")
                 
                 // 1.3.上传对象(图片data)
-                put.uploadingData = Data.imageCompressForSize(sourceImage: image, targetPx: 750.0) ?? image.pngData()!
+                put.uploadingData = Data.imageCompressForSize(sourceImage: image, targetPx: 1280.0) ?? image.pngData()!
                 
                 // 1.4.上传进度
 //                put.uploadProgress = { (bytesSent , totalByteSent , totalBytesExpectedToSend) in

+ 4 - 2
RainbowPlanet/RainbowPlanet/Modules/CircleModule/Circle/ViewController/CircleViewController.swift

@@ -73,8 +73,10 @@ class CircleViewController: BaseViewController {
             vc.circleNameStr = self?.communityCircleModel?.name ?? ""
             self?.navigationController?.pushViewController(vc, animated: true)
         }
-        plusButton.rx.tap.subscribe(onNext: { (_) in
-            
+        plusButton.rx.tap.subscribe(onNext: {[weak self] (_) in
+            let vc = CirclePublishMessageViewController()
+            vc.circleId = self?.communityCircleModel?.id
+            self?.navigationController?.pushViewController(vc, animated: true)
         }).disposed(by: disposeBag)
         loadCircleData()
     }

+ 63 - 0
RainbowPlanet/RainbowPlanet/Modules/CircleModule/CirclePublishMessage/View/CirclePublishMessageTitleCollectionViewCell.swift

@@ -0,0 +1,63 @@
+//
+//  CirclePublishMessageTitleCollectionViewCell.swift
+//  RainbowPlanet
+//
+//  Created by 南鑫林 on 2019/10/20.
+//  Copyright © 2019 RainbowPlanet. All rights reserved.
+//
+
+import UIKit
+import RxSwift
+import IQKeyboardManagerSwift
+
+class CirclePublishMessageTitleCollectionViewCell: UICollectionViewCell {
+    let disposeBag = DisposeBag()
+    
+    class func cellWith(collectionView:UICollectionView,indexPath:IndexPath) -> CirclePublishMessageTitleCollectionViewCell {
+        let ID = "CirclePublishMessageTitleCollectionViewCell"
+        collectionView.register(CirclePublishMessageTitleCollectionViewCell.self, forCellWithReuseIdentifier: ID)
+        let cell : CirclePublishMessageTitleCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: ID, for: indexPath) as! CirclePublishMessageTitleCollectionViewCell
+        cell.indexPath = indexPath
+        return cell
+    }
+    //MARK: - indexPath
+    var indexPath: IndexPath?{
+        didSet {
+            
+        }
+    }
+    //MARK: - 初始化
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupViews()
+        setupLayouts()
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    //MRAK: - 设置View
+    private func setupViews() {
+        addSubview(titleTextView)
+    }
+    
+    private func setupLayouts() {
+        titleTextView.snp.makeConstraints { (make) in
+            make.top.equalToSuperview()
+            make.left.equalTo(14)
+            make.right.equalTo(-14)
+            make.bottom.equalTo(-10)
+        }
+    }
+    
+    lazy var titleTextView: IQTextView = {
+        let titleTextView = IQTextView()
+        titleTextView.placeholder = "请输入您的问题"
+        titleTextView.font = kRegularFont14
+        titleTextView.textColor = k333333Color
+        titleTextView.placeholderTextColor = kDDDDDDColor
+        titleTextView.tintColor = kThemeColor
+        return titleTextView
+    }()
+}

+ 124 - 4
RainbowPlanet/RainbowPlanet/Modules/CircleModule/CirclePublishMessage/ViewController/CirclePublishMessageViewController.swift

@@ -21,19 +21,25 @@ class CirclePublishMessageViewController: BaseViewController {
     }
     
     var circleId : Int?
-    var content : String?
-    var imgs : String?
+    var content : String = ""
+    var imgs : String = ""
+    var maxImageCount: Int = 9
+    var images = Array<UIImage>()
 
     
     override func viewDidLoad() {
         super.viewDidLoad()
 
-        // Do any additional setup after loading the view.
+        setupViews()
+        setupLayouts()
+        setupData()
     }
     
     override func setupViews() {
         navigationBar.title = "留言"
         navigationBar.addSubview(publishButton)
+        navigationBar.wr_setBottomLineHidden(hidden: true)
+        view.addSubview(collectionView)
     }
     
     override func setupLayouts() {
@@ -57,16 +63,130 @@ class CirclePublishMessageViewController: BaseViewController {
         publishButton.setTitleColor(kffffffColor, for: UIControl.State.normal)
         publishButton.setBackgroundImage(UIImage.imageWithColor(color: kThemeColor), for: UIControl.State.normal)
         publishButton.setBackgroundImage(UIImage.imageWithColor(color: kd8d8d8Color), for: UIControl.State.disabled)
+        publishButton.titleLabel?.font = kMediumFont13
         publishButton.cornerRadius = 13
         publishButton.masksToBounds = true
         return publishButton
     }()
     
+    private lazy var collectionView: UICollectionView = {
+        let collectionView = UICollectionView.init(frame: CGRect(x: 0, y: kNavBarTotalHeight, width: kScreenWidth, height: kScreenHeight-kNavBarTotalHeight), collectionViewLayout: collectionViewLayout)
+        collectionView.backgroundColor = kffffffColor
+        collectionView.delegate = self;
+        collectionView.dataSource = self;
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0)
+        let view = UIView(frame: CGRect(x: 0, y: -10, width: kScreenWidth, height: 10))
+        view.backgroundColor = kf7f8faColor
+        collectionView.addSubview(view)
+        return collectionView
+    }()
+    
+    private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
+        let collectionViewLayout = UICollectionViewFlowLayout.init()
+        return collectionViewLayout
+    }()
+    
     
     /// 发布
     func communityCircleMessagePostApi() {
-        SwiftMoyaNetWorkServiceCommunity.shared().communityCircleMessagePostApi(circleId: circleId ?? 0, content: content ?? "",imgs:imgs ?? "") { [weak self] _ in
+        SwiftMoyaNetWorkServiceCommunity.shared().communityCircleMessagePostApi(circleId: circleId ?? 0, content: content ,imgs: imgs) { [weak self] _ in
             self?.navigationController?.popToRootViewController(animated: true)
         }
     }
 }
+
+extension CirclePublishMessageViewController: UICollectionViewDelegateFlowLayout,UICollectionViewDataSource,UICollectionViewDelegate {
+    func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return 2
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        switch section {
+        case 0:
+            return 1
+        default:
+            if images.count < maxImageCount {
+                return images.count + 1
+            } else {
+                return maxImageCount
+            }
+        }
+        
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        switch indexPath.section {
+        case 0:
+            let cell = CirclePublishMessageTitleCollectionViewCell.cellWith(collectionView: collectionView, indexPath: indexPath)
+            return cell
+        default:
+            if images.count < maxImageCount && indexPath.row == images.count {
+                // 添加图片
+                let cell = PublishEditDefaultCollectionCell.cellWith(collectionView: collectionView, indexPath: indexPath)
+                cell.noteStr = "添加图片"
+                return cell
+            } else {
+                // 展示图片
+                let cell = PublishEditAddImgCollectionCell.cellWith(collectionView: collectionView, indexPath: indexPath)
+                cell.mediaType = .image
+                if indexPath.row < images.count {
+                    cell.showImage = images[indexPath.row]
+                }
+                cell.delPicBlock = {
+                    [weak self] (index) in
+                    self?.images.remove(at: index!)
+                    self?.collectionView.reloadSections([1])
+                }
+                return cell
+            }
+        }
+
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
+        switch indexPath.section {
+        case 0:
+            return CGSize(width:kScreenWidth, height: 200)
+        default:
+            return CGSize(width:(kScreenWidth-28-20)/3.0, height:(kScreenWidth-28-20)/3.0)
+        }
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
+        return UIEdgeInsets(top: 0, left: 14, bottom: 0, right: 14)
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
+        switch section {
+        case 0:
+             return 0
+        default:
+             return 0
+        }
+       
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
+        switch section {
+        case 0:
+            return 0
+        default:
+            return 10
+        }
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        if images.count < maxImageCount && indexPath.row == images.count {
+            let vc = CustomTLPhotoPickerViewController()
+            vc.mediaType = .image
+            vc.numberOfColumn = 4
+            vc.maxSelectedAssets = 3
+            self.navigationController?.pushViewController(vc, animated: true)
+        }
+    }
+    
+}
+

+ 0 - 2
RainbowPlanet/RainbowPlanet/Modules/PublishModule/PublishEdit/View/PublishEditAddImgCollectionCell.swift

@@ -35,8 +35,6 @@ class PublishEditAddImgCollectionCell: UICollectionViewCell {
     var productSearchModel: ProductSearchModel? {
         didSet {
             picImageView.kf.setImage(with: kURLThumbnailsImage(name: productSearchModel?.img ?? "default_pic", size: self.size), placeholder: kImage(name: "default_pic"))
-            
-
         }
     }
     

+ 5 - 0
RainbowPlanet/RainbowPlanet/Router/BrowsePictureRouterModuleType.swift

@@ -13,9 +13,11 @@ import SwiftyMediator
 ///
 /// - pushBrowsePictureImageStrs: url
 /// - pushBrowsePictureImages: image
+/// - pushBrowsePicturePHAssets: phAsset
 public enum BrowsePictureRouterModuleType: MediatorTargetType {
     case pushBrowsePictureImageStrs(imageStrs: Array<String>, index: Int)
     case pushBrowsePictureImages(images: Array<UIImage>, index: Int)
+    case pushBrowsePicturePHAssets(phAssets: Array<PHAsset>, index: Int)
 
 }
 
@@ -28,6 +30,9 @@ extension BrowsePictureRouterModuleType: MediatorSourceType {
         case .pushBrowsePictureImages(let images,let index):
             let vc = BrowsePicturesViewController(images: images, index: index)
             return vc
+        case .pushBrowsePicturePHAssets(let phAssets,let index):
+            let vc = BrowsePicturesViewController(phAssets: phAssets, index: index)
+            return vc
         }
     }
 }

+ 22 - 0
RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "btn_photograph@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "btn_photograph@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/btn_photograph@2x.png


BIN
RainbowPlanet/RainbowPlanet/Supporting Files/CommunityModule.xcassets/btn_photograph.imageset/btn_photograph@3x.png


+ 42 - 1
RainbowPlanet/RainbowPlanet/Tools/BrowsePictures/ViewController/BrowsePicturesViewController.swift

@@ -21,6 +21,9 @@ class BrowsePicturesViewController: BaseViewController {
     //存储图片数组
     var images:[UIImage]
     
+    //本地图片数组Assets
+    var phAssets: [PHAsset]
+    
     //默认显示的图片索引
     var index:Int
     
@@ -31,9 +34,10 @@ class BrowsePicturesViewController: BaseViewController {
     var collectionViewLayout: UICollectionViewFlowLayout!
     
     //初始化
-    init(imageStrs:Array<String> = [],images:Array<UIImage> = [], index:Int = 0){
+    init(imageStrs:Array<String> = [],images:Array<UIImage> = [],phAssets:Array<PHAsset> = [], index:Int = 0){
         self.imageStrs = imageStrs
         self.images = images
+        self.phAssets = phAssets
         self.index = index
         
         super.init(nibName: nil, bundle: nil)
@@ -154,6 +158,31 @@ class BrowsePicturesViewController: BaseViewController {
                 pageControl.isHidden = true
             }
         }
+        
+        if !(phAssets.isEmpty ){
+            if phAssets.count == 1 {
+                pageControl.isHidden = true
+            }else {
+                pageControl.isHidden = true
+            }
+            pageControl.numberOfPages = phAssets.count
+            
+            
+            let pageControlWidth = CGFloat((phAssets.count - 1) * 10) + 20
+            
+            pageControl.snp.remakeConstraints { (make) in
+                make.bottom.equalTo(-30)
+                make.height.equalTo(5)
+                make.centerX.equalToSuperview()
+                make.width.equalTo(pageControlWidth)
+            }
+            pageControl.numberOfPages = phAssets.count
+            if  pageControl.numberOfPages > 1 {
+                pageControl.isHidden = false
+            }else {
+                pageControl.isHidden = true
+            }
+        }
         pageControl.isUserInteractionEnabled = false
         pageControl.currentPage = index
         view.addSubview(self.pageControl)
@@ -177,6 +206,11 @@ class BrowsePicturesViewController: BaseViewController {
             if !(self?.images.isEmpty ?? true) {
                 BrowsePicturesSaveImageView.browsePicturesSaveImageView(image: self?.images[self?.pageControl.currentPage ?? 0])
             }
+            
+            if !(self?.phAssets.isEmpty ?? true) {
+                let image = TLPhotoLibrary.fullResolutionImageData(asset: (self?.phAssets[self?.pageControl.currentPage ?? 0])!)
+                BrowsePicturesSaveImageView.browsePicturesSaveImageView(image: image!)
+            }
         }).disposed(by: disposeBag)
     }
     
@@ -220,6 +254,10 @@ UICollectionViewDelegateFlowLayout{
             if !(images.isEmpty){
                 cell.iconImageView.image = images[indexPath.row]
             }
+            if !(phAssets.isEmpty){
+                let image = TLPhotoLibrary.fullResolutionImageData(asset: phAssets[indexPath.row])
+                cell.iconImageView.image = image
+            }
             return cell
     }
     
@@ -232,6 +270,9 @@ UICollectionViewDelegateFlowLayout{
         if !(images.isEmpty){
             return self.images.count
         }
+        if !(phAssets.isEmpty){
+            return self.phAssets.count
+        }
         return 0
     }
     

+ 54 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/SynchronizedDictionary.swift

@@ -0,0 +1,54 @@
+//
+//  SynchronizedDictionary.swift
+//  TLPhotoPicker
+//
+//  Created by wade.hawk on 30/03/2019.
+//
+
+import Foundation
+
+public class SynchronizedDictionary<K:Hashable,V> {
+    private var dictionary: [K:V] = [:]
+    private let accessQueue = DispatchQueue(label: "SynchronizedDictionaryAccess",
+                                            attributes: .concurrent)
+    
+    deinit {
+        //print("deinit SynchronizedDictionary")
+    }
+    
+    public func removeAll() {
+        self.accessQueue.async(flags:.barrier) {
+            self.dictionary.removeAll()
+        }
+    }
+    
+    public func removeValue(forKey: K) {
+        self.accessQueue.async(flags:.barrier) {
+            self.dictionary.removeValue(forKey: forKey)
+        }
+    }
+    
+    public func forEach(_ closure: ((K,V) -> Void)) {
+        self.accessQueue.sync {
+            self.dictionary.forEach{ arg in
+                let (key, value) = arg
+                closure(key,value)
+            }
+        }
+    }
+    
+    public subscript(key: K) -> V? {
+        set {
+            self.accessQueue.async(flags:.barrier) {
+                self.dictionary[key] = newValue
+            }
+        }
+        get {
+            var element: V?
+            self.accessQueue.sync {
+                element = self.dictionary[key]
+            }
+            return element
+        }
+    }
+}

+ 81 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAlbumPopView.swift

@@ -0,0 +1,81 @@
+//
+//  TLAlbumPopView.swift
+//  TLPhotosPicker
+//
+//  Created by wade.hawk on 2017. 4. 19..
+//  Copyright © 2017년 wade.hawk. All rights reserved.
+//
+
+import UIKit
+
+protocol PopupViewProtocol: class {
+    var bgView: UIView! { get set }
+    var popupView: UIView! { get set }
+    var originalFrame: CGRect { get set }
+    var show: Bool { get set }
+    func setupPopupFrame()
+}
+
+extension PopupViewProtocol where Self: UIView {
+    fileprivate func getFrame(scale: CGFloat) -> CGRect {
+        var frame = self.originalFrame
+        frame.size.width = frame.size.width * scale
+        frame.size.height = frame.size.height * scale
+        frame.origin.x = self.frame.width/2 - frame.width/2
+        return frame
+    }
+    func setupPopupFrame() {
+        if self.originalFrame != self.popupView.frame {
+            self.originalFrame = self.popupView.frame
+        }
+    }
+    func show(_ show: Bool, duration: TimeInterval = 0.1) {
+        guard self.show != show else { return }
+        self.layer.removeAllAnimations()
+        self.isHidden = false
+        self.popupView.frame = show ? getFrame(scale: 0.1) : self.popupView.frame
+        self.bgView.alpha = show ? 0 : 1
+        UIView.animate(withDuration: duration, animations: {
+            self.bgView.alpha = show ? 1 : 0
+            self.popupView.transform = show ? CGAffineTransform(scaleX: 1.05, y: 1.05) : CGAffineTransform(scaleX: 0.1, y: 0.1)
+            self.popupView.frame = show ? self.getFrame(scale: 1.05) : self.getFrame(scale: 0.1)
+        }) { _ in
+            self.isHidden = show ? false : true
+            UIView.animate(withDuration: duration) {
+                if show {
+                    self.popupView.transform = CGAffineTransform(scaleX: 1, y: 1)
+                    self.popupView.frame = self.originalFrame
+                }
+                self.show = show
+            }
+        }
+    }
+}
+
+open class TLAlbumPopView: UIView, PopupViewProtocol {
+    @IBOutlet open var bgView: UIView!
+    @IBOutlet open var popupView: UIView!
+    @IBOutlet var popupViewHeight: NSLayoutConstraint!
+    @IBOutlet open var tableView: UITableView!
+    @objc var originalFrame = CGRect.zero
+    @objc var show = false
+    
+    deinit {
+//        print("deinit TLAlbumPopView")
+    }
+    
+    override open func awakeFromNib() {
+        super.awakeFromNib()
+        self.popupView.layer.cornerRadius = 5.0
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapBgView))
+        self.bgView.addGestureRecognizer(tapGesture)
+        self.tableView.register(UINib(nibName: "TLCollectionTableViewCell", bundle: TLBundle.bundle()), forCellReuseIdentifier: "TLCollectionTableViewCell")
+        if #available(iOS 13.0, *) {
+//            self.popupView.backgroundColor = .systemBackground
+        }
+    }
+    
+    @objc func tapBgView() {
+        self.show(false)
+    }
+}

+ 68 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAssetCollection+Extension.swift

@@ -0,0 +1,68 @@
+//
+//  TLAssetCollection+Extension.swift
+//  TLPhotoPicker
+//
+//  Created by wade.hawk on 21/01/2019.
+//
+
+import Foundation
+import Photos
+
+public enum PHFetchedResultGroupedBy {
+    case year
+    case month
+    case week
+    case day
+    case hour
+    case custom(dateFormat: String)
+    var dateFormat: String {
+        switch self {
+        case .year:
+            return "yyyy"
+        case .month:
+            return "yyyyMM"
+        case .week:
+            return "yyyyMMW"
+        case .day:
+            return "yyyyMMdd"
+        case .hour:
+            return "yyyyMMddHH"
+        case let .custom(dateFormat):
+            return dateFormat
+        }
+    }
+}
+
+extension TLAssetsCollection {
+    func enumarateFetchResult(groupedBy: PHFetchedResultGroupedBy) -> Dictionary<String,[TLPHAsset]> {
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateFormat = groupedBy.dateFormat
+        var assets = [PHAsset]()
+        assets.reserveCapacity(self.fetchResult?.count ?? 0)
+        self.fetchResult?.enumerateObjects({ (phAsset, idx, stop) in
+            if phAsset.creationDate != nil {
+                assets.append(phAsset)
+            }
+        })
+        let sections = Dictionary(grouping: assets.map{ TLPHAsset(asset: $0) }) { (element) -> String in
+            if let creationDate = element.phAsset?.creationDate {
+                let identifier = dateFormatter.string(from: creationDate)
+                return identifier
+            }
+            return ""
+        }
+        return sections
+    }
+
+    func section(groupedBy: PHFetchedResultGroupedBy) -> [(String,[TLPHAsset])] {
+        let dict = enumarateFetchResult(groupedBy: groupedBy)
+        var sections = [(String,[TLPHAsset])]()
+        let sortedKeys = dict.keys.sorted(by: >)
+        for key in sortedKeys {
+            if let array = dict[key] {
+                sections.append((key, array))
+            }
+        }
+        return sections
+    }
+}

+ 370 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLAssetsCollection.swift

@@ -0,0 +1,370 @@
+//
+//  TLAssetsCollection.swift
+//  TLPhotosPicker
+//
+//  Created by wade.hawk on 2017. 4. 18..
+//  Copyright © 2017년 wade.hawk. All rights reserved.
+//
+
+import Foundation
+import Photos
+import PhotosUI
+import MobileCoreServices
+
+public struct TLPHAsset {
+    enum CloudDownloadState {
+        case ready, progress, complete, failed
+    }
+    
+    public enum AssetType {
+        case photo, video, livePhoto
+    }
+    
+    public enum ImageExtType: String {
+        case png, jpg, gif, heic
+    }
+    
+    var state = CloudDownloadState.ready
+    public var phAsset: PHAsset? = nil
+    //Bool to check if TLPHAsset returned is created using camera.
+    public var isSelectedFromCamera = false
+    public var selectedOrder: Int = 0
+    public var type: AssetType {
+        get {
+            guard let phAsset = self.phAsset else { return .photo }
+            if phAsset.mediaSubtypes.contains(.photoLive) {
+                return .livePhoto
+            }else if phAsset.mediaType == .video {
+                return .video
+            }else {
+                return .photo
+            }
+        }
+    }
+    
+    public var fullResolutionImage: UIImage? {
+        get {
+            guard let phAsset = self.phAsset else { return nil }
+            return TLPhotoLibrary.fullResolutionImageData(asset: phAsset)
+        }
+    }
+    
+    public func extType() -> ImageExtType {
+        var ext = ImageExtType.png
+        if let fileName = self.originalFileName, let extention = URL(string: fileName)?.pathExtension.lowercased() {
+            ext = ImageExtType(rawValue: extention) ?? .png
+        }
+        return ext
+    }
+    
+    @discardableResult
+    public func cloudImageDownload(progressBlock: @escaping (Double) -> Void, completionBlock:@escaping (UIImage?)-> Void ) -> PHImageRequestID? {
+        guard let phAsset = self.phAsset else { return nil }
+        return TLPhotoLibrary.cloudImageDownload(asset: phAsset, progressBlock: progressBlock, completionBlock: completionBlock)
+    }
+    
+    public var originalFileName: String? {
+        get {
+            guard let phAsset = self.phAsset,let resource = PHAssetResource.assetResources(for: phAsset).first else { return nil }
+            return resource.originalFilename
+        }
+    }
+    
+    public func photoSize(options: PHImageRequestOptions? = nil ,completion: @escaping ((Int)->Void), livePhotoVideoSize: Bool = false) {
+        guard let phAsset = self.phAsset, self.type == .photo || self.type == .livePhoto else { completion(-1); return }
+        var resource: PHAssetResource? = nil
+        if phAsset.mediaSubtypes.contains(.photoLive) == true, livePhotoVideoSize {
+            resource = PHAssetResource.assetResources(for: phAsset).filter { $0.type == .pairedVideo }.first
+        }else {
+            resource = PHAssetResource.assetResources(for: phAsset).filter { $0.type == .photo }.first
+        }
+        if let fileSize = resource?.value(forKey: "fileSize") as? Int {
+            completion(fileSize)
+        }else {
+            PHImageManager.default().requestImageData(for: phAsset, options: nil) { (data, uti, orientation, info) in
+                var fileSize = -1
+                if let data = data {
+                    let bcf = ByteCountFormatter()
+                    bcf.countStyle = .file
+                    fileSize = data.count
+                }
+                DispatchQueue.main.async {
+                    completion(fileSize)
+                }
+            }
+        }
+    }
+    
+    public func videoSize(options: PHVideoRequestOptions? = nil, completion: @escaping ((Int)->Void)) {
+        guard let phAsset = self.phAsset, self.type == .video else {  completion(-1); return }
+        let resource = PHAssetResource.assetResources(for: phAsset).filter { $0.type == .video }.first
+        if let fileSize = resource?.value(forKey: "fileSize") as? Int {
+            completion(fileSize)
+        }else {
+            PHImageManager.default().requestAVAsset(forVideo: phAsset, options: options) { (avasset, audioMix, info) in
+                func fileSize(_ url: URL?) -> Int? {
+                    do {
+                        guard let fileSize = try url?.resourceValues(forKeys: [.fileSizeKey]).fileSize else { return nil }
+                        return fileSize
+                    }catch { return nil }
+                }
+                var url: URL? = nil
+                if let urlAsset = avasset as? AVURLAsset {
+                    url = urlAsset.url
+                }else if let sandboxKeys = info?["PHImageFileSandboxExtensionTokenKey"] as? String, let path = sandboxKeys.components(separatedBy: ";").last {
+                    url = URL(fileURLWithPath: path)
+                }
+                let size = fileSize(url) ?? -1
+                DispatchQueue.main.async {
+                    completion(size)
+                }
+            }
+        }
+    }
+    
+    func MIMEType(_ url: URL?) -> String? {
+        guard let ext = url?.pathExtension else { return nil }
+        if !ext.isEmpty {
+            let UTIRef = UTTypeCreatePreferredIdentifierForTag("public.filename-extension" as CFString, ext as CFString, nil)
+            let UTI = UTIRef?.takeUnretainedValue()
+            UTIRef?.release()
+            if let UTI = UTI {
+                guard let MIMETypeRef = UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType) else { return nil }
+                let MIMEType = MIMETypeRef.takeUnretainedValue()
+                MIMETypeRef.release()
+                return MIMEType as String
+            }
+        }
+        return nil
+    }
+    
+    @discardableResult
+    //convertLivePhotosToJPG
+    // false : If you want mov file at live photos
+    // true  : If you want png file at live photos ( HEIC )
+    public func tempCopyMediaFile(videoRequestOptions: PHVideoRequestOptions? = nil, imageRequestOptions: PHImageRequestOptions? = nil, exportPreset: String = AVAssetExportPresetHighestQuality, convertLivePhotosToJPG: Bool = false, progressBlock:((Double) -> Void)? = nil, completionBlock:@escaping ((URL,String) -> Void)) -> PHImageRequestID? {
+        guard let phAsset = self.phAsset else { return nil }
+        var type: PHAssetResourceType? = nil
+        if phAsset.mediaSubtypes.contains(.photoLive) == true, convertLivePhotosToJPG == false {
+            type = .pairedVideo
+        }else {
+            type = phAsset.mediaType == .video ? .video : .photo
+        }
+        guard let resource = (PHAssetResource.assetResources(for: phAsset).filter{ $0.type == type }).first else { return nil }
+        let fileName = resource.originalFilename
+        var writeURL: URL? = nil
+        if #available(iOS 10.0, *) {
+            writeURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(fileName)")
+        } else {
+            writeURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("\(fileName)")
+        }
+        if (writeURL?.pathExtension.uppercased() == "HEIC" || writeURL?.pathExtension.uppercased() == "HEIF") && convertLivePhotosToJPG {
+            if let fileName2 = writeURL?.deletingPathExtension().lastPathComponent {
+                writeURL?.deleteLastPathComponent()
+                writeURL?.appendPathComponent("\(fileName2).jpg")
+            }
+        }
+        guard let localURL = writeURL,let mimetype = MIMEType(writeURL) else { return nil }
+        switch phAsset.mediaType {
+        case .video:
+            var requestOptions = PHVideoRequestOptions()
+            if let options = videoRequestOptions {
+                requestOptions = options
+            }else {
+                requestOptions.isNetworkAccessAllowed = true
+            }
+            //iCloud download progress
+            requestOptions.progressHandler = { (progress, error, stop, info) in
+                DispatchQueue.main.async {
+                    progressBlock?(progress)
+                }
+            }
+            return PHImageManager.default().requestExportSession(forVideo: phAsset, options: requestOptions, exportPreset: exportPreset) { (session, infoDict) in
+                session?.outputURL = localURL
+                session?.outputFileType = AVFileType.mov
+                session?.exportAsynchronously(completionHandler: {
+                    DispatchQueue.main.async {
+                        completionBlock(localURL, mimetype)
+                    }
+                })
+            }
+        case .image:
+            var requestOptions = PHImageRequestOptions()
+            if let options = imageRequestOptions {
+                requestOptions = options
+            }else {
+                requestOptions.isNetworkAccessAllowed = true
+            }
+            //iCloud download progress
+            requestOptions.progressHandler = { (progress, error, stop, info) in
+                DispatchQueue.main.async {
+                    progressBlock?(progress)
+                }
+            }
+            return PHImageManager.default().requestImageData(for: phAsset, options: requestOptions, resultHandler: { (data, uti, orientation, info) in
+                do {
+                    var data = data
+                    let needConvertLivePhotoToJPG = phAsset.mediaSubtypes.contains(.photoLive) == true && convertLivePhotosToJPG == true
+                    if needConvertLivePhotoToJPG, let imgData = data, let rawImage = UIImage(data: imgData)?.upOrientationImage() {
+                        data = rawImage.jpegData(compressionQuality: 1)
+                    }
+                    try data?.write(to: localURL)
+                    DispatchQueue.main.async {
+                        completionBlock(localURL, mimetype)
+                    }
+                }catch { }
+            })
+        default:
+            return nil
+        }
+    }
+    
+    private func videoFilename(phAsset: PHAsset) -> URL? {
+        guard let resource = (PHAssetResource.assetResources(for: phAsset).filter{ $0.type == .video }).first else {
+            return nil
+        }
+        var writeURL: URL?
+        let fileName = resource.originalFilename
+        if #available(iOS 10.0, *) {
+            writeURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(fileName)")
+        } else {
+            writeURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("\(fileName)")
+        }
+        return writeURL
+    }
+    
+    //Apparently, This is not the only way to export video.
+    //There is many way that export a video.
+    //This method was one of them.
+    public func exportVideoFile(options: PHVideoRequestOptions? = nil,
+                                outputURL: URL? = nil,
+                                outputFileType: AVFileType = .mov,
+                                progressBlock:((Double) -> Void)? = nil,
+                                completionBlock:@escaping ((URL,String) -> Void)) {
+        guard
+            let phAsset = self.phAsset,
+            phAsset.mediaType == .video,
+            let writeURL = outputURL ?? videoFilename(phAsset: phAsset),
+            let mimetype = MIMEType(writeURL)
+            else {
+                return
+        }
+        var requestOptions = PHVideoRequestOptions()
+        if let options = options {
+            requestOptions = options
+        }else {
+            requestOptions.isNetworkAccessAllowed = true
+        }
+        requestOptions.progressHandler = { (progress, error, stop, info) in
+            DispatchQueue.main.async {
+                progressBlock?(progress)
+            }
+        }
+        PHImageManager.default().requestAVAsset(forVideo: phAsset, options: requestOptions) { (avasset, avaudioMix, infoDict) in
+            guard let avasset = avasset else {
+                return
+            }
+            let exportSession = AVAssetExportSession.init(asset: avasset, presetName: AVAssetExportPresetHighestQuality)
+            exportSession?.outputURL = writeURL
+            exportSession?.outputFileType = outputFileType
+            exportSession?.exportAsynchronously(completionHandler: {
+                completionBlock(writeURL, mimetype)
+            })
+        }
+    }
+    
+    init(asset: PHAsset?) {
+        self.phAsset = asset
+    }
+
+    public static func asset(with localIdentifier: String) -> TLPHAsset? {
+        let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
+        return TLPHAsset(asset: fetchResult.firstObject)
+    }
+}
+
+extension TLPHAsset: Equatable {
+    public static func ==(lhs: TLPHAsset, rhs: TLPHAsset) -> Bool {
+        guard let lphAsset = lhs.phAsset, let rphAsset = rhs.phAsset else { return false }
+        return lphAsset.localIdentifier == rphAsset.localIdentifier
+    }
+}
+
+extension Array {
+    subscript (safe index: Int) -> Element? {
+        return indices ~= index ? self[index] : nil
+    }
+}
+
+public struct TLAssetsCollection {
+    var phAssetCollection: PHAssetCollection? = nil
+    var fetchResult: PHFetchResult<PHAsset>? = nil
+    var useCameraButton: Bool = false
+    var recentPosition: CGPoint = CGPoint.zero
+    var title: String
+    var localIdentifier: String
+    public var sections: [(title: String, assets: [TLPHAsset])]? = nil
+    var count: Int {
+        get {
+            guard let count = self.fetchResult?.count, count > 0 else { return self.useCameraButton ? 1 : 0 }
+            return count + (self.useCameraButton ? 1 : 0)
+        }
+    }
+    
+    init(collection: PHAssetCollection) {
+        self.phAssetCollection = collection
+        self.title = collection.localizedTitle ?? ""
+        self.localIdentifier = collection.localIdentifier
+    }
+    
+    func getAsset(at index: Int) -> PHAsset? {
+        if self.useCameraButton && index == 0 { return nil }
+        let index = index - (self.useCameraButton ? 1 : 0)
+        guard let result = self.fetchResult, index < result.count else { return nil }
+        return result.object(at: max(index,0))
+    }
+    
+    func getTLAsset(at indexPath: IndexPath) -> TLPHAsset? {
+        let isCameraRow = self.useCameraButton && indexPath.section == 0 && indexPath.row == 0
+        if isCameraRow {
+            return nil
+        }
+        if let sections = self.sections {
+            let index = indexPath.row - ((self.useCameraButton && indexPath.section == 0) ? 1 : 0)
+            let result = sections[safe: indexPath.section]
+            return result?.assets[safe: index]
+        }else {
+            var index = indexPath.row
+            index = index - (self.useCameraButton ? 1 : 0)
+            guard let result = self.fetchResult, index < result.count else { return nil }
+            return TLPHAsset(asset: result.object(at: max(index,0)))
+        }
+    }
+    
+    mutating func reloadSection(groupedBy: PHFetchedResultGroupedBy) {
+        var groupedSections = self.section(groupedBy: groupedBy)
+        if self.useCameraButton {
+            groupedSections.insert(("camera",[TLPHAsset(asset: nil)]), at: 0)
+        }
+        self.sections = groupedSections
+    }
+    
+    static func ==(lhs: TLAssetsCollection, rhs: TLAssetsCollection) -> Bool {
+        return lhs.localIdentifier == rhs.localIdentifier
+    }
+}
+
+extension UIImage {
+    func upOrientationImage() -> UIImage? {
+        switch imageOrientation {
+        case .up:
+            return self
+        default:
+            UIGraphicsBeginImageContextWithOptions(size, false, scale)
+            draw(in: CGRect(origin: .zero, size: size))
+            let result = UIGraphicsGetImageFromCurrentImageContext()
+            UIGraphicsEndImageContext()
+            return result
+        }
+    }
+}

+ 29 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLBundle.swift

@@ -0,0 +1,29 @@
+//
+//  TLBundle.swift
+//  Pods
+//
+//  Created by wade.hawk on 2017. 5. 9..
+//
+//
+
+import Foundation
+
+open class TLBundle {
+    open class func podBundleImage(named: String) -> UIImage? {
+        let podBundle = Bundle(for: TLBundle.self)
+        if let url = podBundle.url(forResource: "TLPhotoPickerController", withExtension: "bundle") {
+            let bundle = Bundle(url: url)
+            return UIImage(named: named, in: bundle, compatibleWith: nil)!
+        }
+        return nil
+    }
+    
+    class func bundle() -> Bundle {
+        let podBundle = Bundle(for: TLBundle.self)
+        if let url = podBundle.url(forResource: "TLPhotoPicker", withExtension: "bundle") {
+            let bundle = Bundle(url: url)
+            return bundle ?? podBundle
+        }
+        return podBundle
+    }
+}

+ 25 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLCollectionTableViewCell.swift

@@ -0,0 +1,25 @@
+//
+//  TLCollectionTableViewCell.swift
+//  TLPhotosPicker
+//
+//  Created by wade.hawk on 2017. 5. 3..
+//  Copyright © 2017년 wade.hawk. All rights reserved.
+//
+
+import UIKit
+
+open class TLCollectionTableViewCell: UITableViewCell {
+    @IBOutlet open var thumbImageView: UIImageView!
+    @IBOutlet open var titleLabel: UILabel!
+    @IBOutlet open var subTitleLabel: UILabel!
+    
+    override open func awakeFromNib() {
+        super.awakeFromNib()   
+        if #available(iOS 11.0, *) {
+            self.thumbImageView.accessibilityIgnoresInvertColors = true
+        }
+        if #available(iOS 13.0, *) {
+//            self.contentView.backgroundColor = .systemBackground
+        }
+    }
+}

+ 60 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLCollectionTableViewCell.xib

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="TLCollectionTableViewCell" id="9FY-S1-KKQ" customClass="TLCollectionTableViewCell" customModule="TLPhotoPicker" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="75"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="9FY-S1-KKQ" id="UUZ-nI-xH1">
+                <rect key="frame" x="0.0" y="0.0" width="375" height="75"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4Dk-kD-sMy">
+                        <rect key="frame" x="15" y="12.5" width="50" height="50"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="50" id="yiu-ZU-58w"/>
+                            <constraint firstAttribute="width" constant="50" id="zAc-Fo-aGe"/>
+                        </constraints>
+                    </imageView>
+                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="7nv-2U-xMp">
+                        <rect key="frame" x="75" y="19.5" width="235" height="36.5"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Camera Roll" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ES8-Tq-6Wz">
+                                <rect key="frame" x="0.0" y="0.0" width="235" height="17"/>
+                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="9999" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NuJ-3w-ZAP">
+                                <rect key="frame" x="0.0" y="22" width="235" height="14.5"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                                <color key="textColor" red="0.43137254901960786" green="0.43137254901960786" blue="0.43137254901960786" alpha="1" colorSpace="calibratedRGB"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                    </stackView>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="4Dk-kD-sMy" firstAttribute="leading" secondItem="UUZ-nI-xH1" secondAttribute="leading" constant="15" id="7W9-hT-caE"/>
+                    <constraint firstItem="7nv-2U-xMp" firstAttribute="centerY" secondItem="UUZ-nI-xH1" secondAttribute="centerY" id="E6U-ch-GDV"/>
+                    <constraint firstAttribute="trailingMargin" secondItem="7nv-2U-xMp" secondAttribute="trailing" constant="50" id="T3U-hj-718"/>
+                    <constraint firstItem="4Dk-kD-sMy" firstAttribute="centerY" secondItem="UUZ-nI-xH1" secondAttribute="centerY" id="ZHB-JZ-YKE"/>
+                    <constraint firstItem="7nv-2U-xMp" firstAttribute="leading" secondItem="4Dk-kD-sMy" secondAttribute="trailing" constant="10" id="i0n-S1-xnn"/>
+                </constraints>
+            </tableViewCellContentView>
+            <connections>
+                <outlet property="subTitleLabel" destination="NuJ-3w-ZAP" id="vlD-VJ-e5z"/>
+                <outlet property="thumbImageView" destination="4Dk-kD-sMy" id="f8b-VP-j8s"/>
+                <outlet property="titleLabel" destination="ES8-Tq-6Wz" id="sYW-0b-o03"/>
+            </connections>
+            <point key="canvasLocation" x="9.5" y="-83"/>
+        </tableViewCell>
+    </objects>
+</document>

+ 178 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoCollectionViewCell.swift

@@ -0,0 +1,178 @@
+//
+//  TLPhotoCollectionViewCell.swift
+//  TLPhotosPicker
+//
+//  Created by wade.hawk on 2017. 5. 3..
+//  Copyright © 2017년 wade.hawk. All rights reserved.
+//
+
+import UIKit
+import PhotosUI
+
+open class TLPlayerView: UIView {
+    @objc open var player: AVPlayer? {
+        get {
+            return playerLayer.player
+        }
+        set {
+            playerLayer.player = newValue
+        }
+    }
+    
+    @objc open var playerLayer: AVPlayerLayer {
+        return layer as! AVPlayerLayer
+    }
+    
+    // Override UIView property
+    override open class var layerClass: AnyClass {
+        return AVPlayerLayer.self
+    }
+}
+
+open class TLPhotoCollectionViewCell: UICollectionViewCell {
+    private var observer: NSObjectProtocol?
+    @IBOutlet open var imageView: UIImageView?
+    @IBOutlet open var playerView: TLPlayerView?
+    @IBOutlet open var livePhotoView: PHLivePhotoView?
+    @IBOutlet open var liveBadgeImageView: UIImageView?
+    @IBOutlet open var durationView: UIView?
+    @IBOutlet open var videoIconImageView: UIImageView?
+    @IBOutlet open var durationLabel: UILabel?
+    @IBOutlet open var indicator: UIActivityIndicatorView?
+    @IBOutlet open var selectedView: UIView?
+    @IBOutlet open var selectedHeight: NSLayoutConstraint?
+    @IBOutlet open var orderLabel: UILabel?
+    @IBOutlet open var orderBgView: UIView?
+    
+    var configure = TLPhotosPickerConfigure() {
+        didSet {
+            self.selectedView?.layer.borderColor = self.configure.selectedColor.cgColor
+            self.orderBgView?.backgroundColor = self.configure.selectedColor
+            self.videoIconImageView?.image = self.configure.videoIcon
+        }
+    }
+    
+    @objc open var isCameraCell = false
+    
+    open var duration: TimeInterval? {
+        didSet {
+            guard let duration = self.duration else { return }
+            self.selectedHeight?.constant = -10
+            self.durationLabel?.text = timeFormatted(timeInterval: duration)
+        }
+    }
+    
+    @objc open var player: AVPlayer? = nil {
+        didSet {
+            if self.configure.autoPlay == false { return }
+            if self.player == nil {
+                self.playerView?.playerLayer.player = nil
+                if let observer = self.observer {
+                    NotificationCenter.default.removeObserver(observer)
+                }
+            }else {
+                self.playerView?.playerLayer.player = self.player
+                self.observer = NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { [weak self] (_) in
+                    DispatchQueue.main.async {
+                        guard let `self` = self else { return }
+                        self.player?.seek(to: CMTime.zero)
+                        self.player?.play()
+                        self.player?.isMuted = self.configure.muteAudio
+                    }
+                })
+            }
+        }
+    }
+    
+    @objc open var selectedAsset: Bool = false {
+        willSet(newValue) {
+            self.selectedView?.isHidden = !newValue
+            self.durationView?.backgroundColor = newValue ? self.configure.selectedColor : UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
+            if !newValue {
+                self.orderLabel?.text = ""
+            }
+        }
+    }
+    
+    @objc open func timeFormatted(timeInterval: TimeInterval) -> String {
+        let seconds: Int = lround(timeInterval)
+        var hour: Int = 0
+        var minute: Int = Int(seconds/60)
+        let second: Int = seconds % 60
+        if minute > 59 {
+            hour = minute / 60
+            minute = minute % 60
+            return String(format: "%d:%d:%02d", hour, minute, second)
+        } else {
+            return String(format: "%d:%02d", minute, second)
+        }
+    }
+    
+    @objc open func popScaleAnim() {
+        UIView.animate(withDuration: 0.1, animations: {
+            self.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
+        }) { _ in
+            UIView.animate(withDuration: 0.1, animations: {
+                self.transform = CGAffineTransform(scaleX: 1, y: 1)
+            })
+        }
+    }
+    
+    @objc open func update(with phAsset: PHAsset) {
+        
+    }
+    
+    @objc open func selectedCell() {
+        
+    }
+    
+    @objc open func willDisplayCell() {
+        
+    }
+    
+    @objc open func endDisplayingCell() {
+        
+    }
+    
+    @objc func stopPlay() {
+        if let player = self.player {
+            player.pause()
+            self.player = nil
+        }
+        self.livePhotoView?.livePhoto = nil
+        self.livePhotoView?.isHidden = true
+        self.livePhotoView?.stopPlayback()
+        self.livePhotoView?.delegate = nil
+    }
+    
+    deinit {
+        NXLLog("deinit TLPhotoCollectionViewCell")
+    }
+    
+    override open func awakeFromNib() {
+        super.awakeFromNib()
+        self.playerView?.playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
+        self.livePhotoView?.isHidden = true
+        self.durationView?.isHidden = true
+        self.selectedView?.isHidden = true
+        self.selectedView?.layer.borderWidth = 10
+        self.selectedView?.layer.cornerRadius = 15
+        self.orderBgView?.layer.cornerRadius = 2
+        self.videoIconImageView?.image = self.configure.videoIcon
+        if #available(iOS 11.0, *) {
+            self.imageView?.accessibilityIgnoresInvertColors = true
+            self.playerView?.accessibilityIgnoresInvertColors = true
+            self.livePhotoView?.accessibilityIgnoresInvertColors = true
+            self.videoIconImageView?.accessibilityIgnoresInvertColors = true
+        }
+    }
+    
+    override open func prepareForReuse() {
+        super.prepareForReuse()
+        stopPlay()
+        self.durationView?.isHidden = true
+        self.durationView?.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
+        self.selectedHeight?.constant = 10
+        self.selectedAsset = false
+    }
+}

+ 144 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoCollectionViewCell.xib

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="TLPhotoCollectionViewCell" id="IF3-4e-v0j" customClass="TLPhotoCollectionViewCell" customModule="TLPhotoPicker" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0cR-fZ-1bW">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vfk-Ao-TKR" customClass="PHLivePhotoView">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    </view>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TGh-wt-hcR">
+                        <rect key="frame" x="70" y="70" width="25" height="25"/>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="25" id="NMA-SI-Idt"/>
+                            <constraint firstAttribute="height" constant="25" id="XvY-Ee-EA2"/>
+                        </constraints>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="apl-2Q-Hz7" customClass="TLPlayerView" customModule="TLPhotoPicker" customModuleProvider="target">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    </view>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d1a-KW-Ta4">
+                        <rect key="frame" x="-5" y="-5" width="110" height="110"/>
+                        <subviews>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hbA-dR-I09">
+                                <rect key="frame" x="75" y="5" width="30" height="30"/>
+                                <subviews>
+                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Weu-ef-IZ5">
+                                        <rect key="frame" x="1.5" y="8" width="25" height="16"/>
+                                        <constraints>
+                                            <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="25" id="YMX-0Z-bfm"/>
+                                        </constraints>
+                                        <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="13"/>
+                                        <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                        <nil key="highlightedColor"/>
+                                    </label>
+                                </subviews>
+                                <color key="backgroundColor" red="0.27843137254901962" green="0.47058823529411764" blue="0.85098039215686272" alpha="1" colorSpace="calibratedRGB"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="30" id="8ln-g5-eHL"/>
+                                    <constraint firstItem="Weu-ef-IZ5" firstAttribute="leading" secondItem="hbA-dR-I09" secondAttribute="leading" constant="1.5" id="Yw3-zp-UZQ"/>
+                                    <constraint firstAttribute="trailing" secondItem="Weu-ef-IZ5" secondAttribute="trailing" constant="3.5" id="jsL-aw-IED"/>
+                                    <constraint firstItem="Weu-ef-IZ5" firstAttribute="centerY" secondItem="hbA-dR-I09" secondAttribute="centerY" constant="1" id="nUh-b3-s9s"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="hbA-dR-I09" secondAttribute="trailing" constant="5" id="hjj-HC-A4B"/>
+                            <constraint firstItem="hbA-dR-I09" firstAttribute="top" secondItem="d1a-KW-Ta4" secondAttribute="top" constant="5" id="xJ1-j7-RX7"/>
+                        </constraints>
+                    </view>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aju-ob-KlZ">
+                        <rect key="frame" x="0.0" y="75" width="100" height="25"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Uzf-HJ-aUY">
+                                <rect key="frame" x="5" y="2.5" width="20" height="20"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                            </imageView>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0:02" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zNx-FC-5V2">
+                                <rect key="frame" x="30" y="0.0" width="65" height="25"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="65" id="3hi-82-ovQ"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" red="0.11109734326601028" green="0.57953345775604248" blue="0.96568840742111206" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="25" id="DzP-0j-1SD"/>
+                            <constraint firstItem="zNx-FC-5V2" firstAttribute="top" secondItem="aju-ob-KlZ" secondAttribute="top" id="Lim-xf-cbx"/>
+                            <constraint firstAttribute="bottom" secondItem="zNx-FC-5V2" secondAttribute="bottom" id="T0P-EH-uPu"/>
+                            <constraint firstAttribute="trailing" secondItem="zNx-FC-5V2" secondAttribute="trailing" constant="5" id="uhl-Y8-3g5"/>
+                        </constraints>
+                    </view>
+                    <activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="DZc-ag-e55">
+                        <rect key="frame" x="40" y="40" width="20" height="20"/>
+                    </activityIndicatorView>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="vfk-Ao-TKR" secondAttribute="bottom" id="2gw-cO-5bN"/>
+                <constraint firstAttribute="bottom" secondItem="aju-ob-KlZ" secondAttribute="bottom" id="2wo-Ki-15d"/>
+                <constraint firstAttribute="trailing" secondItem="aju-ob-KlZ" secondAttribute="trailing" id="2zm-kD-GPn"/>
+                <constraint firstAttribute="bottom" secondItem="apl-2Q-Hz7" secondAttribute="bottom" id="3zu-V3-EAd"/>
+                <constraint firstAttribute="trailing" secondItem="0cR-fZ-1bW" secondAttribute="trailing" id="9sd-Ea-EBO"/>
+                <constraint firstAttribute="trailing" secondItem="vfk-Ao-TKR" secondAttribute="trailing" id="BZ0-ZG-uW5"/>
+                <constraint firstAttribute="bottom" secondItem="TGh-wt-hcR" secondAttribute="bottom" constant="5" id="Ba1-Z6-ypz"/>
+                <constraint firstItem="0cR-fZ-1bW" firstAttribute="leading" secondItem="IF3-4e-v0j" secondAttribute="leading" id="DXe-M9-mxX"/>
+                <constraint firstItem="aju-ob-KlZ" firstAttribute="leading" secondItem="IF3-4e-v0j" secondAttribute="leading" id="FDl-UE-XVM"/>
+                <constraint firstItem="apl-2Q-Hz7" firstAttribute="top" secondItem="IF3-4e-v0j" secondAttribute="top" id="JlA-ya-cf3"/>
+                <constraint firstAttribute="trailing" secondItem="apl-2Q-Hz7" secondAttribute="trailing" id="KZg-oh-B11"/>
+                <constraint firstAttribute="trailing" secondItem="TGh-wt-hcR" secondAttribute="trailing" constant="5" id="Meo-q5-HyZ"/>
+                <constraint firstItem="vfk-Ao-TKR" firstAttribute="leading" secondItem="IF3-4e-v0j" secondAttribute="leading" id="NOQ-KU-fHD"/>
+                <constraint firstItem="vfk-Ao-TKR" firstAttribute="top" secondItem="IF3-4e-v0j" secondAttribute="top" id="NrV-wc-c3q"/>
+                <constraint firstItem="d1a-KW-Ta4" firstAttribute="height" secondItem="IF3-4e-v0j" secondAttribute="height" constant="10" id="VMJ-Qp-D4N"/>
+                <constraint firstItem="apl-2Q-Hz7" firstAttribute="leading" secondItem="IF3-4e-v0j" secondAttribute="leading" id="bsL-0J-0Y5"/>
+                <constraint firstItem="d1a-KW-Ta4" firstAttribute="width" secondItem="IF3-4e-v0j" secondAttribute="width" constant="10" id="clS-DI-jvL"/>
+                <constraint firstItem="d1a-KW-Ta4" firstAttribute="top" secondItem="IF3-4e-v0j" secondAttribute="top" constant="-5" id="d3r-HZ-6We"/>
+                <constraint firstItem="DZc-ag-e55" firstAttribute="centerX" secondItem="IF3-4e-v0j" secondAttribute="centerX" id="fnn-1c-fSn"/>
+                <constraint firstItem="DZc-ag-e55" firstAttribute="centerY" secondItem="IF3-4e-v0j" secondAttribute="centerY" id="qZc-2h-nOx"/>
+                <constraint firstItem="d1a-KW-Ta4" firstAttribute="leading" secondItem="IF3-4e-v0j" secondAttribute="leading" constant="-5" id="sOj-ms-Oo3"/>
+                <constraint firstItem="0cR-fZ-1bW" firstAttribute="top" secondItem="IF3-4e-v0j" secondAttribute="top" id="sSC-fd-MoT"/>
+                <constraint firstAttribute="bottom" secondItem="0cR-fZ-1bW" secondAttribute="bottom" id="xzA-tS-EVK"/>
+            </constraints>
+            <connections>
+                <outlet property="durationLabel" destination="zNx-FC-5V2" id="5SU-lD-AGm"/>
+                <outlet property="durationView" destination="aju-ob-KlZ" id="hiP-Ze-cbc"/>
+                <outlet property="imageView" destination="0cR-fZ-1bW" id="N3f-v5-K9b"/>
+                <outlet property="indicator" destination="DZc-ag-e55" id="R3y-XL-0SY"/>
+                <outlet property="liveBadgeImageView" destination="TGh-wt-hcR" id="cry-3P-rLI"/>
+                <outlet property="livePhotoView" destination="vfk-Ao-TKR" id="uvj-l1-wcp"/>
+                <outlet property="orderBgView" destination="hbA-dR-I09" id="cQo-BP-xh1"/>
+                <outlet property="orderLabel" destination="Weu-ef-IZ5" id="B3t-X5-o3a"/>
+                <outlet property="playerView" destination="apl-2Q-Hz7" id="vTI-ie-mcV"/>
+                <outlet property="selectedHeight" destination="VMJ-Qp-D4N" id="uVe-tQ-4q5"/>
+                <outlet property="selectedView" destination="d1a-KW-Ta4" id="c31-4y-72g"/>
+                <outlet property="videoIconImageView" destination="Uzf-HJ-aUY" id="d7e-Bl-181"/>
+            </connections>
+            <point key="canvasLocation" x="32" y="-10"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 282 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoLibrary.swift

@@ -0,0 +1,282 @@
+//
+//  TLPhotoLibrary.swift
+//  TLPhotosPicker
+//
+//  Created by wade.hawk on 2017. 5. 3..
+//  Copyright © 2017년 wade.hawk. All rights reserved.
+//
+
+import Foundation
+import Photos
+
+protocol TLPhotoLibraryDelegate: class {
+    func loadCameraRollCollection(collection: TLAssetsCollection)
+    func loadCompleteAllCollection(collections: [TLAssetsCollection])
+}
+
+class TLPhotoLibrary {
+    
+    weak var delegate: TLPhotoLibraryDelegate? = nil
+    
+    lazy var imageManager: PHCachingImageManager = {
+        return PHCachingImageManager()
+    }()
+    
+    deinit {
+        //        print("deinit TLPhotoLibrary")
+    }
+    
+    @discardableResult
+    func livePhotoAsset(asset: PHAsset, size: CGSize = CGSize(width: 720, height: 1280), progressBlock: Photos.PHAssetImageProgressHandler? = nil, completionBlock:@escaping (PHLivePhoto,Bool)-> Void ) -> PHImageRequestID {
+        let options = PHLivePhotoRequestOptions()
+        options.deliveryMode = .opportunistic
+        options.isNetworkAccessAllowed = true
+        options.progressHandler = progressBlock
+        let scale = min(UIScreen.main.scale,2)
+        let targetSize = CGSize(width: size.width*scale, height: size.height*scale)
+        let requestID = self.imageManager.requestLivePhoto(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { (livePhoto, info) in
+            let complete = (info?["PHImageResultIsDegradedKey"] as? Bool) == false
+            if let livePhoto = livePhoto {
+                completionBlock(livePhoto,complete)
+            }
+        }
+        return requestID
+    }
+    
+    @discardableResult
+    func videoAsset(asset: PHAsset, size: CGSize = CGSize(width: 720, height: 1280), progressBlock: Photos.PHAssetImageProgressHandler? = nil, completionBlock:@escaping (AVPlayerItem?, [AnyHashable : Any]?) -> Void ) -> PHImageRequestID {
+        let options = PHVideoRequestOptions()
+        options.isNetworkAccessAllowed = true
+        options.deliveryMode = .automatic
+        options.progressHandler = progressBlock
+        let requestID = self.imageManager.requestPlayerItem(forVideo: asset, options: options, resultHandler: { playerItem, info in
+            completionBlock(playerItem,info)
+        })
+        return requestID
+    }
+    
+    @discardableResult
+    func imageAsset(asset: PHAsset, size: CGSize = CGSize(width: 160, height: 160), options: PHImageRequestOptions? = nil, completionBlock:@escaping (UIImage,Bool)-> Void ) -> PHImageRequestID {
+        var options = options
+        if options == nil {
+            options = PHImageRequestOptions()
+            options?.isSynchronous = false
+            options?.resizeMode = .exact
+            options?.deliveryMode = .opportunistic
+            options?.isNetworkAccessAllowed = true
+        }
+        let scale = min(UIScreen.main.scale,2)
+        let targetSize = CGSize(width: size.width*scale, height: size.height*scale)
+        let requestID = self.imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, info in
+            let complete = (info?["PHImageResultIsDegradedKey"] as? Bool) == false
+            if let image = image {
+                completionBlock(image,complete)
+            }
+        }
+        return requestID
+    }
+    
+    func cancelPHImageRequest(requestID: PHImageRequestID) {
+        self.imageManager.cancelImageRequest(requestID)
+    }
+    
+    @discardableResult
+    class func cloudImageDownload(asset: PHAsset, size: CGSize = PHImageManagerMaximumSize, progressBlock: @escaping (Double) -> Void, completionBlock:@escaping (UIImage?)-> Void ) -> PHImageRequestID {
+        let options = PHImageRequestOptions()
+        options.isSynchronous = false
+        options.isNetworkAccessAllowed = true
+        options.deliveryMode = .opportunistic
+        options.version = .current
+        options.resizeMode = .exact
+        options.progressHandler = { (progress,error,stop,info) in
+            progressBlock(progress)
+        }
+        let requestID = PHCachingImageManager().requestImageData(for: asset, options: options) { (imageData, dataUTI, orientation, info) in
+            if let data = imageData,let _ = info {
+                completionBlock(UIImage(data: data))
+            }else{
+                completionBlock(nil)//error
+            }
+        }
+        return requestID
+    }
+    
+    @discardableResult
+    class func fullResolutionImageData(asset: PHAsset) -> UIImage? {
+        let options = PHImageRequestOptions()
+        options.isSynchronous = true
+        options.resizeMode = .none
+        options.isNetworkAccessAllowed = false
+        options.version = .current
+        var image: UIImage? = nil
+        _ = PHCachingImageManager().requestImageData(for: asset, options: options) { (imageData, dataUTI, orientation, info) in
+            if let data = imageData {
+                image = UIImage(data: data)
+            }
+        }
+        return image
+    }
+}
+
+extension PHFetchOptions {
+    func merge(predicate: NSPredicate) {
+        if let storePredicate = self.predicate {
+            self.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [storePredicate, predicate])
+        }else {
+            self.predicate = predicate
+        }
+    }
+}
+
+//MARK: - Load Collection
+extension TLPhotoLibrary {
+    func getOption(configure: TLPhotosPickerConfigure) -> PHFetchOptions {
+        let options: PHFetchOptions
+        if let fetchOption = configure.fetchOption {
+            options = fetchOption
+        }else {
+            options = PHFetchOptions()
+            options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
+        }
+        if let mediaType = configure.mediaType {
+            let mediaPredicate = NSPredicate(format: "mediaType = %i", mediaType.rawValue)
+            options.merge(predicate: mediaPredicate)
+        }
+        if configure.allowedVideo == false {
+            let notVideoPredicate = NSPredicate(format: "mediaType != %i", PHAssetMediaType.video.rawValue)
+            options.merge(predicate: notVideoPredicate)
+        }
+        if configure.allowedLivePhotos == false {
+            let notLivePhotoPredicate = NSPredicate(format: "NOT ((mediaSubtype & %d) != 0)", PHAssetMediaSubtype.photoLive.rawValue)
+            options.merge(predicate: notLivePhotoPredicate)
+        }
+        if let maxVideoDuration = configure.maxVideoDuration {
+            let durationPredicate = NSPredicate(format: "duration < %f", maxVideoDuration)
+            options.merge(predicate: durationPredicate)
+        }
+        return options
+    }
+    
+    func fetchResult(collection: TLAssetsCollection?, configure: TLPhotosPickerConfigure) -> PHFetchResult<PHAsset>? {
+        guard let phAssetCollection = collection?.phAssetCollection else { return nil }
+        let options = getOption(configure: configure)
+        return PHAsset.fetchAssets(in: phAssetCollection, options: options)
+    }
+    
+    func fetchCollection(configure: TLPhotosPickerConfigure) {
+        let useCameraButton = configure.usedCameraButton
+        let options = getOption(configure: configure)
+        let fetchCollectionOption = configure.fetchCollectionOption
+        
+        func getAlbum(subType: PHAssetCollectionSubtype, result: inout [TLAssetsCollection]) {
+            let collectionOption = fetchCollectionOption[.assetCollections(.album)]
+            let fetchCollection = PHAssetCollection.fetchAssetCollections(with: .album,
+                                                                          subtype: subType,
+                                                                          options: collectionOption)
+            var collections = [PHAssetCollection]()
+            fetchCollection.enumerateObjects { (collection, index, _) in 
+                if configure.allowedAlbumCloudShared == false && collection.assetCollectionSubtype == .albumCloudShared {
+                }else {
+                    collections.append(collection)
+                }
+            }
+            for collection in collections {
+                if !result.contains(where: { $0.localIdentifier == collection.localIdentifier }) {
+                    var assetsCollection = TLAssetsCollection(collection: collection)
+                    assetsCollection.title = configure.customLocalizedTitle[assetsCollection.title] ?? assetsCollection.title
+                    assetsCollection.fetchResult = PHAsset.fetchAssets(in: collection, options: options)
+                    if assetsCollection.count > 0 {
+                        result.append(assetsCollection)
+                    }
+                }
+            }
+        }
+        
+        @discardableResult
+        func getSmartAlbum(subType: PHAssetCollectionSubtype,
+                           useCameraButton: Bool = false,
+                           result: inout [TLAssetsCollection])
+            -> TLAssetsCollection?
+        {
+            let collectionOption = fetchCollectionOption[.assetCollections(.smartAlbum)]
+            let fetchCollection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum,
+                                                                          subtype: subType,
+                                                                          options: collectionOption)
+            if
+                let collection = fetchCollection.firstObject,
+                result.contains(where: { $0.localIdentifier == collection.localIdentifier }) == false
+            {
+                var assetsCollection = TLAssetsCollection(collection: collection)
+                assetsCollection.title = configure.customLocalizedTitle[assetsCollection.title] ?? assetsCollection.title
+                assetsCollection.fetchResult = PHAsset.fetchAssets(in: collection, options: options)
+                if assetsCollection.count > 0 || useCameraButton {
+                    result.append(assetsCollection)
+                    return assetsCollection
+                }
+            }
+            return nil
+        }
+        if let fetchCollectionTypes = configure.fetchCollectionTypes {
+            DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+                var assetCollections = [TLAssetsCollection]()
+                for (type,subType) in fetchCollectionTypes {
+                    if type == .smartAlbum {
+                        getSmartAlbum(subType: subType, result: &assetCollections)
+                    }else {
+                        getAlbum(subType: subType, result: &assetCollections)
+                    }
+                }
+                DispatchQueue.main.async {
+                    self?.delegate?.loadCompleteAllCollection(collections: assetCollections)
+                }
+            }
+        }else {
+            DispatchQueue.global(qos: .userInteractive).async { [weak self] in
+                var assetCollections = [TLAssetsCollection]()
+                //Camera Roll
+                let camerarollCollection = getSmartAlbum(subType: .smartAlbumUserLibrary,
+                                                         useCameraButton: useCameraButton,
+                                                         result: &assetCollections)
+                if var cameraRoll = camerarollCollection {
+                    cameraRoll.title = configure.customLocalizedTitle[cameraRoll.title] ?? cameraRoll.title
+                    cameraRoll.useCameraButton = useCameraButton
+                    assetCollections[0] = cameraRoll
+                    DispatchQueue.main.async {
+                        self?.delegate?.loadCameraRollCollection(collection: cameraRoll)
+                    }
+                }
+                //Selfies
+                getSmartAlbum(subType: .smartAlbumSelfPortraits, result: &assetCollections)
+                //Panoramas
+                getSmartAlbum(subType: .smartAlbumPanoramas, result: &assetCollections)
+                //Favorites
+                getSmartAlbum(subType: .smartAlbumFavorites, result: &assetCollections)
+                //CloudShared
+                getSmartAlbum(subType: .albumCloudShared, result: &assetCollections)
+                //get all another albums
+                getAlbum(subType: .any, result: &assetCollections)
+                if configure.allowedVideo {
+                    //Videos
+                    getSmartAlbum(subType: .smartAlbumVideos, result: &assetCollections)
+                }
+                //Album
+                let collectionOption = fetchCollectionOption[.topLevelUserCollections]
+                let albumsResult = PHCollectionList.fetchTopLevelUserCollections(with: collectionOption)
+                albumsResult.enumerateObjects({ (collection, index, stop) -> Void in
+                    guard let collection = collection as? PHAssetCollection else { return }
+                    var assetsCollection = TLAssetsCollection(collection: collection)
+                    assetsCollection.title = configure.customLocalizedTitle[assetsCollection.title] ?? assetsCollection.title
+                    assetsCollection.fetchResult = PHAsset.fetchAssets(in: collection, options: options)
+                    if assetsCollection.count > 0, !assetCollections.contains(where: { $0.localIdentifier == collection.localIdentifier }) {
+                        assetCollections.append(assetsCollection)
+                    }
+                })
+                
+                DispatchQueue.main.async {
+                    self?.delegate?.loadCompleteAllCollection(collections: assetCollections)
+                }
+            }
+        }
+    }
+}
+

+ 71 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomCell_Instagram.swift

@@ -0,0 +1,71 @@
+//
+//  CustomCell_Instagram.swift
+//  TLPhotoPicker
+//
+//  Created by wade.hawk on 2017. 5. 15..
+//  Copyright © 2017년 CocoaPods. All rights reserved.
+//
+
+import Foundation
+import PhotosUI
+
+class CustomCell_Instagram: TLPhotoCollectionViewCell {
+    
+    
+    
+    @IBOutlet weak var selectedBgView: UIView!
+    let selectedColor = kThemeColor
+    
+    override var duration: TimeInterval? {
+        didSet {
+            self.durationLabel?.isHidden = self.duration == nil ? true : false
+            guard let duration = self.duration else { return }
+            self.durationLabel?.text = timeFormatted(timeInterval: duration)
+        }
+    }
+    
+    override var isCameraCell: Bool {
+        didSet {
+            self.orderLabel?.isHidden = self.isCameraCell
+        }
+    }
+    
+    override public var selectedAsset: Bool {
+        willSet(newValue) {
+            self.orderLabel?.backgroundColor = newValue ? self.selectedColor : UIColor(red: 1, green: 1, blue: 1, alpha: 0.3)
+            self.selectedBgView.backgroundColor = newValue ? kThemeColor.withAlphaComponent(0.2) : UIColor.clear
+            self.selectedBgView?.layer.borderColor =  newValue ? kThemeColor.cgColor : UIColor.clear.cgColor
+            self.selectedBgView?.layer.borderWidth = 1.5
+        }
+    }
+    
+    override func update(with phAsset: PHAsset) {
+        super.update(with: phAsset)
+    }
+    
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        self.durationView?.backgroundColor = UIColor.clear
+        self.orderLabel?.clipsToBounds = true
+        self.orderLabel?.layer.cornerRadius = 11
+        self.orderLabel?.layer.borderWidth = 1
+        self.orderLabel?.layer.borderColor = UIColor.white.cgColor
+        self.selectedBgView?.backgroundColor = UIColor.clear
+        self.selectedBgView?.layer.borderColor = UIColor.clear.cgColor
+        self.selectedBgView?.layer.borderWidth = 1.5
+        self.orderLabel?.addTapGesture(1, target: self, action: #selector(orderSelected))
+    }
+    
+    override open func prepareForReuse() {
+        super.prepareForReuse()
+        self.durationView?.backgroundColor = UIColor.clear
+        self.selectedBgView?.backgroundColor = UIColor.clear
+        self.selectedBgView?.layer.borderColor = UIColor.clear.cgColor
+        self.selectedBgView?.layer.borderWidth = 1.5
+        
+    }
+    
+    @objc func orderSelected() {
+        
+    }
+}

+ 104 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomCell_Instagram.xib

@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" restorationIdentifier="CustomCell_Instagram" reuseIdentifier="CustomCell_Instagram" id="9Rv-Fn-yCT" customClass="CustomCell_Instagram" customModule="RainbowPlanet" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="100" height="112"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                <rect key="frame" x="0.0" y="0.0" width="100" height="112"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="rOH-JI-pE1">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="112"/>
+                    </imageView>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="aFk-4q-7GA">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="112"/>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    </view>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uwg-fz-Lnl">
+                        <rect key="frame" x="0.0" y="0.0" width="100" height="112"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JhX-P9-VnM">
+                                <rect key="frame" x="73" y="5" width="22" height="22"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" secondItem="JhX-P9-VnM" secondAttribute="height" multiplier="1:1" id="HyJ-ox-Pd8"/>
+                                    <constraint firstAttribute="width" constant="22" id="ZSd-26-g6g"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="13"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="JhX-P9-VnM" secondAttribute="trailing" constant="5" id="T4h-5Q-DtR"/>
+                            <constraint firstItem="JhX-P9-VnM" firstAttribute="top" secondItem="uwg-fz-Lnl" secondAttribute="top" constant="5" id="hIw-3h-tPP"/>
+                        </constraints>
+                    </view>
+                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dN5-jZ-SiB">
+                        <rect key="frame" x="0.0" y="87" width="100" height="25"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0AD-DJ-hk7">
+                                <rect key="frame" x="30" y="0.0" width="65" height="25"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="65" id="Hky-qZ-eo9"/>
+                                </constraints>
+                                <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                                <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="bottom" secondItem="0AD-DJ-hk7" secondAttribute="bottom" id="B6M-k2-HZD"/>
+                            <constraint firstAttribute="trailing" secondItem="0AD-DJ-hk7" secondAttribute="trailing" constant="5" id="c2b-dQ-g7Y"/>
+                            <constraint firstAttribute="height" constant="25" id="fEJ-nX-P4S"/>
+                            <constraint firstItem="0AD-DJ-hk7" firstAttribute="top" secondItem="dN5-jZ-SiB" secondAttribute="top" id="uOO-Wg-zvQ"/>
+                        </constraints>
+                    </view>
+                    <activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="1Zf-X9-xCZ">
+                        <rect key="frame" x="40" y="46" width="20" height="20"/>
+                    </activityIndicatorView>
+                </subviews>
+            </view>
+            <constraints>
+                <constraint firstItem="1Zf-X9-xCZ" firstAttribute="centerX" secondItem="9Rv-Fn-yCT" secondAttribute="centerX" id="4EP-vr-Yn3"/>
+                <constraint firstItem="aFk-4q-7GA" firstAttribute="top" secondItem="9Rv-Fn-yCT" secondAttribute="top" id="7SX-Im-vEN"/>
+                <constraint firstItem="dN5-jZ-SiB" firstAttribute="leading" secondItem="9Rv-Fn-yCT" secondAttribute="leading" id="81d-II-POL"/>
+                <constraint firstItem="rOH-JI-pE1" firstAttribute="leading" secondItem="9Rv-Fn-yCT" secondAttribute="leading" id="KJ3-eV-OY7"/>
+                <constraint firstAttribute="trailing" secondItem="dN5-jZ-SiB" secondAttribute="trailing" id="Mbt-2E-UCm"/>
+                <constraint firstItem="uwg-fz-Lnl" firstAttribute="leading" secondItem="9Rv-Fn-yCT" secondAttribute="leading" id="N99-b3-cRO"/>
+                <constraint firstAttribute="bottom" secondItem="aFk-4q-7GA" secondAttribute="bottom" id="OPU-Nj-MzI"/>
+                <constraint firstAttribute="trailing" secondItem="rOH-JI-pE1" secondAttribute="trailing" id="Ry1-8z-byA"/>
+                <constraint firstAttribute="bottom" secondItem="dN5-jZ-SiB" secondAttribute="bottom" id="SQo-t4-jNX"/>
+                <constraint firstItem="1Zf-X9-xCZ" firstAttribute="centerY" secondItem="9Rv-Fn-yCT" secondAttribute="centerY" id="Ti5-PE-sjK"/>
+                <constraint firstItem="rOH-JI-pE1" firstAttribute="top" secondItem="9Rv-Fn-yCT" secondAttribute="top" id="ZZd-iV-jqo"/>
+                <constraint firstAttribute="bottom" secondItem="rOH-JI-pE1" secondAttribute="bottom" id="ZfF-qA-gEg"/>
+                <constraint firstAttribute="trailing" secondItem="aFk-4q-7GA" secondAttribute="trailing" id="aYO-CP-TQc"/>
+                <constraint firstAttribute="bottom" secondItem="uwg-fz-Lnl" secondAttribute="bottom" id="dGK-ts-Oxc"/>
+                <constraint firstAttribute="trailing" secondItem="uwg-fz-Lnl" secondAttribute="trailing" id="dkB-IK-EYy"/>
+                <constraint firstItem="uwg-fz-Lnl" firstAttribute="top" secondItem="9Rv-Fn-yCT" secondAttribute="top" id="izZ-b1-P9h"/>
+                <constraint firstItem="aFk-4q-7GA" firstAttribute="leading" secondItem="9Rv-Fn-yCT" secondAttribute="leading" id="yKB-rr-UJn"/>
+            </constraints>
+            <size key="customSize" width="100" height="112"/>
+            <connections>
+                <outlet property="durationLabel" destination="0AD-DJ-hk7" id="fAK-hF-IpB"/>
+                <outlet property="imageView" destination="rOH-JI-pE1" id="tgu-TV-hTi"/>
+                <outlet property="indicator" destination="1Zf-X9-xCZ" id="b7n-5J-KNK"/>
+                <outlet property="orderLabel" destination="JhX-P9-VnM" id="Oev-lS-XRO"/>
+                <outlet property="selectedBgView" destination="uwg-fz-Lnl" id="y32-rS-wPh"/>
+            </connections>
+            <point key="canvasLocation" x="32" y="-16.191904047976013"/>
+        </collectionViewCell>
+    </objects>
+</document>

+ 183 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/Custom/CustomTLPhotoPickerViewController.swift

@@ -0,0 +1,183 @@
+//
+//  CustomTLPhotoPickerViewController.swift
+//  RainbowPlanet
+//
+//  Created by 南鑫林 on 2019/10/20.
+//  Copyright © 2019 RainbowPlanet. All rights reserved.
+//
+
+import UIKit
+import RxSwift
+import Kingfisher
+
+import SwiftyMediator
+
+class CustomTLPhotoPickerViewController: TLPhotosPickerViewController {
+
+    deinit {
+        NXLLog("deinit")
+    }
+    
+    override func didReceiveMemoryWarning() {
+        super.didReceiveMemoryWarning()
+        KingfisherManager.shared.cache.clearDiskCache()
+        KingfisherManager.shared.cache.clearMemoryCache()
+    }
+    
+    let disposeBag = DisposeBag()
+    var mediaType: PHAssetMediaType? = nil
+    var numberOfColumn : Int? = nil
+    var maxSelectedAssets : Int? = nil
+    var allowedVideoRecording : Bool = false
+    
+    lazy var customNavigationBar = WRCustomNavigationBar.CustomNavigationBar()
+    
+    override func viewDidLoad() {
+        customPhotoPickerView()
+        super.viewDidLoad()
+        setupNavigationBar()
+    }
+    
+    func customPhotoPickerView() {
+        delegate = self
+        self.navigationBar.isHidden = true
+        var configure = TLPhotosPickerConfigure()
+        configure.maxSelectedAssets = maxSelectedAssets
+        configure.numberOfColumn = 4
+        configure.nibSet = (nibName: "CustomCell_Instagram", bundle: Bundle.main)
+        configure.mediaType = mediaType
+        configure.cameraBgColor = k404040Color
+        configure.cameraIcon = kImage(name: "btn_photograph")
+        configure.allowedVideoRecording = allowedVideoRecording
+        self.configure = configure
+        logDelegate = self
+    }
+    
+    fileprivate func setupNavigationBar()
+    {
+        navigationController?.navigationBar.isHidden = true
+        view.backgroundColor = UIColor.white
+        if #available(iOS 11.0, *) {
+            UIScrollView.appearance().contentInsetAdjustmentBehavior = .never
+        } else {
+            if self.responds(to: #selector(setter: self.automaticallyAdjustsScrollViewInsets)) {
+                self.automaticallyAdjustsScrollViewInsets = false
+            }
+        }
+        
+        customNavigationBar.wr_setBottomLineHidden(hidden: false)
+        customNavigationBar.barBackgroundColor = UIColor.white
+        customNavigationBar.titleLabelColor = k333333Color
+        customNavigationBar.title = "所有照片"
+        statusBarStyle = .default
+        view.addSubview(customNavigationBar)
+        customNavigationBar.addSubview(nextButton)
+        nextButton.snp.makeConstraints { (make) in
+            make.right.equalTo(-14)
+            make.centerY.equalTo(customNavigationBar.titleLabel)
+        }
+        
+        nextButton.rx.tap.subscribe(onNext: { (data) in
+            
+        }).disposed(by: disposeBag)
+        
+        if self.navigationController != nil {
+            if self.navigationController?.viewControllers.count == 1 {
+                if self.navigationController?.presentingViewController != nil {
+                    setLeftButton(image: UIImage(named: "navbar_back_black")! )
+                }
+            } else {
+                setLeftButton(image: UIImage(named: "navbar_back_black")!)
+            }
+        } else {
+            setLeftButton(image: UIImage(named: "navbar_back_black")!)
+        }
+    }
+    
+    private func setLeftButton(image:UIImage) {
+        customNavigationBar.wr_setLeftButton(image: image)
+        customNavigationBar.onClickLeftButton = {
+            [weak self] in
+            self?.wr_toLastViewController(animated: true)
+        }
+    }
+    
+    lazy var nextButton: UIButton = {
+        let nextButton = UIButton(type: .custom)
+        nextButton.titleLabel?.font = .systemFont(ofSize: 14.0)
+        nextButton.setTitle("完成", for: .normal)
+        nextButton.setTitleColor(kThemeColor, for: .normal)
+        nextButton.setTitleColor(k999999Color, for: .disabled)
+        nextButton.isEnabled  = false
+        return nextButton
+    }()
+    
+    override var selectedAssets: [TLPHAsset] {
+        didSet {
+            if selectedAssets.count > 0 {
+                nextButton.setTitle("完成(\( selectedAssets.count)/\(maxSelectedAssets ?? 0))", for: .normal)
+                nextButton.isEnabled = true
+            }else {
+                nextButton.setTitle("完成", for: .normal)
+                nextButton.isEnabled = false
+            }
+        }
+    }
+
+}
+
+extension CustomTLPhotoPickerViewController : TLPhotosPickerViewControllerDelegate {
+    
+    func deninedAuthoization() {
+        
+    }
+    func dismissPhotoPicker(withPHAssets: [PHAsset]) {
+        
+    }
+    public func dismissPhotoPicker(withTLPHAssets: [TLPHAsset]) {
+        
+    }
+    public func dismissComplete() {
+        
+    }
+    public func photoPickerDidCancel() {
+        
+    }
+    public func canSelectAsset(phAsset: PHAsset) -> Bool {
+//        Mediator.push(BrowsePictureRouterModuleType.pushBrowsePicturePHAssets(phAssets: [phAsset], index: 0))
+        return true
+    }
+    public func didExceedMaximumNumberOfSelection(picker: TLPhotosPickerViewController) {
+        SwiftProgressHUD.shared().showText("最多选中\(maxSelectedAssets ?? 0)张")
+    }
+    
+    public func handleNoAlbumPermissions(picker: TLPhotosPickerViewController) {
+        NXLPermission.openSettingsURL()
+    }
+    
+    public func handleNoCameraPermissions(picker: TLPhotosPickerViewController) {
+        NXLPermission.openSettingsURL()
+    }
+}
+
+extension CustomTLPhotoPickerViewController: TLPhotosPickerLogDelegate {
+    //For Log User Interaction
+    func selectedCameraCell(picker: TLPhotosPickerViewController) {
+        if picker.selectedAssets.count == maxSelectedAssets {
+            SwiftProgressHUD.shared().showText("最多选中\(maxSelectedAssets ?? 0)张,不可以拍照哦")
+        }
+        print("selectedCameraCell")
+    }
+    
+    func selectedPhoto(picker: TLPhotosPickerViewController, at: Int) {
+        print("selectedPhoto")
+    }
+    
+    func deselectedPhoto(picker: TLPhotosPickerViewController, at: Int) {
+        print("deselectedPhoto")
+    }
+    
+    func selectedAlbum(picker: TLPhotosPickerViewController, title: String, at: Int) {
+        print("selectedAlbum")
+    }
+}

BIN
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/arrow.png


BIN
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/camera@3x.png


BIN
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/insertPhotoMaterial@3x.png


BIN
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/pop_arrow.png


BIN
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotoPicker/TLPhotoPickerController.bundle/video.png


+ 17 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotopickerDataSourcesProtocol.swift

@@ -0,0 +1,17 @@
+//
+//  TLPhotopickerDataSourcesProtocol.swift
+//  TLPhotoPicker
+//
+//  Created by wade.hawk on 21/01/2019.
+//
+
+import Foundation
+import Photos
+
+public protocol TLPhotopickerDataSourcesProtocol {
+    func headerReferenceSize() -> CGSize
+    func footerReferenceSize() -> CGSize
+    func registerSupplementView(collectionView: UICollectionView)
+    func supplementIdentifier(kind: String) -> String
+    func configure(supplement view: UICollectionReusableView, section: (title: String, assets: [TLPHAsset]))
+}

File diff suppressed because it is too large
+ 1154 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotosPickerViewController.swift


+ 221 - 0
RainbowPlanet/RainbowPlanet/Tools/TLPhotoPicker/TLPhotosPickerViewController.xib

@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TLPhotosPickerViewController" customModule="RainbowPlanet" customModuleProvider="target">
+            <connections>
+                <outlet property="albumPopView" destination="Jcn-hC-Umh" id="c3n-m9-wfd"/>
+                <outlet property="cancelButton" destination="sqJ-Z7-zxj" id="J6u-hz-ePK"/>
+                <outlet property="collectionView" destination="4gR-Bn-quP" id="ZOF-qU-cpd"/>
+                <outlet property="customNavItem" destination="5CU-MZ-p1K" id="ih7-d3-nco"/>
+                <outlet property="doneButton" destination="daA-Ag-vVv" id="P53-fy-Sbh"/>
+                <outlet property="emptyImageView" destination="YDZ-o1-AXT" id="TVN-0v-aQc"/>
+                <outlet property="emptyMessageLabel" destination="7qj-q4-rHC" id="Bcp-Hu-lEY"/>
+                <outlet property="emptyView" destination="HPm-Vc-F86" id="4FT-XL-9ql"/>
+                <outlet property="indicator" destination="AEv-G6-dRI" id="coA-3n-07e"/>
+                <outlet property="navigationBar" destination="X8O-Gg-slz" id="Sp9-g1-r4y"/>
+                <outlet property="navigationBarTopConstraint" destination="IKp-hS-tTy" id="MFH-eP-0tb"/>
+                <outlet property="popArrowImageView" destination="5zn-je-qLx" id="6k9-cH-vcU"/>
+                <outlet property="subTitleArrowImageView" destination="b7w-7R-rco" id="IjY-7S-Zz1"/>
+                <outlet property="subTitleLabel" destination="DON-iU-Cox" id="ZyB-O9-EcR"/>
+                <outlet property="subTitleStackView" destination="kgt-Cn-AXg" id="Vcc-hP-iqe"/>
+                <outlet property="titleLabel" destination="xuG-bc-Oq9" id="2o0-aN-au6"/>
+                <outlet property="titleView" destination="VAz-Py-dsa" id="deY-US-9Jh"/>
+                <outlet property="view" destination="Zyk-dI-msE" id="dxK-gh-unF"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="Zyk-dI-msE">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <navigationBar contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X8O-Gg-slz">
+                    <rect key="frame" x="0.0" y="20" width="375" height="44"/>
+                    <items>
+                        <navigationItem id="5CU-MZ-p1K">
+                            <nil key="title"/>
+                            <barButtonItem key="leftBarButtonItem" title="Cancel" id="sqJ-Z7-zxj">
+                                <connections>
+                                    <action selector="cancelButtonTap" destination="-1" id="dm0-Ur-I5r"/>
+                                </connections>
+                            </barButtonItem>
+                            <view key="titleView" contentMode="scaleToFill" id="VAz-Py-dsa">
+                                <rect key="frame" x="87.5" y="0.0" width="200" height="44"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <subviews>
+                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Ql8-7f-9Uk">
+                                        <rect key="frame" x="0.0" y="0.0" width="200" height="44"/>
+                                        <subviews>
+                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xuG-bc-Oq9">
+                                                <rect key="frame" x="79.5" y="0.0" width="41.5" height="19.5"/>
+                                                <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="16"/>
+                                                <nil key="textColor"/>
+                                                <nil key="highlightedColor"/>
+                                            </label>
+                                            <stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="kgt-Cn-AXg">
+                                                <rect key="frame" x="78" y="19.5" width="44.5" height="24.5"/>
+                                                <subviews>
+                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="DON-iU-Cox">
+                                                        <rect key="frame" x="0.0" y="5.5" width="29.5" height="13.5"/>
+                                                        <fontDescription key="fontDescription" type="system" weight="medium" pointSize="11"/>
+                                                        <nil key="textColor"/>
+                                                        <nil key="highlightedColor"/>
+                                                    </label>
+                                                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="b7w-7R-rco">
+                                                        <rect key="frame" x="34.5" y="7.5" width="10" height="10"/>
+                                                        <constraints>
+                                                            <constraint firstAttribute="width" priority="999" constant="10" id="7bM-Da-Q8A"/>
+                                                            <constraint firstAttribute="height" priority="999" constant="10" id="OHz-gp-CQp"/>
+                                                        </constraints>
+                                                    </imageView>
+                                                </subviews>
+                                            </stackView>
+                                        </subviews>
+                                    </stackView>
+                                </subviews>
+                                <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                                <constraints>
+                                    <constraint firstAttribute="trailing" secondItem="Ql8-7f-9Uk" secondAttribute="trailing" id="Vv5-au-j7y"/>
+                                    <constraint firstAttribute="bottom" secondItem="Ql8-7f-9Uk" secondAttribute="bottom" id="XNk-yU-iZJ"/>
+                                    <constraint firstItem="Ql8-7f-9Uk" firstAttribute="top" secondItem="VAz-Py-dsa" secondAttribute="top" id="h9o-f1-oRa"/>
+                                    <constraint firstItem="Ql8-7f-9Uk" firstAttribute="leading" secondItem="VAz-Py-dsa" secondAttribute="leading" id="j8K-5o-bAh"/>
+                                </constraints>
+                            </view>
+                            <barButtonItem key="rightBarButtonItem" title="Done" id="daA-Ag-vVv">
+                                <connections>
+                                    <action selector="doneButtonTap" destination="-1" id="BzF-rn-rn4"/>
+                                </connections>
+                            </barButtonItem>
+                        </navigationItem>
+                    </items>
+                </navigationBar>
+                <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="4gR-Bn-quP">
+                    <rect key="frame" x="0.0" y="64" width="375" height="603"/>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="1" minimumInteritemSpacing="1" id="VDa-Pp-VBq">
+                        <size key="itemSize" width="50" height="50"/>
+                        <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                        <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                        <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                    </collectionViewFlowLayout>
+                    <connections>
+                        <outlet property="dataSource" destination="-1" id="6fE-SF-z6F"/>
+                        <outlet property="delegate" destination="-1" id="pjZ-sM-fyY"/>
+                    </connections>
+                </collectionView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HPm-Vc-F86">
+                    <rect key="frame" x="0.0" y="64" width="375" height="603"/>
+                    <subviews>
+                        <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="gi6-Sw-3Zf">
+                            <rect key="frame" x="137.5" y="239" width="100" height="125.5"/>
+                            <subviews>
+                                <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YDZ-o1-AXT">
+                                    <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
+                                    <constraints>
+                                        <constraint firstAttribute="width" priority="999" constant="100" id="VUj-VB-7Kr"/>
+                                        <constraint firstAttribute="height" priority="999" constant="100" id="jBo-WN-gZr"/>
+                                    </constraints>
+                                </imageView>
+                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7qj-q4-rHC">
+                                    <rect key="frame" x="29" y="105" width="42" height="20.5"/>
+                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                    <nil key="textColor"/>
+                                    <nil key="highlightedColor"/>
+                                </label>
+                            </subviews>
+                        </stackView>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstItem="gi6-Sw-3Zf" firstAttribute="centerX" secondItem="HPm-Vc-F86" secondAttribute="centerX" id="FpA-96-esN"/>
+                        <constraint firstItem="gi6-Sw-3Zf" firstAttribute="centerY" secondItem="HPm-Vc-F86" secondAttribute="centerY" id="L0c-BG-65l"/>
+                    </constraints>
+                </view>
+                <activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="AEv-G6-dRI">
+                    <rect key="frame" x="177.5" y="323.5" width="20" height="20"/>
+                </activityIndicatorView>
+                <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Jcn-hC-Umh" customClass="TLAlbumPopView" customModule="RainbowPlanet" customModuleProvider="target">
+                    <rect key="frame" x="0.0" y="64" width="375" height="603"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GKD-We-AdT">
+                            <rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
+                            <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.29999999999999999" colorSpace="calibratedRGB"/>
+                        </view>
+                        <view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eL2-gJ-b87">
+                            <rect key="frame" x="1" y="17" width="373" height="130"/>
+                            <subviews>
+                                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" style="plain" separatorStyle="default" rowHeight="75" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="grw-Nk-Sxr">
+                                    <rect key="frame" x="0.0" y="0.0" width="373" height="130"/>
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                </tableView>
+                            </subviews>
+                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            <constraints>
+                                <constraint firstItem="grw-Nk-Sxr" firstAttribute="top" secondItem="eL2-gJ-b87" secondAttribute="top" id="DAx-an-6Qt"/>
+                                <constraint firstAttribute="height" constant="130" id="cet-yf-3jU"/>
+                                <constraint firstAttribute="trailing" secondItem="grw-Nk-Sxr" secondAttribute="trailing" id="nM5-IG-MPJ"/>
+                                <constraint firstItem="grw-Nk-Sxr" firstAttribute="leading" secondItem="eL2-gJ-b87" secondAttribute="leading" id="o4b-cS-L3T"/>
+                                <constraint firstAttribute="bottom" secondItem="grw-Nk-Sxr" secondAttribute="bottom" id="sPK-1G-qLo"/>
+                            </constraints>
+                        </view>
+                        <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5zn-je-qLx">
+                            <rect key="frame" x="180.5" y="10" width="14" height="7"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="7" id="eZN-nB-EHu"/>
+                                <constraint firstAttribute="width" constant="14" id="hWo-ji-iBX"/>
+                            </constraints>
+                        </imageView>
+                    </subviews>
+                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="GKD-We-AdT" firstAttribute="top" secondItem="Jcn-hC-Umh" secondAttribute="top" id="83h-bg-tAJ"/>
+                        <constraint firstAttribute="trailing" secondItem="eL2-gJ-b87" secondAttribute="trailing" constant="1" id="IMh-Wh-897"/>
+                        <constraint firstItem="5zn-je-qLx" firstAttribute="centerX" secondItem="Jcn-hC-Umh" secondAttribute="centerX" id="LpE-mT-MxA"/>
+                        <constraint firstItem="eL2-gJ-b87" firstAttribute="top" secondItem="Jcn-hC-Umh" secondAttribute="top" constant="17" id="XtI-9R-CFm"/>
+                        <constraint firstItem="GKD-We-AdT" firstAttribute="leading" secondItem="Jcn-hC-Umh" secondAttribute="leading" id="krq-3L-sxU"/>
+                        <constraint firstItem="eL2-gJ-b87" firstAttribute="top" secondItem="5zn-je-qLx" secondAttribute="bottom" id="tM4-Tb-JsI"/>
+                        <constraint firstItem="eL2-gJ-b87" firstAttribute="leading" secondItem="Jcn-hC-Umh" secondAttribute="leading" constant="1" id="v5O-lt-eKe"/>
+                        <constraint firstAttribute="bottom" secondItem="GKD-We-AdT" secondAttribute="bottom" id="x4Q-bm-MrY"/>
+                        <constraint firstAttribute="trailing" secondItem="GKD-We-AdT" secondAttribute="trailing" id="xdF-2b-pOS"/>
+                    </constraints>
+                    <connections>
+                        <outlet property="bgView" destination="GKD-We-AdT" id="ymr-tp-YBW"/>
+                        <outlet property="popupView" destination="eL2-gJ-b87" id="xgf-xj-Rd4"/>
+                        <outlet property="popupViewHeight" destination="cet-yf-3jU" id="OS3-MZ-s9c"/>
+                        <outlet property="tableView" destination="grw-Nk-Sxr" id="Y3k-8m-iJp"/>
+                    </connections>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="HPm-Vc-F86" firstAttribute="bottom" secondItem="HLR-WT-D3I" secondAttribute="bottom" id="0AS-35-SWm"/>
+                <constraint firstItem="AEv-G6-dRI" firstAttribute="centerX" secondItem="Zyk-dI-msE" secondAttribute="centerX" id="44C-fd-MVc"/>
+                <constraint firstItem="X8O-Gg-slz" firstAttribute="leading" secondItem="Zyk-dI-msE" secondAttribute="leading" id="4Ud-fp-qD2"/>
+                <constraint firstItem="4gR-Bn-quP" firstAttribute="top" secondItem="X8O-Gg-slz" secondAttribute="bottom" id="CZx-xl-GLa"/>
+                <constraint firstItem="X8O-Gg-slz" firstAttribute="top" secondItem="HLR-WT-D3I" secondAttribute="top" id="IKp-hS-tTy"/>
+                <constraint firstItem="Jcn-hC-Umh" firstAttribute="top" secondItem="X8O-Gg-slz" secondAttribute="bottom" id="KdD-nT-6tf"/>
+                <constraint firstItem="HPm-Vc-F86" firstAttribute="leading" secondItem="HLR-WT-D3I" secondAttribute="leading" id="M3L-CU-HdQ"/>
+                <constraint firstAttribute="trailing" secondItem="4gR-Bn-quP" secondAttribute="trailing" id="M6b-2G-2m1"/>
+                <constraint firstAttribute="trailing" secondItem="Jcn-hC-Umh" secondAttribute="trailing" id="Njh-ZO-lnq"/>
+                <constraint firstAttribute="bottom" secondItem="Jcn-hC-Umh" secondAttribute="bottom" id="NxH-d8-b65"/>
+                <constraint firstItem="HPm-Vc-F86" firstAttribute="top" secondItem="X8O-Gg-slz" secondAttribute="bottom" id="YyG-QW-0ZP"/>
+                <constraint firstItem="AEv-G6-dRI" firstAttribute="centerY" secondItem="Zyk-dI-msE" secondAttribute="centerY" id="aLU-u9-ALA"/>
+                <constraint firstItem="Jcn-hC-Umh" firstAttribute="leading" secondItem="Zyk-dI-msE" secondAttribute="leading" id="aY7-Ml-cd3"/>
+                <constraint firstItem="HPm-Vc-F86" firstAttribute="trailing" secondItem="HLR-WT-D3I" secondAttribute="trailing" id="aoA-8G-xDA"/>
+                <constraint firstItem="4gR-Bn-quP" firstAttribute="leading" secondItem="Zyk-dI-msE" secondAttribute="leading" id="kb7-vy-yTu"/>
+                <constraint firstItem="4gR-Bn-quP" firstAttribute="bottom" secondItem="HLR-WT-D3I" secondAttribute="bottom" id="qqE-w2-Tsc"/>
+                <constraint firstAttribute="trailing" secondItem="X8O-Gg-slz" secondAttribute="trailing" id="yWV-L2-0f4"/>
+            </constraints>
+            <viewLayoutGuide key="safeArea" id="HLR-WT-D3I"/>
+            <point key="canvasLocation" x="33.5" y="53.5"/>
+        </view>
+    </objects>
+</document>