From 87d26c586bfd5f4c8d426e588d2ca4d7105422d3 Mon Sep 17 00:00:00 2001 From: haranicle Date: Fri, 15 Apr 2016 17:31:31 +0900 Subject: [PATCH 1/3] add project --- .gitignore | 65 ++ NohanaImagePicker.xcodeproj/project.pbxproj | 736 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcschemes/NohanaImagePicker.xcscheme | 99 +++ .../NohanaImagePickerSample.xcscheme | 101 +++ NohanaImagePicker/ActivityIndicatable.swift | 24 + NohanaImagePicker/AlbumCell.swift | 14 + .../AlbumListEmptyIndicator.swift | 43 + .../AlbumListViewController.swift | 253 ++++++ .../AnimatableNavigationController.swift | 58 ++ NohanaImagePicker/AssetCell.swift | 55 ++ NohanaImagePicker/AssetDetailCell.swift | 66 ++ .../AssetDetailListViewController.swift | 133 ++++ .../AssetListViewController.swift | 141 ++++ NohanaImagePicker/ColorConfig.swift | 16 + .../ContractingAnimationController.swift | 90 +++ NohanaImagePicker/EmptyIndicatable.swift | 24 + .../ExpandingAnimationController.swift | 75 ++ NohanaImagePicker/ImageName.swift | 22 + .../Images.xcassets/Contents.json | 6 + .../btn_select_l.imageset/Contents.json | 22 + .../btn_select_l.imageset/btn_select_l@2x.png | Bin 0 -> 1777 bytes .../btn_select_l.imageset/btn_select_l@3x.png | Bin 0 -> 1467 bytes .../btn_select_m.imageset/Contents.json | 22 + .../btn_select_m.imageset/btn_select_m@2x.png | Bin 0 -> 1061 bytes .../btn_select_m.imageset/btn_select_m@3x.png | Bin 0 -> 1473 bytes .../btn_selected_l.imageset/Contents.json | 22 + .../btn_selected_l@2x.png | Bin 0 -> 1913 bytes .../btn_selected_l@3x.png | Bin 0 -> 1686 bytes .../btn_selected_m.imageset/Contents.json | 22 + .../btn_selected_m@2x.png | Bin 0 -> 1171 bytes .../btn_selected_m@3x.png | Bin 0 -> 1589 bytes .../Contents.json | 22 + .../icon_photosize_alert_s@2x.png | Bin 0 -> 1518 bytes .../icon_photosize_alert_s@3x.png | Bin 0 -> 3878 bytes .../Contents.json | 22 + .../icon_photosize_low_s@2x.png | Bin 0 -> 2419 bytes .../icon_photosize_low_s@3x.png | Bin 0 -> 2917 bytes NohanaImagePicker/Info.plist | 26 + NohanaImagePicker/ItemListType.swift | 24 + NohanaImagePicker/MomentCell.swift | 20 + .../MomentSectionHeaderView.swift | 15 + NohanaImagePicker/MomentViewController.swift | 143 ++++ NohanaImagePicker/NohanaImagePicker.h | 18 + .../NohanaImagePicker.storyboard | 442 +++++++++++ .../NohanaImagePickerController.swift | 121 +++ NohanaImagePicker/NotificationInfo.swift | 20 + NohanaImagePicker/PhotoKitAlbumList.swift | 98 +++ NohanaImagePicker/PhotoKitAsset.swift | 50 ++ NohanaImagePicker/PhotoKitAssetList.swift | 79 ++ NohanaImagePicker/PickedAssetList.swift | 124 +++ NohanaImagePicker/Size.swift | 40 + .../SwipeInteractionController.swift | 25 + .../en.lproj/NohanaImagePicker.strings | 14 + .../ja.lproj/NohanaImagePicker.strings | 14 + NohanaImagePickerSample/AppDelegate.swift | 46 ++ .../AppIcon.appiconset/Contents.json | 73 ++ .../Base.lproj/LaunchScreen.storyboard | 27 + .../Base.lproj/Main.storyboard | 26 + NohanaImagePickerSample/Info.plist | 47 ++ NohanaImagePickerSample/ViewController.swift | 85 ++ NohanaImagePickerSampleTests/Info.plist | 24 + .../NohanaImagePickerSampleTests.swift | 36 + 63 files changed, 3797 insertions(+) create mode 100644 .gitignore create mode 100644 NohanaImagePicker.xcodeproj/project.pbxproj create mode 100644 NohanaImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePicker.xcscheme create mode 100644 NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePickerSample.xcscheme create mode 100644 NohanaImagePicker/ActivityIndicatable.swift create mode 100644 NohanaImagePicker/AlbumCell.swift create mode 100644 NohanaImagePicker/AlbumListEmptyIndicator.swift create mode 100644 NohanaImagePicker/AlbumListViewController.swift create mode 100644 NohanaImagePicker/AnimatableNavigationController.swift create mode 100644 NohanaImagePicker/AssetCell.swift create mode 100644 NohanaImagePicker/AssetDetailCell.swift create mode 100644 NohanaImagePicker/AssetDetailListViewController.swift create mode 100644 NohanaImagePicker/AssetListViewController.swift create mode 100644 NohanaImagePicker/ColorConfig.swift create mode 100644 NohanaImagePicker/ContractingAnimationController.swift create mode 100644 NohanaImagePicker/EmptyIndicatable.swift create mode 100644 NohanaImagePicker/ExpandingAnimationController.swift create mode 100644 NohanaImagePicker/ImageName.swift create mode 100644 NohanaImagePicker/Images.xcassets/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_l.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_l.imageset/btn_select_l@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_l.imageset/btn_select_l@3x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_m.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@3x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@3x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/btn_selected_m@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/btn_selected_m@3x.png create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_alert_s.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_alert_s.imageset/icon_photosize_alert_s@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_alert_s.imageset/icon_photosize_alert_s@3x.png create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_low_s.imageset/Contents.json create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_low_s.imageset/icon_photosize_low_s@2x.png create mode 100644 NohanaImagePicker/Images.xcassets/icon_photosize_low_s.imageset/icon_photosize_low_s@3x.png create mode 100644 NohanaImagePicker/Info.plist create mode 100644 NohanaImagePicker/ItemListType.swift create mode 100644 NohanaImagePicker/MomentCell.swift create mode 100644 NohanaImagePicker/MomentSectionHeaderView.swift create mode 100644 NohanaImagePicker/MomentViewController.swift create mode 100644 NohanaImagePicker/NohanaImagePicker.h create mode 100644 NohanaImagePicker/NohanaImagePicker.storyboard create mode 100644 NohanaImagePicker/NohanaImagePickerController.swift create mode 100644 NohanaImagePicker/NotificationInfo.swift create mode 100644 NohanaImagePicker/PhotoKitAlbumList.swift create mode 100644 NohanaImagePicker/PhotoKitAsset.swift create mode 100644 NohanaImagePicker/PhotoKitAssetList.swift create mode 100644 NohanaImagePicker/PickedAssetList.swift create mode 100644 NohanaImagePicker/Size.swift create mode 100644 NohanaImagePicker/SwipeInteractionController.swift create mode 100644 NohanaImagePicker/en.lproj/NohanaImagePicker.strings create mode 100644 NohanaImagePicker/ja.lproj/NohanaImagePicker.strings create mode 100644 NohanaImagePickerSample/AppDelegate.swift create mode 100644 NohanaImagePickerSample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 NohanaImagePickerSample/Base.lproj/LaunchScreen.storyboard create mode 100644 NohanaImagePickerSample/Base.lproj/Main.storyboard create mode 100644 NohanaImagePickerSample/Info.plist create mode 100644 NohanaImagePickerSample/ViewController.swift create mode 100644 NohanaImagePickerSampleTests/Info.plist create mode 100644 NohanaImagePickerSampleTests/NohanaImagePickerSampleTests.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95f01fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +.DS_Store + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md + +fastlane/report.xml +fastlane/screenshots diff --git a/NohanaImagePicker.xcodeproj/project.pbxproj b/NohanaImagePicker.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e4ad533 --- /dev/null +++ b/NohanaImagePicker.xcodeproj/project.pbxproj @@ -0,0 +1,736 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + F202573A1C7343D20069B33A /* ImageName.swift in Sources */ = {isa = PBXBuildFile; fileRef = F20257391C7343D20069B33A /* ImageName.swift */; }; + F202573C1C7418920069B33A /* PickedAssetList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F202573B1C7418920069B33A /* PickedAssetList.swift */; }; + F2131F431C79615700797887 /* SwipeInteractionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2131F421C79615700797887 /* SwipeInteractionController.swift */; }; + F218D7D61C6B3D22001FCED1 /* PhotoKitAlbumList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F218D7D51C6B3D22001FCED1 /* PhotoKitAlbumList.swift */; }; + F218D7DE1C6C3A5B001FCED1 /* AlbumCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F218D7DD1C6C3A5B001FCED1 /* AlbumCell.swift */; }; + F23554261C69D19C00796DCA /* NohanaImagePicker.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F23554251C69D19C00796DCA /* NohanaImagePicker.storyboard */; }; + F23554281C69D5DB00796DCA /* NohanaImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F23554271C69D5DB00796DCA /* NohanaImagePickerController.swift */; }; + F237249B1C6DCF96005D1E8A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F237249A1C6DCF96005D1E8A /* Images.xcassets */; }; + F23CC89E1CB745C800BCE443 /* ActivityIndicatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F23CC89D1CB745C800BCE443 /* ActivityIndicatable.swift */; }; + F24EB6901C68AEED0002EC86 /* AlbumListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F24EB68F1C68AEED0002EC86 /* AlbumListViewController.swift */; }; + F24EB6911C68AFB40002EC86 /* NohanaImagePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2C08D741C68651900B00181 /* NohanaImagePicker.framework */; }; + F24EB6921C68AFB40002EC86 /* NohanaImagePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F2C08D741C68651900B00181 /* NohanaImagePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + F25C10C21C8ED9BF007453C3 /* MomentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F25C10C11C8ED9BF007453C3 /* MomentViewController.swift */; }; + F25C69881CA23A0A005935D6 /* MomentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F25C69871CA23A0A005935D6 /* MomentCell.swift */; }; + F25C69901CA27311005935D6 /* EmptyIndicatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F25C698F1CA27311005935D6 /* EmptyIndicatable.swift */; }; + F25C69921CA28728005935D6 /* AlbumListEmptyIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F25C69911CA28728005935D6 /* AlbumListEmptyIndicator.swift */; }; + F25C699B1CA2B79A005935D6 /* ColorConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = F25C699A1CA2B79A005935D6 /* ColorConfig.swift */; }; + F26775DE1C701FA7002E786C /* ItemListType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26775DD1C701FA7002E786C /* ItemListType.swift */; }; + F26775E11C7046C7002E786C /* ExpandingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26775E01C7046C7002E786C /* ExpandingAnimationController.swift */; }; + F26775E51C70574F002E786C /* AnimatableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26775E41C70574F002E786C /* AnimatableNavigationController.swift */; }; + F26775E81C7073CD002E786C /* Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26775E71C7073CD002E786C /* Size.swift */; }; + F26775EA1C71645A002E786C /* ContractingAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F26775E91C71645A002E786C /* ContractingAnimationController.swift */; }; + F27029CB1C71C43A001647AB /* NohanaImagePicker.strings in Resources */ = {isa = PBXBuildFile; fileRef = F27029CD1C71C43A001647AB /* NohanaImagePicker.strings */; }; + F28F4AC31C6C49EE00B7D725 /* PhotoKitAssetList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28F4AC21C6C49EE00B7D725 /* PhotoKitAssetList.swift */; }; + F28F4AC51C6C59A500B7D725 /* PhotoKitAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28F4AC41C6C59A500B7D725 /* PhotoKitAsset.swift */; }; + F2C08D781C68651900B00181 /* NohanaImagePicker.h in Headers */ = {isa = PBXBuildFile; fileRef = F2C08D771C68651900B00181 /* NohanaImagePicker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F2C08D951C688FC400B00181 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C08D941C688FC400B00181 /* AppDelegate.swift */; }; + F2C08D971C688FC400B00181 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C08D961C688FC400B00181 /* ViewController.swift */; }; + F2C08D9A1C688FC400B00181 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F2C08D981C688FC400B00181 /* Main.storyboard */; }; + F2C08D9C1C688FC400B00181 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F2C08D9B1C688FC400B00181 /* Assets.xcassets */; }; + F2C08D9F1C688FC400B00181 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F2C08D9D1C688FC400B00181 /* LaunchScreen.storyboard */; }; + F2C08DAA1C688FC400B00181 /* NohanaImagePickerSampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C08DA91C688FC400B00181 /* NohanaImagePickerSampleTests.swift */; }; + F2DA29771C7749D600B0A8E3 /* NotificationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DA29761C7749D600B0A8E3 /* NotificationInfo.swift */; }; + F2DF3B151C6C76E500C1C0E4 /* AssetListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF3B141C6C76E500C1C0E4 /* AssetListViewController.swift */; }; + F2DF3B171C6C868E00C1C0E4 /* AssetCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF3B161C6C868E00C1C0E4 /* AssetCell.swift */; }; + F2DF3B2B1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF3B2A1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift */; }; + F2DF3B2D1C6D780100C1C0E4 /* AssetDetailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2DF3B2C1C6D780100C1C0E4 /* AssetDetailCell.swift */; }; + F2FE1F781C901D9400FDBE7B /* MomentSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2FE1F761C901D9400FDBE7B /* MomentSectionHeaderView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F24EB6931C68AFB40002EC86 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F2C08D6B1C68651900B00181 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2C08D731C68651900B00181; + remoteInfo = NohanaImagePicker; + }; + F2C08DA61C688FC400B00181 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F2C08D6B1C68651900B00181 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F2C08D911C688FC400B00181; + remoteInfo = NohanaImagePickerSample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F24EB6951C68AFB40002EC86 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + F24EB6921C68AFB40002EC86 /* NohanaImagePicker.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + F20257391C7343D20069B33A /* ImageName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageName.swift; sourceTree = ""; }; + F202573B1C7418920069B33A /* PickedAssetList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickedAssetList.swift; sourceTree = ""; }; + F2131F421C79615700797887 /* SwipeInteractionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwipeInteractionController.swift; sourceTree = ""; }; + F218D7D51C6B3D22001FCED1 /* PhotoKitAlbumList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAlbumList.swift; sourceTree = ""; }; + F218D7DD1C6C3A5B001FCED1 /* AlbumCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumCell.swift; sourceTree = ""; }; + F23554251C69D19C00796DCA /* NohanaImagePicker.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NohanaImagePicker.storyboard; sourceTree = ""; }; + F23554271C69D5DB00796DCA /* NohanaImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NohanaImagePickerController.swift; sourceTree = ""; }; + F237249A1C6DCF96005D1E8A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + F23CC89D1CB745C800BCE443 /* ActivityIndicatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatable.swift; sourceTree = ""; }; + F24EB68F1C68AEED0002EC86 /* AlbumListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumListViewController.swift; sourceTree = ""; }; + F25C10C11C8ED9BF007453C3 /* MomentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentViewController.swift; sourceTree = ""; }; + F25C69871CA23A0A005935D6 /* MomentCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentCell.swift; sourceTree = ""; }; + F25C698F1CA27311005935D6 /* EmptyIndicatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyIndicatable.swift; sourceTree = ""; }; + F25C69911CA28728005935D6 /* AlbumListEmptyIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumListEmptyIndicator.swift; sourceTree = ""; }; + F25C699A1CA2B79A005935D6 /* ColorConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorConfig.swift; sourceTree = ""; }; + F26775DD1C701FA7002E786C /* ItemListType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListType.swift; sourceTree = ""; }; + F26775E01C7046C7002E786C /* ExpandingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpandingAnimationController.swift; sourceTree = ""; }; + F26775E41C70574F002E786C /* AnimatableNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatableNavigationController.swift; sourceTree = ""; }; + F26775E71C7073CD002E786C /* Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Size.swift; sourceTree = ""; }; + F26775E91C71645A002E786C /* ContractingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContractingAnimationController.swift; sourceTree = ""; }; + F27029CC1C71C43A001647AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/NohanaImagePicker.strings; sourceTree = ""; }; + F27029CF1C71C4FE001647AB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/NohanaImagePicker.strings; sourceTree = ""; }; + F28F4AC21C6C49EE00B7D725 /* PhotoKitAssetList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAssetList.swift; sourceTree = ""; }; + F28F4AC41C6C59A500B7D725 /* PhotoKitAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAsset.swift; sourceTree = ""; }; + F2C08D741C68651900B00181 /* NohanaImagePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NohanaImagePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F2C08D771C68651900B00181 /* NohanaImagePicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NohanaImagePicker.h; sourceTree = ""; }; + F2C08D791C68651900B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F2C08D921C688FC400B00181 /* NohanaImagePickerSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NohanaImagePickerSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F2C08D941C688FC400B00181 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + F2C08D961C688FC400B00181 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + F2C08D991C688FC400B00181 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F2C08D9B1C688FC400B00181 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + F2C08D9E1C688FC400B00181 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + F2C08DA01C688FC400B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F2C08DA51C688FC400B00181 /* NohanaImagePickerSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NohanaImagePickerSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + F2C08DA91C688FC400B00181 /* NohanaImagePickerSampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NohanaImagePickerSampleTests.swift; sourceTree = ""; }; + F2C08DAB1C688FC400B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F2DA29761C7749D600B0A8E3 /* NotificationInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationInfo.swift; sourceTree = ""; }; + F2DF3B141C6C76E500C1C0E4 /* AssetListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetListViewController.swift; sourceTree = ""; }; + F2DF3B161C6C868E00C1C0E4 /* AssetCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetCell.swift; sourceTree = ""; }; + F2DF3B2A1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDetailListViewController.swift; sourceTree = ""; }; + F2DF3B2C1C6D780100C1C0E4 /* AssetDetailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDetailCell.swift; sourceTree = ""; }; + F2FE1F761C901D9400FDBE7B /* MomentSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentSectionHeaderView.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F2C08D701C68651900B00181 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08D8F1C688FC400B00181 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F24EB6911C68AFB40002EC86 /* NohanaImagePicker.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08DA21C688FC400B00181 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F218D7DC1C6C3A3E001FCED1 /* Views */ = { + isa = PBXGroup; + children = ( + F218D7DD1C6C3A5B001FCED1 /* AlbumCell.swift */, + F2DF3B161C6C868E00C1C0E4 /* AssetCell.swift */, + F2DF3B2C1C6D780100C1C0E4 /* AssetDetailCell.swift */, + F25C69871CA23A0A005935D6 /* MomentCell.swift */, + F2FE1F761C901D9400FDBE7B /* MomentSectionHeaderView.swift */, + F25C69911CA28728005935D6 /* AlbumListEmptyIndicator.swift */, + ); + name = Views; + sourceTree = ""; + }; + F235541F1C698BE900796DCA /* Resources */ = { + isa = PBXGroup; + children = ( + F23554251C69D19C00796DCA /* NohanaImagePicker.storyboard */, + F27029CD1C71C43A001647AB /* NohanaImagePicker.strings */, + F237249A1C6DCF96005D1E8A /* Images.xcassets */, + ); + name = Resources; + sourceTree = ""; + }; + F23554291C69F38900796DCA /* Photos */ = { + isa = PBXGroup; + children = ( + F218D7D51C6B3D22001FCED1 /* PhotoKitAlbumList.swift */, + F28F4AC21C6C49EE00B7D725 /* PhotoKitAssetList.swift */, + F28F4AC41C6C59A500B7D725 /* PhotoKitAsset.swift */, + ); + name = Photos; + sourceTree = ""; + }; + F26775DF1C704574002E786C /* AnimationController */ = { + isa = PBXGroup; + children = ( + F26775E41C70574F002E786C /* AnimatableNavigationController.swift */, + F26775E01C7046C7002E786C /* ExpandingAnimationController.swift */, + F26775E91C71645A002E786C /* ContractingAnimationController.swift */, + F2131F421C79615700797887 /* SwipeInteractionController.swift */, + ); + name = AnimationController; + sourceTree = ""; + }; + F26775E61C7073B1002E786C /* Common */ = { + isa = PBXGroup; + children = ( + F26775DD1C701FA7002E786C /* ItemListType.swift */, + F202573B1C7418920069B33A /* PickedAssetList.swift */, + F20257391C7343D20069B33A /* ImageName.swift */, + F26775E71C7073CD002E786C /* Size.swift */, + F2DA29761C7749D600B0A8E3 /* NotificationInfo.swift */, + F25C698F1CA27311005935D6 /* EmptyIndicatable.swift */, + F23CC89D1CB745C800BCE443 /* ActivityIndicatable.swift */, + F25C699A1CA2B79A005935D6 /* ColorConfig.swift */, + ); + name = Common; + sourceTree = ""; + }; + F2C08D6A1C68651900B00181 = { + isa = PBXGroup; + children = ( + F2C08D761C68651900B00181 /* NohanaImagePicker */, + F2C08D931C688FC400B00181 /* NohanaImagePickerSample */, + F2C08DA81C688FC400B00181 /* NohanaImagePickerSampleTests */, + F2C08D751C68651900B00181 /* Products */, + ); + sourceTree = ""; + }; + F2C08D751C68651900B00181 /* Products */ = { + isa = PBXGroup; + children = ( + F2C08D741C68651900B00181 /* NohanaImagePicker.framework */, + F2C08D921C688FC400B00181 /* NohanaImagePickerSample.app */, + F2C08DA51C688FC400B00181 /* NohanaImagePickerSampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + F2C08D761C68651900B00181 /* NohanaImagePicker */ = { + isa = PBXGroup; + children = ( + F2C08D771C68651900B00181 /* NohanaImagePicker.h */, + F2C08D791C68651900B00181 /* Info.plist */, + F26775E61C7073B1002E786C /* Common */, + F23554291C69F38900796DCA /* Photos */, + F218D7DC1C6C3A3E001FCED1 /* Views */, + F2E508901C68B1770082EF93 /* ViewControllers */, + F26775DF1C704574002E786C /* AnimationController */, + F235541F1C698BE900796DCA /* Resources */, + ); + path = NohanaImagePicker; + sourceTree = ""; + }; + F2C08D931C688FC400B00181 /* NohanaImagePickerSample */ = { + isa = PBXGroup; + children = ( + F2C08D941C688FC400B00181 /* AppDelegate.swift */, + F2C08D961C688FC400B00181 /* ViewController.swift */, + F2C08D981C688FC400B00181 /* Main.storyboard */, + F2C08D9B1C688FC400B00181 /* Assets.xcassets */, + F2C08D9D1C688FC400B00181 /* LaunchScreen.storyboard */, + F2C08DA01C688FC400B00181 /* Info.plist */, + ); + path = NohanaImagePickerSample; + sourceTree = ""; + }; + F2C08DA81C688FC400B00181 /* NohanaImagePickerSampleTests */ = { + isa = PBXGroup; + children = ( + F2C08DA91C688FC400B00181 /* NohanaImagePickerSampleTests.swift */, + F2C08DAB1C688FC400B00181 /* Info.plist */, + ); + path = NohanaImagePickerSampleTests; + sourceTree = ""; + }; + F2E508901C68B1770082EF93 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + F23554271C69D5DB00796DCA /* NohanaImagePickerController.swift */, + F24EB68F1C68AEED0002EC86 /* AlbumListViewController.swift */, + F2DF3B141C6C76E500C1C0E4 /* AssetListViewController.swift */, + F25C10C11C8ED9BF007453C3 /* MomentViewController.swift */, + F2DF3B2A1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift */, + ); + name = ViewControllers; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + F2C08D711C68651900B00181 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F2C08D781C68651900B00181 /* NohanaImagePicker.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + F2C08D731C68651900B00181 /* NohanaImagePicker */ = { + isa = PBXNativeTarget; + buildConfigurationList = F2C08D881C68651900B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePicker" */; + buildPhases = ( + F2C08D6F1C68651900B00181 /* Sources */, + F2C08D701C68651900B00181 /* Frameworks */, + F2C08D711C68651900B00181 /* Headers */, + F2C08D721C68651900B00181 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NohanaImagePicker; + productName = NohanaImagePicker; + productReference = F2C08D741C68651900B00181 /* NohanaImagePicker.framework */; + productType = "com.apple.product-type.framework"; + }; + F2C08D911C688FC400B00181 /* NohanaImagePickerSample */ = { + isa = PBXNativeTarget; + buildConfigurationList = F2C08DAC1C688FC400B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePickerSample" */; + buildPhases = ( + F2C08D8E1C688FC400B00181 /* Sources */, + F2C08D8F1C688FC400B00181 /* Frameworks */, + F2C08D901C688FC400B00181 /* Resources */, + F24EB6951C68AFB40002EC86 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F24EB6941C68AFB40002EC86 /* PBXTargetDependency */, + ); + name = NohanaImagePickerSample; + productName = NohanaImagePickerSample; + productReference = F2C08D921C688FC400B00181 /* NohanaImagePickerSample.app */; + productType = "com.apple.product-type.application"; + }; + F2C08DA41C688FC400B00181 /* NohanaImagePickerSampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F2C08DAF1C688FC400B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePickerSampleTests" */; + buildPhases = ( + F2C08DA11C688FC400B00181 /* Sources */, + F2C08DA21C688FC400B00181 /* Frameworks */, + F2C08DA31C688FC400B00181 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F2C08DA71C688FC400B00181 /* PBXTargetDependency */, + ); + name = NohanaImagePickerSampleTests; + productName = NohanaImagePickerSampleTests; + productReference = F2C08DA51C688FC400B00181 /* NohanaImagePickerSampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F2C08D6B1C68651900B00181 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = nohana; + TargetAttributes = { + F2C08D731C68651900B00181 = { + CreatedOnToolsVersion = 7.2.1; + }; + F2C08D911C688FC400B00181 = { + CreatedOnToolsVersion = 7.2.1; + }; + F2C08DA41C688FC400B00181 = { + CreatedOnToolsVersion = 7.2.1; + TestTargetID = F2C08D911C688FC400B00181; + }; + }; + }; + buildConfigurationList = F2C08D6E1C68651900B00181 /* Build configuration list for PBXProject "NohanaImagePicker" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ja, + ); + mainGroup = F2C08D6A1C68651900B00181; + productRefGroup = F2C08D751C68651900B00181 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F2C08D731C68651900B00181 /* NohanaImagePicker */, + F2C08D911C688FC400B00181 /* NohanaImagePickerSample */, + F2C08DA41C688FC400B00181 /* NohanaImagePickerSampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F2C08D721C68651900B00181 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F23554261C69D19C00796DCA /* NohanaImagePicker.storyboard in Resources */, + F27029CB1C71C43A001647AB /* NohanaImagePicker.strings in Resources */, + F237249B1C6DCF96005D1E8A /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08D901C688FC400B00181 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F2C08D9F1C688FC400B00181 /* LaunchScreen.storyboard in Resources */, + F2C08D9C1C688FC400B00181 /* Assets.xcassets in Resources */, + F2C08D9A1C688FC400B00181 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08DA31C688FC400B00181 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F2C08D6F1C68651900B00181 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F2DF3B171C6C868E00C1C0E4 /* AssetCell.swift in Sources */, + F26775E81C7073CD002E786C /* Size.swift in Sources */, + F26775DE1C701FA7002E786C /* ItemListType.swift in Sources */, + F23CC89E1CB745C800BCE443 /* ActivityIndicatable.swift in Sources */, + F202573C1C7418920069B33A /* PickedAssetList.swift in Sources */, + F25C10C21C8ED9BF007453C3 /* MomentViewController.swift in Sources */, + F25C69881CA23A0A005935D6 /* MomentCell.swift in Sources */, + F2FE1F781C901D9400FDBE7B /* MomentSectionHeaderView.swift in Sources */, + F202573A1C7343D20069B33A /* ImageName.swift in Sources */, + F23554281C69D5DB00796DCA /* NohanaImagePickerController.swift in Sources */, + F25C69921CA28728005935D6 /* AlbumListEmptyIndicator.swift in Sources */, + F2131F431C79615700797887 /* SwipeInteractionController.swift in Sources */, + F2DF3B2D1C6D780100C1C0E4 /* AssetDetailCell.swift in Sources */, + F25C69901CA27311005935D6 /* EmptyIndicatable.swift in Sources */, + F2DA29771C7749D600B0A8E3 /* NotificationInfo.swift in Sources */, + F218D7DE1C6C3A5B001FCED1 /* AlbumCell.swift in Sources */, + F26775E11C7046C7002E786C /* ExpandingAnimationController.swift in Sources */, + F28F4AC51C6C59A500B7D725 /* PhotoKitAsset.swift in Sources */, + F2DF3B151C6C76E500C1C0E4 /* AssetListViewController.swift in Sources */, + F26775E51C70574F002E786C /* AnimatableNavigationController.swift in Sources */, + F2DF3B2B1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift in Sources */, + F218D7D61C6B3D22001FCED1 /* PhotoKitAlbumList.swift in Sources */, + F24EB6901C68AEED0002EC86 /* AlbumListViewController.swift in Sources */, + F25C699B1CA2B79A005935D6 /* ColorConfig.swift in Sources */, + F28F4AC31C6C49EE00B7D725 /* PhotoKitAssetList.swift in Sources */, + F26775EA1C71645A002E786C /* ContractingAnimationController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08D8E1C688FC400B00181 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F2C08D971C688FC400B00181 /* ViewController.swift in Sources */, + F2C08D951C688FC400B00181 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F2C08DA11C688FC400B00181 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F2C08DAA1C688FC400B00181 /* NohanaImagePickerSampleTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F24EB6941C68AFB40002EC86 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F2C08D731C68651900B00181 /* NohanaImagePicker */; + targetProxy = F24EB6931C68AFB40002EC86 /* PBXContainerItemProxy */; + }; + F2C08DA71C688FC400B00181 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F2C08D911C688FC400B00181 /* NohanaImagePickerSample */; + targetProxy = F2C08DA61C688FC400B00181 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + F27029CD1C71C43A001647AB /* NohanaImagePicker.strings */ = { + isa = PBXVariantGroup; + children = ( + F27029CC1C71C43A001647AB /* en */, + F27029CF1C71C4FE001647AB /* ja */, + ); + name = NohanaImagePicker.strings; + sourceTree = ""; + }; + F2C08D981C688FC400B00181 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F2C08D991C688FC400B00181 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + F2C08D9D1C688FC400B00181 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F2C08D9E1C688FC400B00181 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F2C08D861C68651900B00181 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + F2C08D871C68651900B00181 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + F2C08D891C68651900B00181 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NohanaImagePicker/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + F2C08D8A1C68651900B00181 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = NohanaImagePicker/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePicker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + F2C08DAD1C688FC400B00181 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; + INFOPLIST_FILE = NohanaImagePickerSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePickerSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + F2C08DAE1C688FC400B00181 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; + INFOPLIST_FILE = NohanaImagePickerSample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePickerSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + F2C08DB01C688FC400B00181 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = NohanaImagePickerSampleTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePickerSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NohanaImagePickerSample.app/NohanaImagePickerSample"; + }; + name = Debug; + }; + F2C08DB11C688FC400B00181 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + INFOPLIST_FILE = NohanaImagePickerSampleTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = jp.co.nohana.NohanaImagePickerSample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NohanaImagePickerSample.app/NohanaImagePickerSample"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F2C08D6E1C68651900B00181 /* Build configuration list for PBXProject "NohanaImagePicker" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2C08D861C68651900B00181 /* Debug */, + F2C08D871C68651900B00181 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F2C08D881C68651900B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePicker" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2C08D891C68651900B00181 /* Debug */, + F2C08D8A1C68651900B00181 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F2C08DAC1C688FC400B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePickerSample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2C08DAD1C688FC400B00181 /* Debug */, + F2C08DAE1C688FC400B00181 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F2C08DAF1C688FC400B00181 /* Build configuration list for PBXNativeTarget "NohanaImagePickerSampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F2C08DB01C688FC400B00181 /* Debug */, + F2C08DB11C688FC400B00181 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F2C08D6B1C68651900B00181 /* Project object */; +} diff --git a/NohanaImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/NohanaImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..4e02c73 --- /dev/null +++ b/NohanaImagePicker.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePicker.xcscheme b/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePicker.xcscheme new file mode 100644 index 0000000..1159c40 --- /dev/null +++ b/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePicker.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePickerSample.xcscheme b/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePickerSample.xcscheme new file mode 100644 index 0000000..6086545 --- /dev/null +++ b/NohanaImagePicker.xcodeproj/xcshareddata/xcschemes/NohanaImagePickerSample.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NohanaImagePicker/ActivityIndicatable.swift b/NohanaImagePicker/ActivityIndicatable.swift new file mode 100644 index 0000000..aa8f9f9 --- /dev/null +++ b/NohanaImagePicker/ActivityIndicatable.swift @@ -0,0 +1,24 @@ +// +// ActivityIndicatable.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/04/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +public protocol ActivityIndicatable { + func isProgressing() -> Bool + func updateVisibilityOfActivityIndicator(activityIndicator: UIView) +} + +public extension ActivityIndicatable where Self: UIViewController { + func updateVisibilityOfActivityIndicator(activityIndicator: UIView) { + if isProgressing() { + if !view.subviews.contains(activityIndicator) { + view.addSubview(activityIndicator) + } + } else { + activityIndicator.removeFromSuperview() + } + } +} \ No newline at end of file diff --git a/NohanaImagePicker/AlbumCell.swift b/NohanaImagePicker/AlbumCell.swift new file mode 100644 index 0000000..31cba4f --- /dev/null +++ b/NohanaImagePicker/AlbumCell.swift @@ -0,0 +1,14 @@ +// +// AlbumCell.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +class AlbumCell: UITableViewCell { + @IBOutlet weak var thumbnailImageView: UIImageView! + @IBOutlet weak var titleLabel: UILabel! +} diff --git a/NohanaImagePicker/AlbumListEmptyIndicator.swift b/NohanaImagePicker/AlbumListEmptyIndicator.swift new file mode 100644 index 0000000..1cfcf94 --- /dev/null +++ b/NohanaImagePicker/AlbumListEmptyIndicator.swift @@ -0,0 +1,43 @@ +// +// AlbumListEmptyIndicator.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/23. +// Copyright © 2016年 nohana. All rights reserved. +// + +class AlbumListEmptyIndicator: UILabel { + + init(message: String, description: String, frame: CGRect) { + super.init(frame: frame) + + let centerStyle = NSMutableParagraphStyle() + centerStyle.alignment = NSTextAlignment.Center + + let messageAttributes = [ + NSForegroundColorAttributeName : ColorConfig.emptyIndicator, + NSFontAttributeName : UIFont.systemFontOfSize(26), + NSParagraphStyleAttributeName : centerStyle + ] + let messageText = NSAttributedString(string: message, attributes: messageAttributes) + + let descriptionAttributes = [ + NSForegroundColorAttributeName : ColorConfig.emptyIndicator, + NSFontAttributeName : UIFont.systemFontOfSize(14), + NSParagraphStyleAttributeName : centerStyle + ] + let descriptionText = NSAttributedString(string: description, attributes: descriptionAttributes) + + let attributedText = NSMutableAttributedString() + attributedText.appendAttributedString(messageText) + attributedText.appendAttributedString(NSAttributedString(string: "\n\n", attributes: [NSFontAttributeName : UIFont.systemFontOfSize(6)])) + attributedText.appendAttributedString(descriptionText) + + self.numberOfLines = 0 + self.attributedText = attributedText + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} \ No newline at end of file diff --git a/NohanaImagePicker/AlbumListViewController.swift b/NohanaImagePicker/AlbumListViewController.swift new file mode 100644 index 0000000..03831ce --- /dev/null +++ b/NohanaImagePicker/AlbumListViewController.swift @@ -0,0 +1,253 @@ +// +// AlbumListViewController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit +import Photos + +@available(iOS 8.0, *) +class AlbumListViewController: UITableViewController, EmptyIndicatable, ActivityIndicatable { + + weak var nohanaImagePickerController: NohanaImagePickerController? + var photoKitAlbumList: PhotoKitAlbumList! + + override func viewDidLoad() { + super.viewDidLoad() + title = NSLocalizedString("albumlist.title", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController!.assetBundle, comment: "") + setUpToolbarItems() + navigationController?.setToolbarHidden(nohanaImagePickerController?.toolbarHidden ?? false, animated: false) + setUpEmptyIndicator() + setUpActivityIndicator() + self.view.backgroundColor = ColorConfig.backgroundColor + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + setToolbarTitle(nohanaImagePickerController) + if let indexPathForSelectedRow = tableView.indexPathForSelectedRow { + tableView.deselectRowAtIndexPath(indexPathForSelectedRow, animated: true) + } + } + + // MARK: - UITableViewDelegate + + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didSelectPhotoKitAssetList: photoKitAlbumList[exactRow(indexPath)].assetList) + } + + override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return 0 + } + if nohanaImagePickerController.shouldShowMoment && indexPath.row == 0 { + return 52 + } + return 82 + } + + // MARK: - UITableViewDataSource + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if let emptyIndicator = emptyIndicator { + updateVisibilityOfEmptyIndicator(emptyIndicator) + } + if let activityIndicator = activityIndicator { + updateVisibilityOfActivityIndicator(activityIndicator) + } + + guard nohanaImagePickerController?.shouldShowMoment ?? false else { + return photoKitAlbumList.count + } + return photoKitAlbumList.count != 0 ? photoKitAlbumList.count + 1 : 0 + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + guard !isMomentRow(indexPath) else { + guard let cell = tableView.dequeueReusableCellWithIdentifier("MomentAlbumCell") as? AlbumCell else { + fatalError("failed to dequeueReusableCellWithIdentifier(\"MomentAlbumCell\")") + } + cell.titleLabel?.text = NSLocalizedString("albumlist.moment.title", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController!.assetBundle, comment: "") + return cell + } + + guard let cell = tableView.dequeueReusableCellWithIdentifier("AlbumCell") as? AlbumCell else { + fatalError("failed to dequeueReusableCellWithIdentifier(\"AlbumCell\")") + } + let albumList = photoKitAlbumList[exactRow(indexPath)] + cell.titleLabel.text = albumList.title + cell.tag = exactRow(indexPath) + let imageSize = CGSize( + width: cell.thumbnailImageView.frame.size.width * UIScreen.mainScreen().scale, + height: cell.thumbnailImageView.frame.size.width * UIScreen.mainScreen().scale + ) + if let lastAsset = albumList.last { + lastAsset.image(imageSize, handler: { (imageData) -> Void in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if let imageData = imageData { + if cell.tag == self.exactRow(indexPath) { + cell.thumbnailImageView.image = imageData.image + } + } + }) + }) + } else { + cell.thumbnailImageView.image = nil + } + return cell + } + + // MARK: - Storyboard + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + + if isMomentRow(tableView.indexPathForSelectedRow!) { + let momentViewController = segue.destinationViewController as! MomentViewController + momentViewController.nohanaImagePickerController = nohanaImagePickerController + momentViewController.momentAlbumList = PhotoKitAlbumList( + assetCollectionTypes: [.Moment], + assetCollectionSubtypes: [.Any], + mediaType: nohanaImagePickerController.mediaType, + shouldShowEmptyAlbum: nohanaImagePickerController.shouldShowEmptyAlbum, + handler: { () -> Void in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + momentViewController.isLoading = false + momentViewController.collectionView?.reloadData() + }) + }) + } else { + let assetListViewController = segue.destinationViewController as! AssetListViewController + assetListViewController.photoKitAssetList = photoKitAlbumList[exactRow(tableView.indexPathForSelectedRow!)] + assetListViewController.nohanaImagePickerController = nohanaImagePickerController + } + } + + // MARK: - IBAction + + @IBAction func didPushCancel(sender: AnyObject) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + nohanaImagePickerController.delegate?.nohanaImagePickerDidCancel(nohanaImagePickerController) + } + + // MARK: - Private + + private func exactRow(indexPath: NSIndexPath) -> Int { + guard nohanaImagePickerController?.shouldShowMoment ?? false else { + return indexPath.row + } + return indexPath.row - 1 + } + + private func isMomentRow(indexPath: NSIndexPath) -> Bool { + return indexPath.row == 0 && (nohanaImagePickerController?.shouldShowMoment ?? false) + } + + // MARK: - EmptyIndicatable + + var emptyIndicator: UIView? + + func setUpEmptyIndicator() { + let frame = CGRect(origin: CGPoint.zero, size: Size.screenRectWithoutAppBar(self).size) + emptyIndicator = AlbumListEmptyIndicator( + message: NSLocalizedString("albumlist.empty.message", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController!.assetBundle, comment: ""), + description: NSLocalizedString("albumlist.empty.description", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController!.assetBundle, comment: ""), + frame: frame) + } + + func isEmpty() -> Bool { + if isProgressing() { + return false + } + return photoKitAlbumList.count == 0 + } + + // MARK: - ActivityIndicatable + + var activityIndicator: UIActivityIndicatorView? + var isLoading = true + + func setUpActivityIndicator() { + activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray) + let screenRect = Size.screenRectWithoutAppBar(self) + activityIndicator?.center = CGPoint(x: screenRect.size.width / 2, y: screenRect.size.height / 2) + activityIndicator?.startAnimating() + } + + func isProgressing() -> Bool { + return isLoading + } +} + +@available(iOS 8.0, *) +extension UIViewController { + + // MARK: - Toolbar + + func setUpToolbarItems() { + let leftSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil) + let rightSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil) + + let infoButton = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil) + infoButton.enabled = false + infoButton.setTitleTextAttributes([NSFontAttributeName: UIFont.systemFontOfSize(14), NSForegroundColorAttributeName: UIColor.blackColor()], forState: .Normal) + self.toolbarItems = [leftSpace, infoButton, rightSpace] + } + + func setToolbarTitle(nohanaImagePickerController:NohanaImagePickerController?) { + guard toolbarItems?.count >= 2 else { + return + } + guard + let infoButton = toolbarItems?[1], + let nohanaImagePickerController = nohanaImagePickerController + else { + return + } + if nohanaImagePickerController.maximumNumberOfSelection == 0 { + let title = String(format: NSLocalizedString("toolbar.title.nolimit", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController.assetBundle, comment: ""), + nohanaImagePickerController.pickedAssetList.count) + infoButton.title = title + } else { + let title = String(format: NSLocalizedString("toolbar.title.haslimit", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController.assetBundle, comment: ""), + nohanaImagePickerController.pickedAssetList.count, + nohanaImagePickerController.maximumNumberOfSelection) + infoButton.title = title + } + } + + // MARK: - Notification + + func addPickPhotoKitAssetNotificationObservers() { + NSNotificationCenter.defaultCenter().addObserver(self, selector: "didPickPhotoKitAsset:", name: NotificationInfo.Asset.PhotoKit.didPick, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "didDropPhotoKitAsset:", name: NotificationInfo.Asset.PhotoKit.didDrop, object: nil) + } + + func didPickPhotoKitAsset(notification: NSNotification) { + guard let picker = notification.object as? NohanaImagePickerController else { + return + } + setToolbarTitle(picker) + } + + func didDropPhotoKitAsset(notification: NSNotification) { + guard let picker = notification.object as? NohanaImagePickerController else { + return + } + setToolbarTitle(picker) + } +} diff --git a/NohanaImagePicker/AnimatableNavigationController.swift b/NohanaImagePicker/AnimatableNavigationController.swift new file mode 100644 index 0000000..374e1db --- /dev/null +++ b/NohanaImagePicker/AnimatableNavigationController.swift @@ -0,0 +1,58 @@ +// +// AnimatableNavigationController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/14. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +@available(iOS 8.0, *) +class AnimatableNavigationController: UINavigationController, UINavigationControllerDelegate, UIViewControllerTransitioningDelegate { + + let swipeInteractionController = SwipeInteractionController() + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.delegate = self + } + + func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { + + switch operation { + case .Push where fromVC is AssetListViewController: + guard let fromVC = fromVC as? AssetListViewController, + selectedIndex = fromVC.collectionView?.indexPathsForSelectedItems()?.first, + fromCell = fromVC.collectionView?.cellForItemAtIndexPath(selectedIndex) as? AssetCell + else { + return nil + } + return ExpandingAnimationController(fromCell: fromCell) + case .Pop where fromVC is AssetDetailListViewController: + guard let fromVC = fromVC as? AssetDetailListViewController, + fromCell = fromVC.collectionView?.cellForItemAtIndexPath(fromVC.currentIndexPath) as? AssetDetailCell + else { + return nil + } + return ContractingAnimationController(fromCell: fromCell) + default: + return nil + } + } + + func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) { + swipeInteractionController.attachToViewController(viewController) + } + + func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + if animationController is ExpandingAnimationController { + return nil + } + if animationController is ContractingAnimationController { + return nil + } + return swipeInteractionController + } + +} diff --git a/NohanaImagePicker/AssetCell.swift b/NohanaImagePicker/AssetCell.swift new file mode 100644 index 0000000..f6b3193 --- /dev/null +++ b/NohanaImagePicker/AssetCell.swift @@ -0,0 +1,55 @@ +// +// AssetCell.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +@available(iOS 8.0, *) +class AssetCell: UICollectionViewCell { + + @IBOutlet weak var imageView: UIImageView! + @IBOutlet weak var pickButton: UIButton! + @IBOutlet weak var overlayView: UIView! + + weak var nohanaImagePickerController: NohanaImagePickerController? + var asset: AssetType? + + override func willMoveToSuperview(newSuperview: UIView?) { + super.willMoveToSuperview(newSuperview) + + pickButton.setImage( + UIImage(named: ImageName.AssetCell.PickButton.SizeM.dropped, inBundle: nohanaImagePickerController?.assetBundle, compatibleWithTraitCollection: nil), + forState: .Normal) + pickButton.setImage( + UIImage(named: ImageName.AssetCell.PickButton.SizeM.picked, inBundle: nohanaImagePickerController?.assetBundle, compatibleWithTraitCollection: nil), + forState: [.Normal, .Selected]) + } + + @IBAction func didPushPickButton(sender: UIButton) { + guard let asset = asset, nohanaImagePickerController = nohanaImagePickerController else { + return + } + if pickButton.selected { + if nohanaImagePickerController.pickedAssetList.dropAsset(asset) { + pickButton.selected = false + } + } else { + if nohanaImagePickerController.pickedAssetList.pickAsset(asset) { + pickButton.selected = true + } + } + self.overlayView.hidden = !pickButton.selected + } + + func update(asset: AssetType, nohanaImagePickerController: NohanaImagePickerController?) { + self.asset = asset + self.nohanaImagePickerController = nohanaImagePickerController + self.pickButton.selected = nohanaImagePickerController?.pickedAssetList.isPicked(asset) ?? false + self.overlayView.hidden = !pickButton.selected + self.pickButton.hidden = !(nohanaImagePickerController?.canPickAsset(asset) ?? true) + } +} \ No newline at end of file diff --git a/NohanaImagePicker/AssetDetailCell.swift b/NohanaImagePicker/AssetDetailCell.swift new file mode 100644 index 0000000..229bca7 --- /dev/null +++ b/NohanaImagePicker/AssetDetailCell.swift @@ -0,0 +1,66 @@ +// +// AssetDetailCell.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/12. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +class AssetDetailCell: UICollectionViewCell, UIScrollViewDelegate { + + @IBOutlet weak var scrollView: UIScrollView! + @IBOutlet weak var imageView: UIImageView! + + @IBOutlet weak var imageViewHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var imageViewWidthConstraint: NSLayoutConstraint! + let doubleTapGestureRecognizer :UITapGestureRecognizer = UITapGestureRecognizer() + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + doubleTapGestureRecognizer.addTarget(self, action: "didDoubleTap:") + doubleTapGestureRecognizer.numberOfTapsRequired = 2 + } + + override func willMoveToSuperview(newSuperview: UIView?) { + super.willMoveToSuperview(newSuperview) + scrollView.removeGestureRecognizer(doubleTapGestureRecognizer) + scrollView.addGestureRecognizer(doubleTapGestureRecognizer) + } + + deinit { + scrollView.removeGestureRecognizer(doubleTapGestureRecognizer) + doubleTapGestureRecognizer.removeTarget(self, action: "didDoubleTap:") + } + + // MARK: - UIScrollViewDelegate + + func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { + return imageView + } + + // MARK: - Zoom + + func didDoubleTap(sender: UITapGestureRecognizer) { + if scrollView.zoomScale < scrollView.maximumZoomScale { + let center = sender.locationInView(imageView) + scrollView.zoomToRect(zoomRect(center), animated: true) + } else { + let defaultScale: CGFloat = 1 + scrollView.setZoomScale(defaultScale, animated: true) + } + } + + func zoomRect(center: CGPoint) -> CGRect { + var zoomRect: CGRect = CGRect() + zoomRect.size.height = scrollView.frame.size.height / scrollView.maximumZoomScale + zoomRect.size.width = scrollView.frame.size.width / scrollView.maximumZoomScale + + zoomRect.origin.x = center.x - zoomRect.size.width / 2.0 + zoomRect.origin.y = center.y - zoomRect.size.height / 2.0 + + return zoomRect + } + +} \ No newline at end of file diff --git a/NohanaImagePicker/AssetDetailListViewController.swift b/NohanaImagePicker/AssetDetailListViewController.swift new file mode 100644 index 0000000..e138737 --- /dev/null +++ b/NohanaImagePicker/AssetDetailListViewController.swift @@ -0,0 +1,133 @@ +// +// AssetDetailListViewController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +@available(iOS 8.0, *) +class AssetDetailListViewController: AssetListViewController { + + var selectedIndexPath:NSIndexPath! + var currentIndexPath: NSIndexPath = NSIndexPath() { + willSet { + if currentIndexPath != newValue { + didChangeAssetDetailPage(newValue) + } + } + } + @IBOutlet weak var pickButton: UIButton! + + override var cellSize: CGSize { + get { + return Size.screenRectWithoutAppBar(self).size + } + } + + override func viewDidLoad() { + super.viewDidLoad() + pickButton.setImage( + UIImage(named: ImageName.AssetCell.PickButton.SizeL.dropped, inBundle: nohanaImagePickerController?.assetBundle, compatibleWithTraitCollection: nil), + forState: .Normal) + pickButton.setImage( + UIImage(named: ImageName.AssetCell.PickButton.SizeL.picked, inBundle: nohanaImagePickerController?.assetBundle, compatibleWithTraitCollection: nil), + forState: [.Normal, .Selected]) + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + } + + override func updateTitle() { + self.title = "" + } + + func didChangeAssetDetailPage(indexPath:NSIndexPath) { + let asset = photoKitAssetList[indexPath.item] + pickButton.selected = nohanaImagePickerController?.pickedAssetList.isPicked(asset) ?? false + pickButton.hidden = !(nohanaImagePickerController?.canPickAsset(asset) ?? true) + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, assetDetailListViewController: self, didChangeAssetDetailPage: indexPath, photoKitAsset: asset.originalAsset) + } + + override func scrollCollectionViewToInitialPosition() { + guard isFirstAppearance else { + return + } + isFirstAppearance = false + guard photoKitAssetList.count > 0 else { + return + } + collectionView?.scrollToItemAtIndexPath(self.selectedIndexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: false) + } + + // MARK: - IBAction + + @IBAction func didPushPickButton(sender: UIButton) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + let asset = photoKitAssetList[currentIndexPath.row] + if pickButton.selected { + if nohanaImagePickerController.pickedAssetList.dropAsset(asset) { + pickButton.selected = false + } + } else { + if nohanaImagePickerController.pickedAssetList.pickAsset(asset) { + pickButton.selected = true + } + } + } + + // MARK: - UICollectionViewDelegate + + override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AssetDetailCell", forIndexPath: indexPath) as? AssetDetailCell, + nohanaImagePickerController = nohanaImagePickerController + else { + return UICollectionViewCell(frame: CGRectZero) + } + cell.scrollView.zoomScale = 1 + cell.tag = indexPath.item + + let imageSize = CGSize( + width: cellSize.width * UIScreen.mainScreen().scale, + height: cellSize.height * UIScreen.mainScreen().scale + ) + let asset = photoKitAssetList[indexPath.item] + asset.image(imageSize) { (imageData) -> Void in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if let imageData = imageData { + if cell.tag == indexPath.item { + cell.imageView.image = imageData.image + cell.imageViewHeightConstraint.constant = self.cellSize.height + cell.imageViewWidthConstraint.constant = self.cellSize.width + } + } + }) + } + return (nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, assetDetailListViewController: self, cell: cell, indexPath: indexPath, photoKitAsset: asset.originalAsset)) ?? cell + } + + // MARK: - UIScrollViewDelegate + + override func scrollViewDidScroll(scrollView: UIScrollView) { + guard let collectionView = collectionView else { + return + } + let row = Int((collectionView.contentOffset.x + cellSize.width * 0.5) / cellSize.width) + currentIndexPath = NSIndexPath(forRow: row, inSection: 0) + } + + // MARK: - UICollectionViewDelegateFlowLayout + + override func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + return cellSize + } + +} \ No newline at end of file diff --git a/NohanaImagePicker/AssetListViewController.swift b/NohanaImagePicker/AssetListViewController.swift new file mode 100644 index 0000000..6bc9d07 --- /dev/null +++ b/NohanaImagePicker/AssetListViewController.swift @@ -0,0 +1,141 @@ +// +// AssetListViewController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit +import Photos + +@available(iOS 8.0, *) +class AssetListViewController: UICollectionViewController { + + weak var nohanaImagePickerController: NohanaImagePickerController? + var photoKitAssetList: PhotoKitAssetList! + + override func viewDidLoad() { + super.viewDidLoad() + updateTitle() + setUpToolbarItems() + addPickPhotoKitAssetNotificationObservers() + self.view.backgroundColor = ColorConfig.backgroundColor + } + + var cellSize: CGSize { + get { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return CGSize.zero + } + var numberOfColumns = nohanaImagePickerController.numberOfColumnsInLandscape + if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) { + numberOfColumns = nohanaImagePickerController.numberOfColumnsInPortrait + } + let cellMargin:CGFloat = 2 + let cellWidth = (view.frame.width - cellMargin * (CGFloat(numberOfColumns) - 1)) / CGFloat(numberOfColumns) + return CGSize(width: cellWidth, height: cellWidth) + } + } + + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + setToolbarTitle(nohanaImagePickerController) + collectionView?.reloadData() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + scrollCollectionViewToInitialPosition() + } + + var isFirstAppearance = true + + func updateTitle() { + title = photoKitAssetList.title + } + + func scrollCollectionViewToInitialPosition() { + guard isFirstAppearance else { + return + } + guard photoKitAssetList.count > 0 else { + return + } + let index = NSIndexPath(forRow: self.photoKitAssetList.count - 1, inSection: 0) + collectionView?.scrollToItemAtIndexPath(index, atScrollPosition: .Bottom, animated: false) + + isFirstAppearance = false + } + + // MARK: - UICollectionViewDataSource + + override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return photoKitAssetList.count + } + + // MARK: - UICollectionViewDelegate + + override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didSelectPhotoKitAsset: photoKitAssetList[indexPath.item].originalAsset) + } + + override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AssetCell", forIndexPath: indexPath) as? AssetCell, + nohanaImagePickerController = nohanaImagePickerController else { + return UICollectionViewCell(frame: CGRectZero) + } + cell.tag = indexPath.item + cell.update(photoKitAssetList[indexPath.row], nohanaImagePickerController: nohanaImagePickerController) + + let imageSize = CGSize( + width: cellSize.width * UIScreen.mainScreen().scale, + height: cellSize.height * UIScreen.mainScreen().scale + ) + let asset = photoKitAssetList[indexPath.item] + asset.image(imageSize) { (imageData) -> Void in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if let imageData = imageData { + if cell.tag == indexPath.item { + cell.imageView.image = imageData.image + } + } + }) + } + return (nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, assetListViewController: self, cell: cell, indexPath: indexPath, photoKitAsset: asset.originalAsset)) ?? cell + } + + // MARK: - UICollectionViewDelegateFlowLayout + + func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { + return cellSize + } + + // MARK: - Storyboard + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + let assetListDetailViewController = segue.destinationViewController as! AssetDetailListViewController + assetListDetailViewController.photoKitAssetList = photoKitAssetList + assetListDetailViewController.nohanaImagePickerController = nohanaImagePickerController + assetListDetailViewController.selectedIndexPath = collectionView?.indexPathsForSelectedItems()?.first + } + + // MARK: - IBAction + @IBAction func didPushDone(sender: AnyObject) { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return + } + + let pickedPhotoKitAssets = nohanaImagePickerController.pickedAssetList.map{ ($0 as! PhotoKitAsset).originalAsset } + nohanaImagePickerController.delegate?.nohanaImagePicker(nohanaImagePickerController, didFinishPickingPhotoKitAssets: pickedPhotoKitAssets ) + } +} + + diff --git a/NohanaImagePicker/ColorConfig.swift b/NohanaImagePicker/ColorConfig.swift new file mode 100644 index 0000000..eff2cbb --- /dev/null +++ b/NohanaImagePicker/ColorConfig.swift @@ -0,0 +1,16 @@ +// +// ColorConfig.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/23. +// Copyright © 2016年 nohana. All rights reserved. +// + +public struct ColorConfig { + public static var backgroundColor = UIColor.whiteColor() + public static var emptyIndicator = UIColor(red: 0x88/0xff, green: 0x88/0xff, blue: 0x88/0xff, alpha: 1) + + public struct AlbumList { + public static var momentCellSeparator = UIColor(red: 0xbb/0xff, green: 0xbb/0xff, blue: 0xbb/0xff, alpha: 1) + } +} \ No newline at end of file diff --git a/NohanaImagePicker/ContractingAnimationController.swift b/NohanaImagePicker/ContractingAnimationController.swift new file mode 100644 index 0000000..2d44dcf --- /dev/null +++ b/NohanaImagePicker/ContractingAnimationController.swift @@ -0,0 +1,90 @@ +// +// ContractingAnimationController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/15. +// Copyright © 2016年 nohana. All rights reserved. +// + +import AVFoundation + +@available(iOS 8.0, *) +extension Size { + static func contractingAnimationToCellRect(toVC: AssetListViewController, toCell: AssetCell) -> CGRect { + let origin = CGPoint(x: toCell.frame.origin.x, y: toCell.frame.origin.y - toVC.collectionView!.contentOffset.y) + return CGRect(origin: origin, size: toCell.frame.size) + } + + static func contractingAnimationFromCellRect(fromVC: AssetDetailListViewController, fromCell: AssetDetailCell, contractingImageSize: CGSize) -> CGRect { + var rect = AVMakeRectWithAspectRatioInsideRect(contractingImageSize, fromCell.imageView.frame) + rect.origin.y += Size.appBarHeight(fromVC) + rect.origin.x -= fromCell.scrollView.contentOffset.x + rect.origin.y -= fromCell.scrollView.contentOffset.y + return rect + } +} + +@available(iOS 8.0, *) +class ContractingAnimationController: NSObject, UIViewControllerAnimatedTransitioning { + + var fromCell: AssetDetailCell + + init(fromCell: AssetDetailCell) { + self.fromCell = fromCell + } + + func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { + return 0.3 + } + + func animateTransition(transitionContext: UIViewControllerContextTransitioning) { + guard + let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as? AssetDetailListViewController, + toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as? AssetListViewController, + containerView = transitionContext.containerView(), + fromCellIndex = fromVC.collectionView?.indexPathForCell(fromCell) + else { + return + } + + var toCellTmp = toVC.collectionView?.cellForItemAtIndexPath(fromCellIndex) as? AssetCell + if toCellTmp == nil { + // if toCell is not shown in collection view, scroll collection view to toCell index path. + toVC.collectionView?.scrollToItemAtIndexPath(fromVC.currentIndexPath, atScrollPosition: .CenteredVertically, animated: false) + toVC.collectionView?.layoutIfNeeded() + toCellTmp = toVC.collectionView?.cellForItemAtIndexPath(fromCellIndex) as? AssetCell + } + + guard let toCell = toCellTmp else { + return + } + + let contractingImageView = UIImageView(image: fromCell.imageView.image) + contractingImageView.contentMode = toCell.imageView.contentMode + contractingImageView.clipsToBounds = true + contractingImageView.frame = Size.contractingAnimationFromCellRect(fromVC, fromCell: fromCell, contractingImageSize: contractingImageView.image!.size) + + containerView.addSubview(toVC.view) + containerView.addSubview(contractingImageView) + toVC.view.alpha = 0 + fromCell.alpha = 0 + toCell.alpha = 0 + + UIView.animateWithDuration( + transitionDuration(transitionContext), + delay: 0, + options: .CurveEaseInOut, + animations: { () -> Void in + toVC.view.alpha = 1 + contractingImageView.frame = Size.contractingAnimationToCellRect(toVC, toCell: toCell) + }) { (_) -> Void in + self.fromCell.alpha = 1 + toCell.alpha = 1 + contractingImageView.removeFromSuperview() + transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) + } + + } + + +} \ No newline at end of file diff --git a/NohanaImagePicker/EmptyIndicatable.swift b/NohanaImagePicker/EmptyIndicatable.swift new file mode 100644 index 0000000..04086f3 --- /dev/null +++ b/NohanaImagePicker/EmptyIndicatable.swift @@ -0,0 +1,24 @@ +// +// EmptyIndicatable.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/23. +// Copyright © 2016年 nohana. All rights reserved. +// + +public protocol EmptyIndicatable { + func isEmpty() -> Bool + func updateVisibilityOfEmptyIndicator(emptyIndicator: UIView) +} + +public extension EmptyIndicatable where Self: UIViewController { + func updateVisibilityOfEmptyIndicator(emptyIndicator: UIView) { + if isEmpty(){ + if !view.subviews.contains(emptyIndicator) { + view.addSubview(emptyIndicator) + } + } else { + emptyIndicator.removeFromSuperview() + } + } +} \ No newline at end of file diff --git a/NohanaImagePicker/ExpandingAnimationController.swift b/NohanaImagePicker/ExpandingAnimationController.swift new file mode 100644 index 0000000..02e2f1c --- /dev/null +++ b/NohanaImagePicker/ExpandingAnimationController.swift @@ -0,0 +1,75 @@ +// +// ExpandingAnimationController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/14. +// Copyright © 2016年 nohana. All rights reserved. +// + +import AVFoundation + +@available(iOS 8.0, *) +extension Size { + + static func expandingAnimationFromCellRect(fromVC: AssetListViewController, fromCell: AssetCell) -> CGRect { + let origin = CGPoint(x: fromCell.frame.origin.x, y: fromCell.frame.origin.y - fromVC.collectionView!.contentOffset.y) + return CGRect(origin: origin, size: fromCell.frame.size) + } + + static func expandingAnimationToCellRect(fromVC: UIViewController, toSize:CGSize) -> CGRect { + return AVMakeRectWithAspectRatioInsideRect(toSize, Size.screenRectWithoutAppBar(fromVC)) + } +} + +@available(iOS 8.0, *) +class ExpandingAnimationController: NSObject, UIViewControllerAnimatedTransitioning { + + var fromCell: AssetCell + + init(fromCell: AssetCell) { + self.fromCell = fromCell + } + + func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { + return 0.3 + } + + func animateTransition(transitionContext: UIViewControllerContextTransitioning) { + guard let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as? AssetListViewController, + toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as? AssetDetailListViewController, + containerView = transitionContext.containerView() + else { + return + } + + let expandingImageView = UIImageView(image: fromCell.imageView.image) + expandingImageView.contentMode = fromCell.imageView.contentMode + expandingImageView.clipsToBounds = true + expandingImageView.frame = Size.expandingAnimationFromCellRect(fromVC, fromCell: fromCell) + + containerView.addSubview(toVC.view) + containerView.addSubview(expandingImageView) + toVC.view.alpha = 0 + toVC.collectionView?.hidden = true + toVC.view.backgroundColor = UIColor.blackColor() + fromCell.alpha = 0 + + UIView.animateWithDuration( + transitionDuration(transitionContext), + delay: 0, + usingSpringWithDamping: 0.8, + initialSpringVelocity: 10, + options: .CurveEaseOut, + animations: { () -> Void in + toVC.view.alpha = 1 + expandingImageView.frame = Size.expandingAnimationToCellRect(fromVC, toSize: expandingImageView.image!.size) + }) { (_) -> Void in + self.fromCell.alpha = 1 + toVC.collectionView?.hidden = false + expandingImageView.removeFromSuperview() + transitionContext.completeTransition(!transitionContext.transitionWasCancelled()) + } + } + +} + diff --git a/NohanaImagePicker/ImageName.swift b/NohanaImagePicker/ImageName.swift new file mode 100644 index 0000000..f9ccc37 --- /dev/null +++ b/NohanaImagePicker/ImageName.swift @@ -0,0 +1,22 @@ +// +// ImageName.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/16. +// Copyright © 2016年 nohana. All rights reserved. +// + +struct ImageName { + struct AssetCell { + struct PickButton { + struct SizeM { + static let picked = "btn_selected_m" + static let dropped = "btn_select_m" + } + struct SizeL { + static let picked = "btn_selected_l" + static let dropped = "btn_select_l" + } + } + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/Contents.json b/NohanaImagePicker/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/NohanaImagePicker/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/btn_select_l.imageset/Contents.json b/NohanaImagePicker/Images.xcassets/btn_select_l.imageset/Contents.json new file mode 100644 index 0000000..5e4b2c9 --- /dev/null +++ b/NohanaImagePicker/Images.xcassets/btn_select_l.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "btn_select_l@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "btn_select_l@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/btn_select_l.imageset/btn_select_l@2x.png b/NohanaImagePicker/Images.xcassets/btn_select_l.imageset/btn_select_l@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d1cb8166a3d0b6219217df766e19a037a908f43 GIT binary patch literal 1777 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 literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/Contents.json b/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/Contents.json new file mode 100644 index 0000000..a4c6bec --- /dev/null +++ b/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "btn_select_m@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "btn_select_m@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@2x.png b/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0e3cac351d67d6708ad474548ecd2c390a304812 GIT binary patch literal 1061 zcmeAS@N?(olHy`uVBq!ia0vp^5g^RL3?$vW53m6#mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fHRz`!UQ;1l8sRIq?9VAZNs zH*em2^XARDbLSuy-nen&{rmS2_Vw%6Z{50u?2v2MuHC+U`|8!JckbMQh+nyKdsg%>YgeEs@0PytYC(V|6{E?v5R|2|Og!Gi}6!C>*?#qZv| zd-UiLP-N-SrBA>BXw=iEPl4RCXU{^cKqi49!_Gd%7D&aF1o;IshYh7ML) z4Vu#Dt$%3L*LKBL`oz@V@vUL9>@rjLjdjd;O7A&ogr` zW-N+iFFkQNwn6h-mQPYlP}E%{z@fb>_@TY)ap#cR;5*y5sS~ibh7gpq-Xq zH+GaCHae?2_4)sKPr0Px7VNybDJZP|vyxPt(+<_Ii#tkfMapOH*m|&0R3#?8BvF*p zJm;?IqjlNsQXV(TcHdEWw=8c>XG^r%JF&v;Uz>GR_9VZ4_fFF7`@|0i!uhh;zJEBu z&~JSBZQrta$7P>B`5+>5I7@Sysd1^>onm2em2W3k^PLbsve%^e;$-t5yH*z6-)mHQ zasBMH#y_WWAI06Dv0wWJ>!R38_MaTxU*3z_@j`X&?UrAkV(;(WUT|^tgu2@*n--PG z>%@N9mU-hv=bhRuB@a1y!&937{7HIgB;$MR@E6fFhYMzK<>$%%Ul$v3XLr)hGEI>d zlXGu3?mE$3e$P?*QP}DSamCK7E-t=uJ<(b%K^>bP0l+XkK D8saP= literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@3x.png b/NohanaImagePicker/Images.xcassets/btn_select_m.imageset/btn_select_m@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3b735afd6fd7ea5dc7a6691b3383c25a9c459e69 GIT binary patch literal 1473 zcmd5*X;4#F6uz$suLwNH(P%&_f@^9)fq)B8Fc?fwp^+eq9W9HB#Zp1E24o3~fM~HD z8-<8y5)uO;ApwhsLBqaffj}*S5CSTWwgf^^!JvKWOs9YQuV?Q0mUGXUIdf<3Ia(kE zu~=>a06_eyiE>+bG07z{lw>c$}+X|&6HS5kNp82iV0C8Z|`Wx_x&gER6`}=7zT~QIi-e$an>tA8I z$7aR`$NDKLveBKfqx#2EwZ2`Wd(`%LMAa}-qwLpdn3{}8UAM2UTh}wJP3`UNd)W6| zHQ{j3_D1`1hs2G1`gtqAd)hGJ47vamwVOf)d1fo9s96wC4NnBXa`F3wv_oko05G2Z zWKu9=;Lm|sdhd3eV#;X^zjC_)K58j!Uu#b?E43pKMl3s&z*&CjV6&?}GkaG( zpJtUh4_SfvR0nHy-dh~Srz8AHP9n4*1T((W7S6J*fLY6P;L=5y9TrFv>3S}2JSwb+ zgS+GgnuF`VrtIV+fVVn!5q=TO%v`+V${(0WpJf|{SD$nFr6Kw1=Zu>Ls}V4EvZSzl9WOhg%#`~Y6Wm3&Ein)e&U`x^N7pK68d50wj^>m3+gnQ z2Rw>b{SMe(z9y%%`XjaanO81J%>n!1LKdaC__gZp!O>+JFmKEoETB6%vQL*<#fRGI z-5kTOc$C$~*j}Dbb53fWZ(ZUPvgv8Cm(b>N3X6OQ{ajxnHWTaxc zMnYpZPwIG+$3iSZ-YkjwrdUpwCFdS&GYJ%mFz76;ntR6MK~4B$T!ry^fnmi3#D7(A z+H8VtTzf4wA)ov2BYY>iFL0g3j})~i(7y%z{Q}7n--yh=0Rq_FG5`Po literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/Contents.json b/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/Contents.json new file mode 100644 index 0000000..8d2c67f --- /dev/null +++ b/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "btn_selected_l@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "btn_selected_l@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@2x.png b/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f5c27b944ea8a72ff032ac75e93bbb950a0e6403 GIT binary patch literal 1913 zcmeHGi#yZ%AO9@mHgc_$%0W6us7}9_%+svAxP88`x zi4$@yY~Ss>jm~tT7@N&7$tJTK&U&8TpYeO1_xt^NUhnJkyr1{G;7GtB1HBD;0009Y z)WIM%?)#=X8fyFJNrrCj`$t(QYT=b|I7b2fQCRm1r$&LnQ2k?!KZq_1rO_^si1^m z!kWO#RT9y{K*P|{0xF_}VayWZrApDt%1WP*P()izWSB4~a8iY6PCoW>Br2bZ$m>-X znlmOa3yP_gm1VJPw2c#8L^I0m1ti9LSdcG~aWN2*#IT_8bEJxCiF~}B^9L5vD`9M) z@v|hd@s810EKLUssry(!{EV)#D;P*KkD{h)A%5mp#l*aP{53ZL10e`>{X2bI2r!Ze zo6rQ=^KyPSHxUgXa5TMEPBeF}qjU5!8qy)chS0b=I?>Hd!a^E_6i`S7EyEEa>3~?q z?HIj;hBS)dHB^44R5A6Mn^*+tQpYnEm7nKioMPCBGM*-p^SXISMUdg`KAV1FiA*_L zKmkOC>7rt$h;Bq?S+RsTnQE?mGzLvY5MlFP-hU-Bo=o+nhnIw-8|U-_xpGb{8*3Yh z#z0y`cx?~w24y@IPd6m9HnW69t(^0PR4p9c5Jy9jnU>ANVRN!Eneq#su6K)R(LY(T zsGM0|S(dBjaCBV~b8Y`5Nuio&OrUXe9TLM#s+>_SEx>|8{I{sNYDrG#7Z6}0GIIl6 zkk>VOg#eq8nd{YOfBt6Jw~qA`Txq2=cWyq0~h83Mx1r0DY5hieQi2Q~{uo=yTBH7z(=k zA;H}=a6OpQ)+u}I?!Fi8?`x)2<$#Qh^-N9NeO?spYW_8LraW{I8#-7w=y<((Ff=Vm zJE`qrVq5%Zcw4jb^9WkWan5$=?&DOXKOp*ry%`$va*B_07&~)0&o=u>S=+D|H)Fo% z7p`?;;=EZ@PJ4T5y_YO0#k>L5Q`#Rdb#^Q1%+U%aR8k5fUwd3v6dFFt3Or_++&)%6fK0%DIUjA$r z+nnNROM8FuW7EL%&=V0&88K0Tk8D0AP7XefU)NL-XP%sWJd&ameXMhJ$K9LR`R-jM zzN2lDW0$fptMR5eJgsSKee3=>JlbU`JtNC%Yr>q--RE`Q{$@fOUQI>MY6NnNn`i$8 zOkIv;47!Ezz+2-X*5dHh(=i`B0_;VXhLc*mbWOJf5?`|Hw;gk)P0OnNszN{hJ+*p3 zdOx}5XJOFc3;tc(qM7AdJ5%~X*s66gR(BNM+HX=<5XI0xI5@!2!_w_qDWR7zEO&!jBGEXJ+;YE5x!`_4Idc_if5M%6DNDB- zg`h9I1ttC|*`;OU7`uQ-Mm!DasCEwNuMT~WEmIW@0lU+)?7B~12MoL77sK}M-x1t4 zyHU2OQQ+t7vT$G9@tIqQSZ7RJv+ZGe&vOH2`1a|kMj^hzbxEt;QtG>%U-|)_@v=ic zi@&F_=`yF&^m@j-amOI-vq^u>BgV!b^>-US*~r#q4>$x*(RR4p4)01mU_BE@Bt<3L zA`jeLR$Q#-N!T{OdcI$g`vqT>>sk!$X`s>0$(Dl=QtMBdKUx4l>Fun&b1-#Vv5dYfNcxI4|5R&v+>j$UK6KkWRAr!n`@G4bv- zRpFWqYgswIq6w5k$cPEQK(8s1WWDPL56@lw?T%vlRmW3svB~KdzJA3oVvWg`{qu|5 Z9sfv*D=W58AFBTn_;>{zeCl~7^Iz{V(D?uW literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@3x.png b/NohanaImagePicker/Images.xcassets/btn_selected_l.imageset/btn_selected_l@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..343033034c8b813c58ba15fa35f98ce743821128 GIT binary patch literal 1686 zcmeH``BM^j7{8=_&ZJDhVm6<1%Azq}G zY2Ie0X_g&=ff<5;8);f92wr$`y7YhS%=^5bdER+vo*&+M?;sBc=xLj20|4}3P+zo? zm|xO_DEqFK5s?yVXT1F`XeRKB#jx~f=}f17zBkuP#*o=apR zj_?_Gx_L-g^L4sOB3~F1)_$FC91_<3JKew*RIvq5S%ONjZ2sfewc**e71`|M0yU4K z&6;{LGW#mCM`dz>mPgSUoGO>fmq+JbKOS&hkflsGUau~L zPa2zhlSPK`7yB9fQki^ZZ0>C~S#4^OKEN*-oA1shtN)mPmqXSNEHQq}zt5qp5iSkl zd-dqNqH$4=Tp^|N3MWJq;SvkqtN(>Zlqtj$qF&(=`^zL@a^X`hMN^@W;*RR9XLk@A$1v}MM zHkul=l5|rsPX)DQX&IZAO_#f`Y4b~Crb((a>wWZ&_%ORJn^uu$`ns);PQzDjWf>Z@ z#fb6~tam%%F5OM^<-G8}VN(_vhiZzb;Wp|eb@Li};JJzRc6)V$1B)y+mX4^MaWx)O zCs#!2;~OZjqA2)zvn_WlZJlrSd@boaG724j>*DC}Aux@qt5NX47CuU*I5=QMQGsb4 zVX2N?o_%UN$SlW5ooGawcd=FwW_P@*$$FIZT;)b%i)(|SobkZ4xY`)USk?P##A&qX zT$MY?>l3vQL{Aka2k#98(vaqkZLa71#AeSOTx~pmR<{*m5oc~=llMS%^RGp@X+vUxlE{2r1M*PuG5~^?i{VWq+;PbB9?SS_y%(JqTC@Lx<8&xkvp>l9;hpCAYzqQ`UM&ldo z0~YOAkC2I29KolZ<%8;MaAQ{z4?-mC_#Y zpvAHZY)GlstoQvj8a_^06Cp6p+#8c1?MhP#~X`fksRfuBS|A(w|G%r;ou z>y*#Y)jAn{^Z;VN^t+{a%-G%oOXSllTcO6uB~MSLiyyEs>tnb=oYR=Bb*3SWw#B0j zk@MwaYmFkrYMdT_;$GYpNvIRA#G@jyQgNX$>aL4xV6J}mIMt~Orf=-uV@~&D-1PJy zH?HC6ogXOi#TZMlf*-&c=V-#2>}y@;nwamjAxW{%5jY+g!5h(@i{5GLl-f zxl!UsPpHv;-@f)l?C&&N*ftAHggLXq-JsV#b{yGs@OS{N;0^;s6&^|IU^$X@_k_;Lt?1`7L!eb@vZ6{U}}IN^CW5>@e Yg6Hv%>7NsCC|?%9{0{rpd7rxeAG6C%^8f$< literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/Contents.json b/NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/Contents.json new file mode 100644 index 0000000..307b92b --- /dev/null +++ b/NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "btn_selected_m@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "btn_selected_m@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/btn_selected_m@2x.png b/NohanaImagePicker/Images.xcassets/btn_selected_m.imageset/btn_selected_m@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d3a92594b9e7f4d6ad1bb22d6801381085d55991 GIT binary patch literal 1171 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 TY0buhe*wZ+I-p7BzDfTA`6v3^ literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Images.xcassets/icon_photosize_alert_s.imageset/icon_photosize_alert_s@3x.png b/NohanaImagePicker/Images.xcassets/icon_photosize_alert_s.imageset/icon_photosize_alert_s@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..80f020347118a1b59bf3b974f7b3501cf56df479 GIT binary patch literal 3878 zcmV+>583dEP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv00000008+zyMF)x010qNS#tmY3labT3lag+-G2N401k>tL_t(|+U=ZsR8v>J z$G;~JNP;{9;UNztph8R45sR;}sMxDuyV_;7?Xp_$RHsYZacXtyOxwA3-RbLAbgcGH zee~AH%&2YcZS8f&i`a_7Siw@^f)rmNAS8i=013sA=h^pdJ!Qc3cx}D*#I)wPXd5(0K(X8cLDSOXajJQ{Zs+C zNKuq!tYHnO@+pLnR|z5C5<)y==;C3^c$E;6GS>AP41jz>NC_c?9!BMbgr;fwg4^vb z2&T?KZ5o7-WdPm)WB$ILyW3g~ty+!7u5GwY8Cse{y1OkQy?1R=0FWyb?(oP+XHxP6 zr+U(4lB!OVCnhB)h(sc>KzY?PO>dV-BximUAP6B70qh0vXy0cpr_)wZ{|wii&C+GZ2K3UjTR? zfP#PDWHPlKKm38K^sC|&m&-Za*8l*d(h%e>UaDKY?w29)@dvNl53Qw3R#wl5gCK-N1Nb|Dx!gUPrrn=?e4_dI;e%a@Cp?kw_%r z?z>#B%Inv!KRkWo7LP07GlE-TM-)< zKO_KsrQKx2!S{Bf{2vzw%qv$YJRhF;+!Cc!#sC0K)Am!RPR(Aqa%CNWe&!WO+uqAT zplRB@ecNVnAP@i`DP=;?AOJvYTs;2m%^leC@^*yEWPbDZ+HB(2e*2<`rfD|-h(sdU z;>C-11CYaq0r;&DJ9l`UOMiOeuXiq-D;giD;`P^R;B-1MVI&|ne^sx%RO@k0}6>KkI>FP57;_*jR zLm4csd}=M8+3>5N!T7J`aGE4@0nN z5JK|7>niu{+36bUi1EKq`~^o2z8`cDiAl+LW9MGK&BWz$;=tZr4lX@z+_=myzx;AJ zfDnE@e;|IxU7Jis0!vWyKc zZ1vkjO1~;jaXK8mY?6B7i6>Tp=?y7C5EMl{#$8ujzSQc^HxxXw9I1(WK0y#dq9}^`8J8|8KBEvSTqF`<#gl8i>9K~f z;>k4-iG+tn=XrIBjEtOAP*9Kr)@l0s)6zTMUe{vux5k*8_n^=FV+lh@CjhY9dnton z*T$t&Q&aC_L5O@cjZE(PPNTLLeq{R09KmN}1;d|C0MMju@D4>|VqzwOahTG6ASjB; z;I11RG=c?2t5gF{rN;t>DmKpVESFy=rBay;#v&xXKzMzEUe_EVRA6$-#DEkV3mE=% zz`cF#PGpuaj6TmV@av#J()T+r@A@P`*Sa7wlEX z9ELyL_;r#%xza*~*ooWP~)>gpQ6Z2EZmfjD>W+(od9nj(=%JT-e}n^3`3l@)%? zaI|4`n$4)HtPuR()EU`EF8YQLLJk}_Py@gYz}*kT`t|EO>~?!Sm(E+VOmN~w(=bA#cBGHk+GjYilinAON^JJ3GBGNpt4SA1{>(PB~C?`qZ$x z(qO@8HyKfM`jlW^sWb$0=FRsmKv!2+e*>mj*Z{bEE#8vs>}=z-Y11|Wpu`f1wAEl} z(Q0l*`g(!K?S`?f6}fr;5;Pd$5fPX@KNpuvF9f_GeBsNpXwnMLmKQBqrklSokBhD$ zn>KCwlSZS_0Wbov2`#!PtXAuVh=_<;0JEIG?ukd^{q1~jd3ie?EO5a2tRHz6MmR5CFEy%F12bjVPs3wR+tK zt-qIcTP^s_bL)Rpmhsdx&o=TS?~WcldJ>F->}3BE!2c$&2!PUPG=3fz7nch#VF530 zc;@?o;-x}mGCcp{wlVh^=~FT+dk!8Q$3@&}wc2mf)6?Gppa)|OU0}yd{=xzX03w&m zZ|CObt`dnv5{jZkb01h}D?0sosN3ZdT+8sd-Ke-!hQLG% zXfx>hJrheLAiObzT%qvn-hafdP$DJ4HWow&0Pjq6pnwm$tgOts zXwf2fYHI320FcS$il5y-TX(+rOyoduO}geL6#eZKy1T7Vr%i%fF}PJNPCCtI96Ry> z-hJl{=$d`cEcp;5QYqfqdqAH&VS;x!ef;?G4+{$mtHE@adhnVB5LgVA6hMMjtNkyv zTD_FJ`Azxdrq^HDn)+i5Cfok@AMebb_jB(GZhd`yX;xO&`vBSjG=Mn|@`JUOKtNCc z6gfFLlZ%RqPQ=8-+{1_Q*7o0~x?QdSius{Lt5ir%nTXi9c<&mcz1N0zlM%X>J42eo zNTgD{`udI^Kroq1x3aUd-_+~%?Esnpw4jeQ1PTIR9A{*He*W~2KmPbHk&%(!XtkTy z>sx-m^;e03##N1!$Q26DTf5&k@FVFg7K?7df(8FxTU%=a(_J>9PoxYdfhtb`xHTG0 zkHKL0zr4J>2V^o?Bmk&lVC%!PCFo*tqqT*J8Qe0brU#US8f?ECeG9p%Z=l0$+v_ogWH7m64G#<%=)A z*q)i0xrqO;tFyCX|K45B%cUjb$KsemeSLlDk|j$H>-G9>7D6)sGXRI*GKZoEnBAcm z0Ldp$o_y-D#~#}#7K=mqnT*CZ)A7R}I?K)%Cy&O#6_3Z`_~@gL4nO_$(-*AIObyfUG9A(mK zwcl>qwCT@9MMXvc-{YTS1q=MdUk3{Wz-*U=1BeHZuxHPn#j97ZUaeB8)V?y)H0{yr zT8wuZwY`lE8t8S+A>UiAA-y)61n6_PEqxLus?+32$tm$dhuh3%bK}vYM^A3swyheB z>07`!Mh=K@koX9Q2u!G63V{0oEZwiNx*a=qyba(<04o42W#65G zK4F7_O0bSQQedoO92noB0uWVDP%vT3mMx2B%$PANCMHHrQPfb6PLYm|j>hWh>Th=K z+Vzif=gx8S1REGDFoW?0Jz%V0^bWTPhzN}D2m_!55Dg##fIKTJD|*9*4O6F0o2E`o zOdKB*6O*V=D54}1iHzNqhTH9SxZQ5M&1SQ7c6RCw219FYZEfTJ{rj)i*Vo(FN;>e* zMU_r4GswGOd_iF2@KFH42Ok07*qoM6N<$f=+%004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv00000008+zyMF)x010qNS#tmY3labT3lag+-G2N400`GfL_t(o!|j-BaFo>< z$A9O&Y<9ER&F*G_a7kROEfEMRwK9N)I*!UcwzS0y z){%=8Y5)l|Drr>k!c8u6zlOv>zyJv$3E3rPFYkL!KLjH*2@nF<5Bz8L%f9FGJO6W@ z=Q-~S*K|$SbWK-WN-0fBIaEsdp;D^p%x_IeNy(_p%uM6^=4&G< z5?0c-m6Wn6KR^GMhGA&mH)E@!(P$r~)F)C(wSCKG_2l@6Ro=LV)TZ@o)fw#>J9g|H zmuo(~E1jE~nsi;)pV4(a@9V>ryz=rN*t2UV^(SjNe5ir}gKj2}7VO@=dv|Tss#UAQ z;c&F8z+KuZ(=>-U9F9qrWl{3}a<+cC5pQZL9&akUcWmQ>Hf%n0 z=upj72bNL_P17C|LOge>p`O{V&!oI`A1NMBYa7EbXgt+GBpl`^Lx|iU#k@cL3YSYA%}R0AGwDy4gO(W`d`{rV4J#E220ty{NlKXBl{ z;VT9#rPPEF&j=x&s6Kv_SO5GH^>rsnaymIxU&jNZ$C5kYDUyYq*HsRq`q z{uGbLOTtQUwCV`A4jD?S&mYLh$PmkxEn6Fl#hN<uJ-+<#q!t0qFJm*tdHpMei1nNF*3OeE7J$yuAAz4u`nxz)C4o*Y)26 zf!*7;QdqE%v|z9`E7-P8EZVk+#bYs~Z6kz0DM>KcjrW!vJ!9>uu_UZup3{T z$<0bia&vR@#*G{I)5`)Ji^YU#n!gc3{P0NSK^Dw?6HjVtn|1*Y)ALu1~UUn{yD0JVHDN`OWO*83|ft6CpLWn;B#-{aaDK07`E!YhuWm|`ZXxCxe zvJhHZU@0Zty7%DY;tyE+*(v~@fByN&Lx&E{?h5eZnx@@;s-d2FZ@z}6>1bN}%{5KK zw%R`u8XfmBOq2O<&EiDOae(Xd^YedY7)DyBfn%}Q4Zu{TB=0X++G&?M4?E)KyF;xiBt-Q39B@5@{ z_4&^W+Yy{C?Usl<4|E`qM&aUrv1jKF0N1o>)20Lhflx<%+N?xMIZD&C>4|upnJ?#4 zQC5n}b%8V~Allqa?~F_`GF#@_vT;2{OBa)f#W2i{?>V}zb2<{?L`^j#?z)>4kEf5{ z@2@E?F5an>ik%nu^y$++(=?X~A%dTLSWLnEIiz^JTu7#A5{WcYx_1xdW&2sb`ZJ1) zmQ#JK3g?9u3pkxlD#{O#p5Bvw{Rc95@Zdq)wr%^oyuAGD|J~VhHY^^Gzi67~3nyx- znLHtvy4u?Fy7R3xO{b|TLb$P!9zD`Q(>PIm438%jx5sndv;5uMF+3g*GiNU#^SZw5 z-@kw9ph1HsTb6aKU572py4f&{IZ{e<&Ks}r<%adRk}vFz5Q0b~Os`(Onf$vKc;tx* zjJp3pZoc(44waWtTT?@l(|N&h48x@EWF67wW`+#EgY@+DEFr|cb?erZODS9Hz78#4 zZiEo?g%DZWw`}IE*Zz*falst^?LyIYoiXDd=fN?L5J(FWNDFeqkFrQ45^UeH34!P^ zC7!LMq$J8p_tLNbKr*lEs|_1AtY2Ya;mX?D+S=CL1dvibCWN@Bsp&Lx-*~md3e}zx zi8yI#LHZ3C*ydT*fNX-@(@DhR7d4g;Eeo4F>lGRr>M%_+YsQQjzYs#S=4LG#jrI{j zyr`67#qvVR_V2~va9q>`0H*2S_qkPmN@9dcu7ev4px-$-jc;w35$^< zM^2bHapG+bhrb`di3mt0BWkMX{fKmG!4#MJvvY<8fD1sIXpM@B~m=z=AJ!!j;gM%-fRFq%d)7f zC_@Mh*-nYQkc1Fux=wvv9Ve=fqi8uQrfH%SDAoBfbzP^j;wxgY7%3iHKq}xgEX&&M zN=_c~Xx^_`Fy|jM)Yo5hz+EXRDRFzejDKPRK7WA5#>Oh!wk4n$RaI3pLZMK>?RVZ~ z4j6P(`iC^ub z2n6l{`T|Y?;PrY_TrSu3x~}&G+_V&w@7ZRQZQIet#>NxTXtV~X1->~O$^@K%gD!qI lzRXGhQ6L7WE{=8e^004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv00000008+zyMF)x010qNS#tmY3labT3lag+-G2N401C-TL_t(|+U=WrP*m3) z$3J)Pm4|@h0~V4(03V|PBNB~?X***&Nvkb!5+!PC(sYtqCrOQQv^p8n+B%8Rs1Xzc zjuJH}KB9>YXkvmkF^x8e6%|mUV2mO@V1eavclYkS{bL_R9?LH5LX+V;!_58ex#ynW z{c;}P^E-2)7rp33FM837Ui6|DKP+gPHppVJEVo*%-)fqsYMQ1higM9lFs#YR$%z7_ zo{rJyFq_TeRaL#IwF8UAQk9>dKM4o`gr1JqCunSJj8awApmhL?#ZtF)>Cy**z#pX; zilXE?URJeQwcXiUv=^V7slE8zOl^1e7EQHUonlm0R(=KC4fN@*_{44!VX7#KOB_5a zV;wnLv#2o`s4*BgRak&5%Os5(4?u8maP-)*V=HrVa?S!~K>NW9p=nw_K@jo;K?ni3 zaIS>Zm2ZG{=ewONEoSV46Bsl&41lPpsH77oPUK#?bjbiHzK&Dp>jtb=>zjfg+y$Vj zD(l`}j;5-u1+$l__Oighz(J|0sjmT{bi+#G(;i_qn;(!QDFgeY_u=jx9RBn`yZ0Na ztLPgP#K?sE02nlA&`7;r|DU|Pyvx{zh|l1&Sy|IGNz=3fK@buFZr!M4&a*R+uO8K$e@>u=Z^y z@87@wkiC2N&IS76!yfFD2RAh}#rXO8?ZLkM{p*V(?A(^+cKvJLUm+%LDA6&o07OJY zjBaRXICAXRu}Z+=ifa=?~*|KqilSf2EJeZrC`!o>Zi@3cyH0++? zfFKCL0Ow1JNq=WWE6?CAE?zjt{SQ3E;IME2qNAgePMkRLu`j%ZSCzF|t#1i}5CL}2 zuBo6X~!dxm{Gw{zsPPujdUe$;4QS+s-~ zUV4S8GiNbo+;}RkUFG(zo2~AxF;o*6*q3`pIlaY*$jHc|+}zx9yyz{wGFjR^!%0C9 zMgiQsQOTTVr(=@kR`=dJYBbB&Za|Xm{D&%v!ortlbD^}jRUQ9;05Z1iBHZpR>gwvs zEVE(F3McQ^uV3uG zef#F&MQ_n%A)1<+VkJrXi-Ujl&wL7wf8Fl=`iRJO%N!f7woq{VYmOfI+{u%Yl3rN2 zaN!udl(*=z5dQxD8|=Z*&`?iC>S~wwn@n=MWmc=}g&;F+4Gj(T0EnXKH-G;8WxznZ zFmZNC2t`q*3xe<~2j7yJj-k5BWgUy9eJ><8$u8>}s;k(NneOBf5fKj@Jb2J6-lEf$ zHBIZU)9F$j{9N%F4u1SmhxKH+{g+jgjt%3%k3ZsE@fjzdIC0{iCr_Rn2l)4h5LT;o zg&+ue`!(xUEqBW^Se5p>ze$!mtZ(xc%duLm0Qmd+4@gZ-?XtJ%ln@q+Wr8S*vmN}y zUE8@_cFC=}O^prhmYL1&*@DYum-ukkb|)V?bm-%oHf?$g2%?h{r^iBQn$|}Y#f^>z zZ(Ofn$JWhm*H>Ni8h3>N*s*mpH?CJWsF0G9vNSF(P7nBXix7&UEEEJG-oew?taPhO zJJ9H=N0{7yj!BkTf2X&I*}Z%BJfJ_F@D?5kAqwt$!9yO?{?Qg(b zttf5b(+P_I6$M_2ebuGdX@tl z*S$kSy%Df^i+S_r{T-XP@aRm$Z6OpznJJ3mqYj>xv7Q=3wa0_4;uK4jgy}2=?Ut+`3WIv{0Q+x7NW+&z|Pf-2EO04LF@)TQD_w-Xj1$&D~Gw z+0#z`$Rm#|NKQ^3g6#~|dqP;P)|G-Fgxjxeo}rV;7R|*Rp;$YYYtb~VrMC!OyLRpC zc;GEM6vAS${8SXhKREc_oNT@;FY8pm;Cj_?m$p*3r;V&zN!J5d^_0!unOqDQ&iy>MSa+U*oQyL=ih=C;)oB z{ys%fzC3#LXaz2g0oolqilTfjisCP9!5jJGGbz*<3|$V?fv|8rBjQJ)X)491i#puC zdxFpb1IT=T55XZJ0NlKJvrws!U^aQdjW2~!X{KG#3ASfv4t_c$+RA*;r7h%hBwpzMc3887)KtT|4 z1wjY`C_Ym}W(QfP&LA)_kl`ca862+1SXb+Ed)^t8eS3k?_m6WXe50bG?mvJ2d|pXO zNi}T@kyb-PQIsrE6n}5O=1zW6uLY>n>3H^qIZT;013!OTE=87`*uQ5d*;yN1 zI!{j#9TUsC&DqXmaambe-jE?fW&<~AaX@rOkFZ!QKbIuQnRVK;eJfuc{@jfw-N#Gw z7cnJe24b^Jwj}i-Ve}aK_6_1>!Evv(Ra;X-pFXx{0}L22V0cVSOv(QJ`!CXBu5d1& znx^@QqL}I671yqMbb{>;!t@bLdi;rY%af-*MZf-`UTt&N`|nY4^?N6uHf`EphYuef zNsH8;QwT*-77Kzf!oky5ztv@f(-P75#JVgOMUlIsqP^Ot*=#1gS>7TzI5;{dCucq_ z&O~$ovMdjiBaV&Zd4mMj?y z^u<0;>Hs83%EaDPjCFNvN>BB?H8)Ux@mtQ7oNc$f@S9`YsJ!lzwl}4xVyvqJK&R97 znLBsxGGGAqyoF9tl*dF-oam^K^==xqHQqB$EwSvi1#OB^e5Q!iZ~Vg7GgMVoL{WSg zDAmy{w`b@?>g|Vy=I^M}=_G6urlhl4t6pEW;E;Yi_Up&_%@fm{zZDe~eT{7srjnVNIoM<}UGL~S ziXR*5>+5ePBqU4+60jdp==SW{Goi7uv7)Dg>n<3L#v8L{&3YM_0O)Dvu>F&hlSdR5 z7G~De)Re2Ls`hlS-iE5GYE@O$mHhnt9m9qVn+iMxL}53Qj%~PrEx{QI48oS<3!oc5 zW9Vk6fDF_ERX`2)4`W*9BSgDG{@8MrKelhGG;DbYll{-y(*gD3{}ulQc=TE^YG!4N P00000NkvXXu0mjfO0$mL literal 0 HcmV?d00001 diff --git a/NohanaImagePicker/Info.plist b/NohanaImagePicker/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/NohanaImagePicker/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/NohanaImagePicker/ItemListType.swift b/NohanaImagePicker/ItemListType.swift new file mode 100644 index 0000000..d39b2e0 --- /dev/null +++ b/NohanaImagePicker/ItemListType.swift @@ -0,0 +1,24 @@ +// +// ItemListType.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/14. +// Copyright © 2016年 nohana. All rights reserved. +// + +public protocol ItemListType: CollectionType { + typealias Item + var title:String { get } + func update(handler:(() -> Void)?) + subscript (index: Int) -> Item { get } +} + +public protocol AssetType { + var identifier:Int { get } + func image(targetSize:CGSize, handler: (ImageData?) -> Void) +} + +public struct ImageData { + public var image: UIImage + public var info: Dictionary? +} \ No newline at end of file diff --git a/NohanaImagePicker/MomentCell.swift b/NohanaImagePicker/MomentCell.swift new file mode 100644 index 0000000..2014664 --- /dev/null +++ b/NohanaImagePicker/MomentCell.swift @@ -0,0 +1,20 @@ +// +// MomentCell.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/23. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +class MomentCell: AlbumCell { + + override func drawRect(rect: CGRect) { + super.drawRect(rect) + let lineWidth: CGFloat = 1 / UIScreen.mainScreen().scale + ColorConfig.AlbumList.momentCellSeparator.setFill() + UIRectFill(CGRect(x: 16, y: frame.size.height - lineWidth, width: frame.size.width, height:lineWidth)) + } + +} diff --git a/NohanaImagePicker/MomentSectionHeaderView.swift b/NohanaImagePicker/MomentSectionHeaderView.swift new file mode 100644 index 0000000..0f2e22d --- /dev/null +++ b/NohanaImagePicker/MomentSectionHeaderView.swift @@ -0,0 +1,15 @@ +// +// MomentSectionHeaderView.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/09. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +class MomentSectionHeaderView: UICollectionReusableView { + + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var locationLabel: UILabel! +} diff --git a/NohanaImagePicker/MomentViewController.swift b/NohanaImagePicker/MomentViewController.swift new file mode 100644 index 0000000..92f0c43 --- /dev/null +++ b/NohanaImagePicker/MomentViewController.swift @@ -0,0 +1,143 @@ +// +// MomentViewController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/03/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit +import Photos + +@available(iOS 8.0, *) +class MomentViewController: AssetListViewController, ActivityIndicatable { + + var momentAlbumList: PhotoKitAlbumList! + + override func viewDidLoad() { + super.viewDidLoad() + setUpActivityIndicator() + } + + override func updateTitle() { + title = NSLocalizedString("albumlist.moment.title", tableName: "NohanaImagePicker", bundle: nohanaImagePickerController!.assetBundle, comment: "") + } + + override func scrollCollectionViewToInitialPosition() { + guard isFirstAppearance else { + return + } + guard let collectionView = collectionView else { + return + } + let lastSection = momentAlbumList.count - 1 + guard lastSection >= 0 else { + return + } + + let index = NSIndexPath(forItem: momentAlbumList[lastSection].count - 1, inSection: lastSection) + collectionView.scrollToItemAtIndexPath(index, atScrollPosition: .Bottom, animated: false) + isFirstAppearance = false + + } + + // MARK: - UICollectionViewDataSource + + override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { + if let activityIndicator = activityIndicator { + updateVisibilityOfActivityIndicator(activityIndicator) + } + + return momentAlbumList.count + } + + override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return momentAlbumList[section].count + } + + // MARK: - UICollectionViewDelegate + + override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AssetCell", forIndexPath: indexPath) as? AssetCell, + nohanaImagePickerController = nohanaImagePickerController else { + return UICollectionViewCell(frame: CGRectZero) + } + + let asset = momentAlbumList[indexPath.section][indexPath.row] + cell.tag = indexPath.item + cell.update(asset, nohanaImagePickerController: nohanaImagePickerController) + + let imageSize = CGSize( + width: cellSize.width * UIScreen.mainScreen().scale, + height: cellSize.height * UIScreen.mainScreen().scale + ) + asset.image(imageSize) { (imageData) -> Void in + dispatch_async(dispatch_get_main_queue(), { () -> Void in + if let imageData = imageData { + if cell.tag == indexPath.item { + cell.imageView.image = imageData.image + } + } + }) + } + return (nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, assetListViewController: self, cell: cell, indexPath: indexPath, photoKitAsset: asset.originalAsset)) ?? cell + } + + override func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { + switch kind { + case UICollectionElementKindSectionHeader: + let album = momentAlbumList[indexPath.section] + guard let header = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "MomentHeader", forIndexPath: indexPath) as? MomentSectionHeaderView else { + fatalError("failed to create MomentHeader") + } + header.locationLabel.text = album.title + if let date = album.date { + let formatter = NSDateFormatter() + formatter.dateStyle = .LongStyle + formatter.timeStyle = NSDateFormatterStyle.NoStyle + header.dateLabel.text = formatter.stringFromDate(date) + } else { + header.dateLabel.text = "" + } + return header + default: + fatalError("failed to create MomentHeader") + } + } + + // MARK: - ActivityIndicatable + + var activityIndicator: UIActivityIndicatorView? + var isLoading = true + + func setUpActivityIndicator() { + activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray) + let screenRect = Size.screenRectWithoutAppBar(self) + activityIndicator?.center = CGPoint(x: screenRect.size.width / 2, y: screenRect.size.height / 2) + activityIndicator?.startAnimating() + } + + func isProgressing() -> Bool { + return isLoading + } + + // MARK: - Storyboard + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + guard let selectedIndexPath = collectionView?.indexPathsForSelectedItems()?.first else { + return + } + + let assetListDetailViewController = segue.destinationViewController as! AssetDetailListViewController + assetListDetailViewController.photoKitAssetList = momentAlbumList[selectedIndexPath.section] + assetListDetailViewController.nohanaImagePickerController = nohanaImagePickerController + assetListDetailViewController.selectedIndexPath = NSIndexPath(forItem: selectedIndexPath.item, inSection: 0) + } + + // MARK: - IBAction + + @IBAction override func didPushDone(sender: AnyObject) { + super.didPushDone(sender) + } + +} \ No newline at end of file diff --git a/NohanaImagePicker/NohanaImagePicker.h b/NohanaImagePicker/NohanaImagePicker.h new file mode 100644 index 0000000..8c1a226 --- /dev/null +++ b/NohanaImagePicker/NohanaImagePicker.h @@ -0,0 +1,18 @@ +// +// NohanaImagePicker.h +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +#import + +//! Project version number for NohanaImagePicker. +FOUNDATION_EXPORT double NohanaImagePickerVersionNumber; + +//! Project version string for NohanaImagePicker. +FOUNDATION_EXPORT const unsigned char NohanaImagePickerVersionString[]; + +// In this header, you should import all the public headers of your framework +// using statements like #import diff --git a/NohanaImagePicker/NohanaImagePicker.storyboard b/NohanaImagePicker/NohanaImagePicker.storyboard new file mode 100644 index 0000000..af92788 --- /dev/null +++ b/NohanaImagePicker/NohanaImagePicker.storyboard @@ -0,0 +1,442 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NohanaImagePicker/NohanaImagePickerController.swift b/NohanaImagePicker/NohanaImagePickerController.swift new file mode 100644 index 0000000..7354152 --- /dev/null +++ b/NohanaImagePicker/NohanaImagePickerController.swift @@ -0,0 +1,121 @@ +// +// NohanaImagePickerController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/09. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit +import Photos + +public enum MediaType: Int { + case Any = 0, Photo, Video +} + +@available(iOS 8.0, *) +@objc public protocol NohanaImagePickerControllerDelegate { + func nohanaImagePickerDidCancel(picker: NohanaImagePickerController) + func nohanaImagePicker(picker: NohanaImagePickerController, didFinishPickingPhotoKitAssets pickedAssts :[PHAsset]) + optional func nohanaImagePicker(picker: NohanaImagePickerController, willPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool + optional func nohanaImagePicker(picker: NohanaImagePickerController, didPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) + optional func nohanaImagePicker(picker: NohanaImagePickerController, willDropPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool + optional func nohanaImagePicker(picker: NohanaImagePickerController, didDropPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) + optional func nohanaImagePicker(picker: NohanaImagePickerController, didSelectPhotoKitAsset asset: PHAsset) + optional func nohanaImagePicker(picker: NohanaImagePickerController, didSelectPhotoKitAssetList assetList: PHAssetCollection) + optional func nohanaImagePicker(picker: NohanaImagePickerController, assetListViewController: UICollectionViewController, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell + optional func nohanaImagePicker(picker: NohanaImagePickerController, assetDetailListViewController: UICollectionViewController, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell + optional func nohanaImagePicker(picker: NohanaImagePickerController, assetDetailListViewController: UICollectionViewController, didChangeAssetDetailPage indexPath: NSIndexPath, photoKitAsset: PHAsset) + +} + +@available(iOS 8.0, *) +public class NohanaImagePickerController: UIViewController { + + public var maximumNumberOfSelection: Int = 21 // set 0 to set no limit + public var numberOfColumnsInPortrait: Int = 4 + public var numberOfColumnsInLandscape: Int = 7 + public weak var delegate: NohanaImagePickerControllerDelegate? + public var shouldShowMoment: Bool = true + public var shouldShowEmptyAlbum: Bool = false + public var toolbarHidden: Bool = false + public var canPickAsset = { (asset:AssetType) -> Bool in + return true + } + lazy var assetBundle:NSBundle = NSBundle(forClass: self.dynamicType) + let pickedAssetList: PickedAssetList + let mediaType: MediaType + private let assetCollectionSubtypes: [PHAssetCollectionSubtype] + + public init() { + assetCollectionSubtypes = [ + .AlbumRegular, + .AlbumSyncedEvent, + .AlbumSyncedFaces, + .AlbumSyncedAlbum, + .AlbumImported, + .AlbumMyPhotoStream, + .AlbumCloudShared, + .SmartAlbumGeneric, + .SmartAlbumFavorites, + .SmartAlbumRecentlyAdded, + .SmartAlbumUserLibrary + ] + mediaType = .Photo + pickedAssetList = PickedAssetList() + super.init(nibName: nil, bundle: nil) + self.pickedAssetList.nohanaImagePickerController = self + } + + public init(assetCollectionSubtypes: [PHAssetCollectionSubtype], mediaType: MediaType) { + self.assetCollectionSubtypes = assetCollectionSubtypes + self.mediaType = mediaType + pickedAssetList = PickedAssetList() + super.init(nibName: nil, bundle: nil) + self.pickedAssetList.nohanaImagePickerController = self + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public func viewDidLoad() { + super.viewDidLoad() + + // show albumListViewController + let storyboard = UIStoryboard(name: "NohanaImagePicker", bundle: assetBundle) + guard let navigationController = storyboard.instantiateInitialViewController() as? UINavigationController else { + fatalError("navigationController init failed.") + } + addChildViewController(navigationController) + view.addSubview(navigationController.view) + navigationController.didMoveToParentViewController(self) + + // setup albumListViewController + guard let albumListViewController = navigationController.topViewController as? AlbumListViewController else { + fatalError("albumListViewController is not topViewController.") + } + albumListViewController.photoKitAlbumList = + PhotoKitAlbumList( + assetCollectionTypes: [.SmartAlbum, .Album], + assetCollectionSubtypes: assetCollectionSubtypes, + mediaType: mediaType, + shouldShowEmptyAlbum: shouldShowEmptyAlbum, + handler: { + dispatch_async(dispatch_get_main_queue(), { () -> Void in + albumListViewController.isLoading = false + albumListViewController.tableView.reloadData() + }) + }) + albumListViewController.nohanaImagePickerController = self + } + + public func pickAsset(asset: AssetType) { + pickedAssetList.pickAsset(asset) + } + + public func dropAsset(asset: AssetType) { + pickedAssetList.dropAsset(asset) + } +} + diff --git a/NohanaImagePicker/NotificationInfo.swift b/NohanaImagePicker/NotificationInfo.swift new file mode 100644 index 0000000..e7771b6 --- /dev/null +++ b/NohanaImagePicker/NotificationInfo.swift @@ -0,0 +1,20 @@ +// +// NotificationInfo.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/19. +// Copyright © 2016年 nohana. All rights reserved. +// + +struct NotificationInfo { + struct Asset { + struct PhotoKit { + static let didPick = "jp.co.nohana.NotificationName.Asset.PhotoKit.didPick" + static let didPickUserInfoKeyAsset = "asset" + static let didPickUserInfoKeyPickedAssetsCount = "pickedAssetsCount" + static let didDrop = "jp.co.nohana.NotificationName.Asset.PhotoKit.didDrop" + static let didDropUserInfoKeyAsset = "asset" + static let didDropUserInfoKeyPickedAssetsCount = "pickedAssetsCount" + } + } +} \ No newline at end of file diff --git a/NohanaImagePicker/PhotoKitAlbumList.swift b/NohanaImagePicker/PhotoKitAlbumList.swift new file mode 100644 index 0000000..054dac7 --- /dev/null +++ b/NohanaImagePicker/PhotoKitAlbumList.swift @@ -0,0 +1,98 @@ +// +// PhotoKitAlbumList.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/10. +// Copyright © 2016年 nohana. All rights reserved. +// + +import Photos + +@available(iOS 8.0, *) +public class PhotoKitAlbumList: ItemListType { + + private var albumList:[Item] = [] + private let assetCollectionTypes: [PHAssetCollectionType] + private let assetCollectionSubtypes: [PHAssetCollectionSubtype] + private let mediaType: MediaType + private var shouldShowEmptyAlbum: Bool + + // MARK: - init + + init(assetCollectionTypes: [PHAssetCollectionType], assetCollectionSubtypes: [PHAssetCollectionSubtype], mediaType: MediaType, shouldShowEmptyAlbum: Bool, handler:(() -> Void)?) { + self.assetCollectionTypes = assetCollectionTypes + self.assetCollectionSubtypes = assetCollectionSubtypes + self.mediaType = mediaType + self.shouldShowEmptyAlbum = shouldShowEmptyAlbum + update { () -> Void in + if let handler = handler { + handler() + } + } + } + + // MARK: - ItemListType + + public typealias Item = PhotoKitAssetList + + public var title:String { + get { + return "PhotoKit" + } + } + + public func update(handler:(() -> Void)?) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in + var albumListFetchResult: [PHFetchResult] = [] + for type in self.assetCollectionTypes { + albumListFetchResult = albumListFetchResult + [PHAssetCollection.fetchAssetCollectionsWithType(type, subtype: .Any, options: nil)] + } + + self.albumList = [] + var tmpAlbumList:[Item] = [] + let isAssetCollectionSubtypeAny = self.assetCollectionSubtypes.contains(.Any) + for fetchResult in albumListFetchResult { + fetchResult.enumerateObjectsUsingBlock { (album, index, stop) -> Void in + guard let album = album as? PHAssetCollection else { + return + } + if self.assetCollectionSubtypes.contains(album.assetCollectionSubtype) || isAssetCollectionSubtypeAny { + if self.shouldShowEmptyAlbum || PHAsset.fetchAssetsInAssetCollection(album, options: PhotoKitAssetList.fetchOptions(self.mediaType)).count != 0 { + tmpAlbumList.append(PhotoKitAssetList(album: album, mediaType: self.mediaType)) + } + } + } + } + if self.assetCollectionTypes == [.Moment] { + self.albumList = tmpAlbumList.sort{ $0.date?.timeIntervalSince1970 < $1.date?.timeIntervalSince1970 } + } else { + self.albumList = tmpAlbumList + } + + if let handler = handler { + handler() + } + } + } + + public subscript (index: Int) -> Item { + get { + return albumList[index] as Item + } + } + + // MARK: - CollectionType + + public var startIndex: Int { + get { + return albumList.startIndex + } + } + + public var endIndex: Int { + get { + return albumList.endIndex + } + } + +} diff --git a/NohanaImagePicker/PhotoKitAsset.swift b/NohanaImagePicker/PhotoKitAsset.swift new file mode 100644 index 0000000..2d9a4c2 --- /dev/null +++ b/NohanaImagePicker/PhotoKitAsset.swift @@ -0,0 +1,50 @@ +// +// PhotoKitAsset.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// + +import Photos + +@available(iOS 8.0, *) +public class PhotoKitAsset :AssetType { + + let asset: PHAsset + + public init(asset: PHAsset) { + self.asset = asset + } + + public var originalAsset: PHAsset { + get { + return asset as PHAsset + } + } + + // MARK: - AssetType + + public var identifier:Int { + get { + return asset.localIdentifier.hash + } + } + + public func image(targetSize:CGSize, handler: (ImageData?) -> Void) { + let option = PHImageRequestOptions() + option.networkAccessAllowed = true + + PHImageManager.defaultManager().requestImageForAsset( + self.asset, + targetSize: targetSize, + contentMode: .AspectFit, + options: option ) { (image, info) -> Void in + guard let image = image else { + handler(nil) + return + } + handler(ImageData(image: image, info: info)) + } + } +} diff --git a/NohanaImagePicker/PhotoKitAssetList.swift b/NohanaImagePicker/PhotoKitAssetList.swift new file mode 100644 index 0000000..8d9471c --- /dev/null +++ b/NohanaImagePicker/PhotoKitAssetList.swift @@ -0,0 +1,79 @@ +// +// PhotoKitAssetList.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/11. +// Copyright © 2016年 nohana. All rights reserved. +// +import Photos + +@available(iOS 8.0, *) +public class PhotoKitAssetList :ItemListType { + + private let mediaType: MediaType + public let assetList: PHAssetCollection + private var fetchResult: PHFetchResult! + + init(album: PHAssetCollection, mediaType: MediaType) { + self.assetList = album + self.mediaType = mediaType + update() + } + + // MARK: - ItemListType + + public typealias Item = PhotoKitAsset + + public var title: String { + get{ + return assetList.localizedTitle ?? "" + } + } + + public var date: NSDate? { + get { + return assetList.startDate + } + } + + class func fetchOptions(mediaType: MediaType) -> PHFetchOptions { + let options = PHFetchOptions() + switch mediaType { + case .Photo: + options.predicate = NSPredicate(format: "mediaType == %ld", PHAssetMediaType.Image.rawValue) + default: + fatalError("not supported .Video and .Any yet") + } + return options + } + + public func update(handler: (() -> Void)? = nil) { + fetchResult = PHAsset.fetchAssetsInAssetCollection(assetList, options: PhotoKitAssetList.fetchOptions(mediaType)) + if let handler = handler { + handler() + } + } + + public subscript (index: Int) -> Item { + get { + guard let asset = fetchResult[index] as? PHAsset else { + fatalError("invalid index") + } + return Item(asset: asset) + } + } + + // MARK: - CollectionType + + public var startIndex: Int { + get { + return 0 + } + } + + public var endIndex: Int { + get { + return fetchResult.count + } + } +} diff --git a/NohanaImagePicker/PickedAssetList.swift b/NohanaImagePicker/PickedAssetList.swift new file mode 100644 index 0000000..524bce7 --- /dev/null +++ b/NohanaImagePicker/PickedAssetList.swift @@ -0,0 +1,124 @@ +// +// PickedAssetList.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/17. +// Copyright © 2016年 nohana. All rights reserved. +// + +import Foundation + +@available(iOS 8.0, *) +class PickedAssetList: ItemListType { + + var assetlist: Array = [] + weak var nohanaImagePickerController: NohanaImagePickerController? + + // MARK: - ItemListType + + typealias Item = AssetType + + var title: String { + get { + return "Selected Assets" + } + } + + func update(handler:(() -> Void)?) { + fatalError("not supported") + } + + + subscript (index: Int) -> Item { + get { + return assetlist[index] + } + } + + // MARK: - CollectionType + + var startIndex: Int { + get { + return 0 + } + } + + var endIndex: Int { + get { + return assetlist.count + } + } + + // MARK: - Manage assetlist + + func pickAsset(asset: AssetType) -> Bool { + guard !isPicked(asset) else { + return false + } + guard let nohanaImagePickerController = nohanaImagePickerController else { + return false + } + let assetsCountBeforePicking = self.count + if asset is PhotoKitAsset { + if let canPick = nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, willPickPhotoKitAsset: (asset as! PhotoKitAsset).originalAsset, pickedAssetsCount: assetsCountBeforePicking) + where !canPick { + return false + } + } + guard nohanaImagePickerController.maximumNumberOfSelection == 0 || assetsCountBeforePicking < nohanaImagePickerController.maximumNumberOfSelection else { + return false + } + assetlist.append(asset) + let assetsCountAfterPicking = self.count + if asset is PhotoKitAsset { + let originalAsset = (asset as! PhotoKitAsset).originalAsset + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didPickPhotoKitAsset: originalAsset, pickedAssetsCount: assetsCountAfterPicking) + NSNotificationCenter.defaultCenter().postNotification( + NSNotification( + name: NotificationInfo.Asset.PhotoKit.didPick, + object: nohanaImagePickerController, + userInfo: [ + NotificationInfo.Asset.PhotoKit.didPickUserInfoKeyAsset : originalAsset, + NotificationInfo.Asset.PhotoKit.didPickUserInfoKeyPickedAssetsCount : assetsCountAfterPicking + ] + ) + ) + } + return true + + } + + func dropAsset(asset: AssetType) -> Bool { + guard let nohanaImagePickerController = nohanaImagePickerController else { + return false + } + let assetsCountBeforeDropping = self.count + if asset is PhotoKitAsset { + if let canDrop = nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, willDropPhotoKitAsset: (asset as! PhotoKitAsset).originalAsset, pickedAssetsCount: assetsCountBeforeDropping) where !canDrop { + return false + } + } + assetlist = assetlist.filter{ $0.identifier != asset.identifier } + let assetsCountAfterDropping = self.count + if asset is PhotoKitAsset { + let originalAsset = (asset as! PhotoKitAsset).originalAsset + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didDropPhotoKitAsset: originalAsset, pickedAssetsCount: assetsCountAfterDropping) + NSNotificationCenter.defaultCenter().postNotification( + NSNotification( + name: NotificationInfo.Asset.PhotoKit.didDrop, + object: nohanaImagePickerController, + userInfo: [ + NotificationInfo.Asset.PhotoKit.didDropUserInfoKeyAsset : originalAsset, + NotificationInfo.Asset.PhotoKit.didDropUserInfoKeyPickedAssetsCount : assetsCountAfterDropping + ] + ) + ) + } + return true + } + + func isPicked(asset: AssetType) -> Bool { + return assetlist.contains{ $0.identifier == asset.identifier } + } + +} \ No newline at end of file diff --git a/NohanaImagePicker/Size.swift b/NohanaImagePicker/Size.swift new file mode 100644 index 0000000..ed56025 --- /dev/null +++ b/NohanaImagePicker/Size.swift @@ -0,0 +1,40 @@ +// +// Size.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/14. +// Copyright © 2016年 nohana. All rights reserved. +// + +struct Size { + + static let statusBarHeight = UIApplication.sharedApplication().statusBarFrame.size.height + + static func navigationBarHeight(viewController: UIViewController) -> CGFloat { + return viewController.navigationController?.navigationBar.frame.size.height ?? CGFloat(44) + } + + static func appBarHeight(viewController: UIViewController) -> CGFloat { + return statusBarHeight + navigationBarHeight(viewController) + } + + static func toolbarHeight(viewController: UIViewController) -> CGFloat { + guard let navigationController = viewController.navigationController else { + return 0 + } + guard !navigationController.toolbarHidden else { + return 0 + } + return navigationController.toolbar.frame.size.height + } + + static func screenRectWithoutAppBar(viewController: UIViewController) -> CGRect { + let appBarHeight = Size.appBarHeight(viewController) + let toolbarHeight = Size.toolbarHeight(viewController) + return CGRect( + x: 0, + y: appBarHeight, + width: UIScreen.mainScreen().bounds.width, + height: UIScreen.mainScreen().bounds.height - appBarHeight - toolbarHeight) + } +} diff --git a/NohanaImagePicker/SwipeInteractionController.swift b/NohanaImagePicker/SwipeInteractionController.swift new file mode 100644 index 0000000..737f7c3 --- /dev/null +++ b/NohanaImagePicker/SwipeInteractionController.swift @@ -0,0 +1,25 @@ +// +// SwipeInteractionController.swift +// NohanaImagePicker +// +// Created by kazushi.hara on 2016/02/21. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +@available(iOS 8.0, *) +class SwipeInteractionController: UIPercentDrivenInteractiveTransition { + + var viewController: UIViewController? + + func attachToViewController(viewController: UIViewController) { + guard viewController.navigationController?.viewControllers.count > 1 else { + return + } + let target = viewController.navigationController?.valueForKey("_cachedInteractionController") + let gesture = UIScreenEdgePanGestureRecognizer(target: target, action: "handleNavigationTransition:") + gesture.edges = .Left + viewController.view.addGestureRecognizer(gesture) + } +} \ No newline at end of file diff --git a/NohanaImagePicker/en.lproj/NohanaImagePicker.strings b/NohanaImagePicker/en.lproj/NohanaImagePicker.strings new file mode 100644 index 0000000..547da45 --- /dev/null +++ b/NohanaImagePicker/en.lproj/NohanaImagePicker.strings @@ -0,0 +1,14 @@ +/* + NohanaImagePicker.strings + NohanaImagePicker + + Created by kazushi.hara on 2016/02/15. + Copyright © 2016年 nohana. All rights reserved. +*/ + +"albumlist.title" = "Photos"; +"albumlist.empty.message" = "No Photos"; +"albumlist.empty.description" = "Take some photos with Camera app."; +"albumlist.moment.title" = "Moment"; +"toolbar.title.nolimit" = "Selected Items: %ld"; +"toolbar.title.haslimit" = "Selected Items: %ld / %ld"; diff --git a/NohanaImagePicker/ja.lproj/NohanaImagePicker.strings b/NohanaImagePicker/ja.lproj/NohanaImagePicker.strings new file mode 100644 index 0000000..389c186 --- /dev/null +++ b/NohanaImagePicker/ja.lproj/NohanaImagePicker.strings @@ -0,0 +1,14 @@ +/* + NohanaImagePicker.strings + NohanaImagePicker + + Created by kazushi.hara on 2016/02/15. + Copyright © 2016年 nohana. All rights reserved. +*/ + +"albumlist.title" = "アルバム"; +"albumlist.empty.message" = "アルバムに\n写真がありません"; +"albumlist.empty.description" = "カメラで写真を撮影しましょう"; +"albumlist.moment.title" = "日付から選択"; +"toolbar.title.nolimit" = "%ld枚選択中"; +"toolbar.title.haslimit" = "%ld枚選択中(最大%ld枚)"; diff --git a/NohanaImagePickerSample/AppDelegate.swift b/NohanaImagePickerSample/AppDelegate.swift new file mode 100644 index 0000000..263e01f --- /dev/null +++ b/NohanaImagePickerSample/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// NohanaImagePickerSample +// +// Created by kazushi.hara on 2016/02/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/NohanaImagePickerSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/NohanaImagePickerSample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..eeea76c --- /dev/null +++ b/NohanaImagePickerSample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,73 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/NohanaImagePickerSample/Base.lproj/LaunchScreen.storyboard b/NohanaImagePickerSample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..2e721e1 --- /dev/null +++ b/NohanaImagePickerSample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NohanaImagePickerSample/Base.lproj/Main.storyboard b/NohanaImagePickerSample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..bd6e4ab --- /dev/null +++ b/NohanaImagePickerSample/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NohanaImagePickerSample/Info.plist b/NohanaImagePickerSample/Info.plist new file mode 100644 index 0000000..40c6215 --- /dev/null +++ b/NohanaImagePickerSample/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/NohanaImagePickerSample/ViewController.swift b/NohanaImagePickerSample/ViewController.swift new file mode 100644 index 0000000..2cd7d3e --- /dev/null +++ b/NohanaImagePickerSample/ViewController.swift @@ -0,0 +1,85 @@ +// +// ViewController.swift +// NohanaImagePickerSample +// +// Created by kazushi.hara on 2016/02/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +import UIKit +import Photos +import NohanaImagePicker + +class ViewController: UIViewController, NohanaImagePickerControllerDelegate { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + } + + override func viewDidAppear(animated: Bool) { + let picker = NohanaImagePickerController() + picker.delegate = self + picker.maximumNumberOfSelection = 4 + picker.canPickAsset = { (asset:AssetType) -> Bool in + return asset.identifier % 10 != 0 + } + presentViewController(picker, animated: true, completion: nil) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + // MARK: - NohanaImagePickerControllerDelegate + + func nohanaImagePickerDidCancel(picker: NohanaImagePickerController) { + picker.dismissViewControllerAnimated(true, completion: nil) + } + + func nohanaImagePicker(picker: NohanaImagePickerController, didFinishPickingPhotoKitAssets pickedAssts :[PHAsset]) { + picker.dismissViewControllerAnimated(true, completion: nil) + } + + func willPickPhotoKitAsset(asset: PHAsset, pickedAssetsCount: Int) -> Bool { + print("func:\(__FUNCTION__), asset: \(asset), pickedAssetsCount: \(pickedAssetsCount)") + return true + } + + func didPickPhotoKitAsset(asset: PHAsset, pickedAssetsCount: Int) { + print("func:\(__FUNCTION__), asset: \(asset), pickedAssetsCount: \(pickedAssetsCount)") + } + + func willDropPhotoKitAsset(asset: PHAsset, pickedAssetsCount: Int) -> Bool { + print("func:\(__FUNCTION__), asset: \(asset), pickedAssetsCount: \(pickedAssetsCount)") + return true + } + + func didDropPhotoKitAsset(asset: PHAsset, pickedAssetsCount: Int) { + print("func:\(__FUNCTION__), asset: \(asset), pickedAssetsCount: \(pickedAssetsCount)") + } + + func didSelectPhotoKitAsset(asset: PHAsset) { + print("func:\(__FUNCTION__), assetList: \(asset)") + } + + func didSelectPhotoKitAssetList(assetList: PHAssetCollection) { + print("func:\(__FUNCTION__), assetList: \(assetList)") + } + + func assetListView(collectionView: UICollectionView, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell { + print("func:\(__FUNCTION__), cell:\(cell), indexPath: \(indexPath), photoKitAsset: \(photoKitAsset)") + return cell + } + + func assetDetailListView(collectionView: UICollectionView, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell { + print("func:\(__FUNCTION__), cell:\(cell), indexPath: \(indexPath), photoKitAsset: \(photoKitAsset)") + return cell + } + + func didChangeAssetDetailPage(indexPath: NSIndexPath, photoKitAsset: PHAsset) { + print("func:\(__FUNCTION__), indexPath: \(indexPath), photoKitAsset: \(photoKitAsset)") + } +} + diff --git a/NohanaImagePickerSampleTests/Info.plist b/NohanaImagePickerSampleTests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/NohanaImagePickerSampleTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/NohanaImagePickerSampleTests/NohanaImagePickerSampleTests.swift b/NohanaImagePickerSampleTests/NohanaImagePickerSampleTests.swift new file mode 100644 index 0000000..ee42e3a --- /dev/null +++ b/NohanaImagePickerSampleTests/NohanaImagePickerSampleTests.swift @@ -0,0 +1,36 @@ +// +// NohanaImagePickerSampleTests.swift +// NohanaImagePickerSampleTests +// +// Created by kazushi.hara on 2016/02/08. +// Copyright © 2016年 nohana. All rights reserved. +// + +import XCTest +@testable import NohanaImagePickerSample + +class NohanaImagePickerSampleTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() { + // This is an example of a performance test case. + self.measureBlock { + // Put the code you want to measure the time of here. + } + } + +} From 7073a8591ff99520acd60694ebe9cabe606d9e2f Mon Sep 17 00:00:00 2001 From: haranicle Date: Fri, 15 Apr 2016 18:19:41 +0900 Subject: [PATCH 2/3] add nohanaImagePickerDidSelectMoment --- NohanaImagePicker/AlbumListViewController.swift | 4 ++++ NohanaImagePicker/NohanaImagePickerController.swift | 1 + 2 files changed, 5 insertions(+) diff --git a/NohanaImagePicker/AlbumListViewController.swift b/NohanaImagePicker/AlbumListViewController.swift index 03831ce..26ab3a5 100644 --- a/NohanaImagePicker/AlbumListViewController.swift +++ b/NohanaImagePicker/AlbumListViewController.swift @@ -43,6 +43,10 @@ class AlbumListViewController: UITableViewController, EmptyIndicatable, Activity guard let nohanaImagePickerController = nohanaImagePickerController else { return } + if isMomentRow(indexPath) { + nohanaImagePickerController.delegate?.nohanaImagePickerDidSelectMoment?(nohanaImagePickerController) + } + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didSelectPhotoKitAssetList: photoKitAlbumList[exactRow(indexPath)].assetList) } diff --git a/NohanaImagePicker/NohanaImagePickerController.swift b/NohanaImagePicker/NohanaImagePickerController.swift index 7354152..6a54627 100644 --- a/NohanaImagePicker/NohanaImagePickerController.swift +++ b/NohanaImagePicker/NohanaImagePickerController.swift @@ -17,6 +17,7 @@ public enum MediaType: Int { @objc public protocol NohanaImagePickerControllerDelegate { func nohanaImagePickerDidCancel(picker: NohanaImagePickerController) func nohanaImagePicker(picker: NohanaImagePickerController, didFinishPickingPhotoKitAssets pickedAssts :[PHAsset]) + optional func nohanaImagePickerDidSelectMoment(picker: NohanaImagePickerController) -> Void optional func nohanaImagePicker(picker: NohanaImagePickerController, willPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool optional func nohanaImagePicker(picker: NohanaImagePickerController, didPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) optional func nohanaImagePicker(picker: NohanaImagePickerController, willDropPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool From c9c29f148021e62a1b1573c414078ac05dee81e4 Mon Sep 17 00:00:00 2001 From: haranicle Date: Mon, 18 Apr 2016 12:01:51 +0900 Subject: [PATCH 3/3] bugfix crash when call didSelectPhotoKitAssetList --- NohanaImagePicker/AlbumListViewController.swift | 4 ++-- NohanaImagePicker/NohanaImagePickerController.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NohanaImagePicker/AlbumListViewController.swift b/NohanaImagePicker/AlbumListViewController.swift index 26ab3a5..6a30fd1 100644 --- a/NohanaImagePicker/AlbumListViewController.swift +++ b/NohanaImagePicker/AlbumListViewController.swift @@ -45,9 +45,9 @@ class AlbumListViewController: UITableViewController, EmptyIndicatable, Activity } if isMomentRow(indexPath) { nohanaImagePickerController.delegate?.nohanaImagePickerDidSelectMoment?(nohanaImagePickerController) + } else { + nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didSelectPhotoKitAssetList: photoKitAlbumList[exactRow(indexPath)].assetList) } - - nohanaImagePickerController.delegate?.nohanaImagePicker?(nohanaImagePickerController, didSelectPhotoKitAssetList: photoKitAlbumList[exactRow(indexPath)].assetList) } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { diff --git a/NohanaImagePicker/NohanaImagePickerController.swift b/NohanaImagePicker/NohanaImagePickerController.swift index 6a54627..7bd4726 100644 --- a/NohanaImagePicker/NohanaImagePickerController.swift +++ b/NohanaImagePicker/NohanaImagePickerController.swift @@ -17,13 +17,13 @@ public enum MediaType: Int { @objc public protocol NohanaImagePickerControllerDelegate { func nohanaImagePickerDidCancel(picker: NohanaImagePickerController) func nohanaImagePicker(picker: NohanaImagePickerController, didFinishPickingPhotoKitAssets pickedAssts :[PHAsset]) - optional func nohanaImagePickerDidSelectMoment(picker: NohanaImagePickerController) -> Void optional func nohanaImagePicker(picker: NohanaImagePickerController, willPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool optional func nohanaImagePicker(picker: NohanaImagePickerController, didPickPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) optional func nohanaImagePicker(picker: NohanaImagePickerController, willDropPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) -> Bool optional func nohanaImagePicker(picker: NohanaImagePickerController, didDropPhotoKitAsset asset: PHAsset, pickedAssetsCount: Int) optional func nohanaImagePicker(picker: NohanaImagePickerController, didSelectPhotoKitAsset asset: PHAsset) optional func nohanaImagePicker(picker: NohanaImagePickerController, didSelectPhotoKitAssetList assetList: PHAssetCollection) + optional func nohanaImagePickerDidSelectMoment(picker: NohanaImagePickerController) -> Void optional func nohanaImagePicker(picker: NohanaImagePickerController, assetListViewController: UICollectionViewController, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell optional func nohanaImagePicker(picker: NohanaImagePickerController, assetDetailListViewController: UICollectionViewController, cell: UICollectionViewCell, indexPath: NSIndexPath, photoKitAsset: PHAsset) -> UICollectionViewCell optional func nohanaImagePicker(picker: NohanaImagePickerController, assetDetailListViewController: UICollectionViewController, didChangeAssetDetailPage indexPath: NSIndexPath, photoKitAsset: PHAsset)