add project

This commit is contained in:
haranicle 2016-04-15 17:31:31 +09:00
parent 2cc3c49708
commit 87d26c586b
63 changed files with 3797 additions and 0 deletions

65
.gitignore vendored Normal file
View File

@ -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

View File

@ -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 = "<group>"; };
F202573B1C7418920069B33A /* PickedAssetList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickedAssetList.swift; sourceTree = "<group>"; };
F2131F421C79615700797887 /* SwipeInteractionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwipeInteractionController.swift; sourceTree = "<group>"; };
F218D7D51C6B3D22001FCED1 /* PhotoKitAlbumList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAlbumList.swift; sourceTree = "<group>"; };
F218D7DD1C6C3A5B001FCED1 /* AlbumCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumCell.swift; sourceTree = "<group>"; };
F23554251C69D19C00796DCA /* NohanaImagePicker.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NohanaImagePicker.storyboard; sourceTree = "<group>"; };
F23554271C69D5DB00796DCA /* NohanaImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NohanaImagePickerController.swift; sourceTree = "<group>"; };
F237249A1C6DCF96005D1E8A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
F23CC89D1CB745C800BCE443 /* ActivityIndicatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityIndicatable.swift; sourceTree = "<group>"; };
F24EB68F1C68AEED0002EC86 /* AlbumListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumListViewController.swift; sourceTree = "<group>"; };
F25C10C11C8ED9BF007453C3 /* MomentViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentViewController.swift; sourceTree = "<group>"; };
F25C69871CA23A0A005935D6 /* MomentCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentCell.swift; sourceTree = "<group>"; };
F25C698F1CA27311005935D6 /* EmptyIndicatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyIndicatable.swift; sourceTree = "<group>"; };
F25C69911CA28728005935D6 /* AlbumListEmptyIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumListEmptyIndicator.swift; sourceTree = "<group>"; };
F25C699A1CA2B79A005935D6 /* ColorConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorConfig.swift; sourceTree = "<group>"; };
F26775DD1C701FA7002E786C /* ItemListType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ItemListType.swift; sourceTree = "<group>"; };
F26775E01C7046C7002E786C /* ExpandingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpandingAnimationController.swift; sourceTree = "<group>"; };
F26775E41C70574F002E786C /* AnimatableNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatableNavigationController.swift; sourceTree = "<group>"; };
F26775E71C7073CD002E786C /* Size.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Size.swift; sourceTree = "<group>"; };
F26775E91C71645A002E786C /* ContractingAnimationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContractingAnimationController.swift; sourceTree = "<group>"; };
F27029CC1C71C43A001647AB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/NohanaImagePicker.strings; sourceTree = "<group>"; };
F27029CF1C71C4FE001647AB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/NohanaImagePicker.strings; sourceTree = "<group>"; };
F28F4AC21C6C49EE00B7D725 /* PhotoKitAssetList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAssetList.swift; sourceTree = "<group>"; };
F28F4AC41C6C59A500B7D725 /* PhotoKitAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoKitAsset.swift; sourceTree = "<group>"; };
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 = "<group>"; };
F2C08D791C68651900B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
F2C08D961C688FC400B00181 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
F2C08D991C688FC400B00181 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
F2C08D9B1C688FC400B00181 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F2C08D9E1C688FC400B00181 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
F2C08DA01C688FC400B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
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 = "<group>"; };
F2C08DAB1C688FC400B00181 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F2DA29761C7749D600B0A8E3 /* NotificationInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationInfo.swift; sourceTree = "<group>"; };
F2DF3B141C6C76E500C1C0E4 /* AssetListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetListViewController.swift; sourceTree = "<group>"; };
F2DF3B161C6C868E00C1C0E4 /* AssetCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetCell.swift; sourceTree = "<group>"; };
F2DF3B2A1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDetailListViewController.swift; sourceTree = "<group>"; };
F2DF3B2C1C6D780100C1C0E4 /* AssetDetailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetDetailCell.swift; sourceTree = "<group>"; };
F2FE1F761C901D9400FDBE7B /* MomentSectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MomentSectionHeaderView.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
};
F235541F1C698BE900796DCA /* Resources */ = {
isa = PBXGroup;
children = (
F23554251C69D19C00796DCA /* NohanaImagePicker.storyboard */,
F27029CD1C71C43A001647AB /* NohanaImagePicker.strings */,
F237249A1C6DCF96005D1E8A /* Images.xcassets */,
);
name = Resources;
sourceTree = "<group>";
};
F23554291C69F38900796DCA /* Photos */ = {
isa = PBXGroup;
children = (
F218D7D51C6B3D22001FCED1 /* PhotoKitAlbumList.swift */,
F28F4AC21C6C49EE00B7D725 /* PhotoKitAssetList.swift */,
F28F4AC41C6C59A500B7D725 /* PhotoKitAsset.swift */,
);
name = Photos;
sourceTree = "<group>";
};
F26775DF1C704574002E786C /* AnimationController */ = {
isa = PBXGroup;
children = (
F26775E41C70574F002E786C /* AnimatableNavigationController.swift */,
F26775E01C7046C7002E786C /* ExpandingAnimationController.swift */,
F26775E91C71645A002E786C /* ContractingAnimationController.swift */,
F2131F421C79615700797887 /* SwipeInteractionController.swift */,
);
name = AnimationController;
sourceTree = "<group>";
};
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 = "<group>";
};
F2C08D6A1C68651900B00181 = {
isa = PBXGroup;
children = (
F2C08D761C68651900B00181 /* NohanaImagePicker */,
F2C08D931C688FC400B00181 /* NohanaImagePickerSample */,
F2C08DA81C688FC400B00181 /* NohanaImagePickerSampleTests */,
F2C08D751C68651900B00181 /* Products */,
);
sourceTree = "<group>";
};
F2C08D751C68651900B00181 /* Products */ = {
isa = PBXGroup;
children = (
F2C08D741C68651900B00181 /* NohanaImagePicker.framework */,
F2C08D921C688FC400B00181 /* NohanaImagePickerSample.app */,
F2C08DA51C688FC400B00181 /* NohanaImagePickerSampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
F2C08DA81C688FC400B00181 /* NohanaImagePickerSampleTests */ = {
isa = PBXGroup;
children = (
F2C08DA91C688FC400B00181 /* NohanaImagePickerSampleTests.swift */,
F2C08DAB1C688FC400B00181 /* Info.plist */,
);
path = NohanaImagePickerSampleTests;
sourceTree = "<group>";
};
F2E508901C68B1770082EF93 /* ViewControllers */ = {
isa = PBXGroup;
children = (
F23554271C69D5DB00796DCA /* NohanaImagePickerController.swift */,
F24EB68F1C68AEED0002EC86 /* AlbumListViewController.swift */,
F2DF3B141C6C76E500C1C0E4 /* AssetListViewController.swift */,
F25C10C11C8ED9BF007453C3 /* MomentViewController.swift */,
F2DF3B2A1C6CC4DB00C1C0E4 /* AssetDetailListViewController.swift */,
);
name = ViewControllers;
sourceTree = "<group>";
};
/* 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 = "<group>";
};
F2C08D981C688FC400B00181 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
F2C08D991C688FC400B00181 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
F2C08D9D1C688FC400B00181 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
F2C08D9E1C688FC400B00181 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:NohanaImagePicker.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D731C68651900B00181"
BuildableName = "NohanaImagePicker.framework"
BlueprintName = "NohanaImagePicker"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D7D1C68651900B00181"
BuildableName = "NohanaImagePickerTests.xctest"
BlueprintName = "NohanaImagePickerTests"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D731C68651900B00181"
BuildableName = "NohanaImagePicker.framework"
BlueprintName = "NohanaImagePicker"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D731C68651900B00181"
BuildableName = "NohanaImagePicker.framework"
BlueprintName = "NohanaImagePicker"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D731C68651900B00181"
BuildableName = "NohanaImagePicker.framework"
BlueprintName = "NohanaImagePicker"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D911C688FC400B00181"
BuildableName = "NohanaImagePickerSample.app"
BlueprintName = "NohanaImagePickerSample"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08DA41C688FC400B00181"
BuildableName = "NohanaImagePickerSampleTests.xctest"
BlueprintName = "NohanaImagePickerSampleTests"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D911C688FC400B00181"
BuildableName = "NohanaImagePickerSample.app"
BlueprintName = "NohanaImagePickerSample"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D911C688FC400B00181"
BuildableName = "NohanaImagePickerSample.app"
BlueprintName = "NohanaImagePickerSample"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F2C08D911C688FC400B00181"
BuildableName = "NohanaImagePickerSample.app"
BlueprintName = "NohanaImagePickerSample"
ReferencedContainer = "container:NohanaImagePicker.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -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()
}
}
}

View File

@ -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!
}

View File

@ -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")
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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 )
}
}

View File

@ -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)
}
}

View File

@ -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())
}
}
}

View File

@ -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()
}
}
}

View File

@ -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())
}
}
}

View File

@ -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"
}
}
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -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<NSObject, AnyObject>?
}

View File

@ -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))
}
}

View File

@ -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!
}

View File

@ -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)
}
}

View File

@ -0,0 +1,18 @@
//
// NohanaImagePicker.h
// NohanaImagePicker
//
// Created by kazushi.hara on 2016/02/08.
// Copyright © 2016年 nohana. All rights reserved.
//
#import <UIKit/UIKit.h>
//! 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 <NohanaImagePicker/PublicHeader.h>

View File

@ -0,0 +1,442 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="14F1713" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="tkA-yz-hIq">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--Album List View Controller-->
<scene sceneID="q2I-uS-c5w">
<objects>
<tableViewController id="KD8-91-yw0" customClass="AlbumListViewController" customModule="NohanaImagePicker" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="Nh8-55-S0b">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="AlbumCell" rowHeight="82" id="HiQ-6b-xqZ" customClass="AlbumCell" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="92" width="320" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="HiQ-6b-xqZ" id="Nuf-ja-Ajg">
<rect key="frame" x="0.0" y="0.0" width="287" height="82"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Igm-UH-WUI">
<rect key="frame" x="12" y="6" width="70" height="70"/>
<constraints>
<constraint firstAttribute="width" constant="70" id="Vc2-EG-fnZ"/>
<constraint firstAttribute="height" constant="70" id="xiw-gz-cfn"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Album Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YSz-MM-h6f">
<rect key="frame" x="100" y="33" width="187" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="X2y-hP-9aM"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="YSz-MM-h6f" firstAttribute="leading" secondItem="Igm-UH-WUI" secondAttribute="trailing" constant="18" id="LeH-Q7-bwW"/>
<constraint firstItem="YSz-MM-h6f" firstAttribute="centerY" secondItem="Nuf-ja-Ajg" secondAttribute="centerY" id="XIa-vC-C0g"/>
<constraint firstItem="Igm-UH-WUI" firstAttribute="leading" secondItem="Nuf-ja-Ajg" secondAttribute="leadingMargin" constant="4" id="d1e-N1-R1z"/>
<constraint firstItem="Igm-UH-WUI" firstAttribute="centerY" secondItem="Nuf-ja-Ajg" secondAttribute="centerY" id="lNP-fO-Buq"/>
<constraint firstAttribute="trailingMargin" secondItem="YSz-MM-h6f" secondAttribute="trailing" constant="-8" id="tee-FS-sAo"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<connections>
<outlet property="thumbnailImageView" destination="Igm-UH-WUI" id="RZo-Jd-deA"/>
<outlet property="titleLabel" destination="YSz-MM-h6f" id="bA5-m7-lwo"/>
<segue destination="5jk-x3-LYk" kind="show" id="mWT-mj-Xpg"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="MomentAlbumCell" rowHeight="52" id="URZ-ke-kYw" customClass="MomentCell" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="174" width="320" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="URZ-ke-kYw" id="ZzC-oj-tew">
<rect key="frame" x="0.0" y="0.0" width="287" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Moment Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nzC-P1-sBf">
<rect key="frame" x="16" y="18" width="271" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="tPC-2Q-2pd"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="nzC-P1-sBf" secondAttribute="trailing" constant="-8" id="Cg4-Vn-QD9"/>
<constraint firstItem="nzC-P1-sBf" firstAttribute="leading" secondItem="ZzC-oj-tew" secondAttribute="leadingMargin" constant="8" id="Gli-fC-Ruq"/>
<constraint firstItem="nzC-P1-sBf" firstAttribute="centerY" secondItem="ZzC-oj-tew" secondAttribute="centerY" id="SZH-em-zj3"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<connections>
<outlet property="titleLabel" destination="nzC-P1-sBf" id="XE1-Qw-Xid"/>
<segue destination="FOz-K3-QuR" kind="show" id="oPR-kG-SaF"/>
</connections>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="KD8-91-yw0" id="svJ-EQ-YBS"/>
<outlet property="delegate" destination="KD8-91-yw0" id="BME-Rd-OtX"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="ldl-4P-wIa">
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="3Gh-FG-2Jq">
<connections>
<action selector="didPushCancel:" destination="KD8-91-yw0" id="tR3-ZK-z1Y"/>
</connections>
</barButtonItem>
</navigationItem>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="568"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vZM-gg-XY5" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="731" y="449"/>
</scene>
<!--Asset List View Controller-->
<scene sceneID="29G-N1-dd5">
<objects>
<collectionViewController id="5jk-x3-LYk" customClass="AssetListViewController" customModule="NohanaImagePicker" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="cLH-x9-2VN">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="2" minimumInteritemSpacing="2" id="xXL-vg-bZb">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="44" height="44"/>
<inset key="sectionInset" minX="0.0" minY="8" maxX="0.0" maxY="2"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AssetCell" id="gNu-ad-t1d" customClass="AssetCell" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="72" width="78" height="78"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="WKb-lP-sq5">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
</imageView>
<view hidden="YES" alpha="0.29999999999999999" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="MT5-kg-1gq">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oDB-be-mWY">
<rect key="frame" x="34" y="0.0" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="7Yd-sj-A9c"/>
<constraint firstAttribute="height" constant="44" id="Biw-rC-p8x"/>
</constraints>
<state key="normal" image="btn_select_m"/>
<connections>
<action selector="didPushPickButton:" destination="gNu-ad-t1d" eventType="touchUpInside" id="gAj-yS-7xL"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="oDB-be-mWY" secondAttribute="trailing" constant="-8" id="0u6-oO-0PG"/>
<constraint firstItem="WKb-lP-sq5" firstAttribute="leading" secondItem="gNu-ad-t1d" secondAttribute="leadingMargin" constant="-8" id="Gen-Gr-Deg"/>
<constraint firstItem="oDB-be-mWY" firstAttribute="top" secondItem="gNu-ad-t1d" secondAttribute="topMargin" constant="-8" id="HMs-XU-000"/>
<constraint firstItem="MT5-kg-1gq" firstAttribute="top" secondItem="gNu-ad-t1d" secondAttribute="topMargin" constant="-8" id="ISy-ev-P19"/>
<constraint firstAttribute="bottomMargin" secondItem="WKb-lP-sq5" secondAttribute="bottom" constant="-8" id="Lnf-5f-guH"/>
<constraint firstAttribute="trailingMargin" secondItem="MT5-kg-1gq" secondAttribute="trailing" constant="-8" id="QL8-B4-uFE"/>
<constraint firstAttribute="bottomMargin" secondItem="MT5-kg-1gq" secondAttribute="bottom" constant="-8" id="RNL-zl-et0"/>
<constraint firstAttribute="trailingMargin" secondItem="WKb-lP-sq5" secondAttribute="trailing" constant="-8" id="TF3-Wc-2HZ"/>
<constraint firstItem="MT5-kg-1gq" firstAttribute="leading" secondItem="gNu-ad-t1d" secondAttribute="leadingMargin" constant="-8" id="afC-Dk-wv1"/>
<constraint firstItem="WKb-lP-sq5" firstAttribute="top" secondItem="gNu-ad-t1d" secondAttribute="topMargin" constant="-8" id="l0l-Ge-QIn"/>
</constraints>
<size key="customSize" width="78" height="78"/>
<connections>
<outlet property="imageView" destination="WKb-lP-sq5" id="ejZ-cu-f5Y"/>
<outlet property="overlayView" destination="MT5-kg-1gq" id="yGO-Ix-xoL"/>
<outlet property="pickButton" destination="oDB-be-mWY" id="hf7-6i-k62"/>
<segue destination="r7t-t8-AuE" kind="show" id="vma-iJ-kti"/>
</connections>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionFooterView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AssetListFooter" id="N6q-ob-hkI">
<rect key="frame" x="0.0" y="152" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</collectionReusableView>
<connections>
<outlet property="dataSource" destination="5jk-x3-LYk" id="Wb9-5c-ehn"/>
<outlet property="delegate" destination="5jk-x3-LYk" id="DzT-7Y-grc"/>
</connections>
</collectionView>
<navigationItem key="navigationItem" id="2dO-AB-uAb">
<barButtonItem key="rightBarButtonItem" systemItem="done" id="mvt-PJ-icK">
<connections>
<action selector="didPushDone:" destination="5jk-x3-LYk" id="t78-YJ-Taa"/>
</connections>
</barButtonItem>
</navigationItem>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="568"/>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1SX-Sl-Deo" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1156" y="267"/>
</scene>
<!--Asset Detail List View Controller-->
<scene sceneID="jjo-0p-7dH">
<objects>
<collectionViewController id="r7t-t8-AuE" customClass="AssetDetailListViewController" customModule="NohanaImagePicker" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" pagingEnabled="YES" dataMode="prototypes" id="wIU-KV-n3W">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="KRt-js-xi3">
<size key="itemSize" width="320" height="320"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AssetDetailCell" id="Sux-7h-iE6" customClass="AssetDetailCell" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="156" width="320" height="320"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="320"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" maximumZoomScale="3" translatesAutoresizingMaskIntoConstraints="NO" id="hM8-Xr-3HX">
<rect key="frame" x="0.0" y="0.0" width="320" height="320"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Jr7-IN-pxL">
<rect key="frame" x="0.0" y="0.0" width="320" height="320"/>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" constant="320" id="6g6-cZ-gPH"/>
<constraint firstAttribute="width" constant="320" id="Hgu-gK-eCb"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstItem="Jr7-IN-pxL" firstAttribute="top" secondItem="hM8-Xr-3HX" secondAttribute="top" id="Bzv-EL-kt1"/>
<constraint firstAttribute="trailing" secondItem="Jr7-IN-pxL" secondAttribute="trailing" id="EjO-pU-R9v"/>
<constraint firstItem="Jr7-IN-pxL" firstAttribute="leading" secondItem="hM8-Xr-3HX" secondAttribute="leading" id="Pdy-Fn-ObK"/>
<constraint firstAttribute="bottom" secondItem="Jr7-IN-pxL" secondAttribute="bottom" id="QG5-qB-82F"/>
</constraints>
<connections>
<outlet property="delegate" destination="Sux-7h-iE6" id="VlX-9M-844"/>
</connections>
</scrollView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="hM8-Xr-3HX" secondAttribute="bottom" constant="-8" id="D7T-je-5yG"/>
<constraint firstItem="hM8-Xr-3HX" firstAttribute="top" secondItem="Sux-7h-iE6" secondAttribute="topMargin" constant="-8" id="LFL-jM-CYg"/>
<constraint firstAttribute="trailingMargin" secondItem="hM8-Xr-3HX" secondAttribute="trailing" constant="-8" id="ZtL-Jp-e2u"/>
<constraint firstItem="hM8-Xr-3HX" firstAttribute="leading" secondItem="Sux-7h-iE6" secondAttribute="leadingMargin" constant="-8" id="yfl-cA-Saf"/>
</constraints>
<connections>
<outlet property="imageView" destination="Jr7-IN-pxL" id="x1X-6p-m21"/>
<outlet property="imageViewHeightConstraint" destination="6g6-cZ-gPH" id="rbW-Fe-hPO"/>
<outlet property="imageViewWidthConstraint" destination="Hgu-gK-eCb" id="qeL-Vc-jtG"/>
<outlet property="scrollView" destination="hM8-Xr-3HX" id="zIr-A1-bmZ"/>
</connections>
</collectionViewCell>
</cells>
<connections>
<outlet property="dataSource" destination="r7t-t8-AuE" id="IF2-zY-xUA"/>
<outlet property="delegate" destination="r7t-t8-AuE" id="RAP-Do-LKn"/>
</connections>
</collectionView>
<navigationItem key="navigationItem" id="UuE-5y-0BI">
<barButtonItem key="rightBarButtonItem" id="K6j-IO-w2n">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="c4C-Sf-Krj">
<rect key="frame" x="276" y="8" width="28" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="btn_select_l"/>
<connections>
<action selector="didPushPickButton:" destination="r7t-t8-AuE" eventType="touchUpInside" id="vWF-yZ-1NC"/>
</connections>
</button>
</barButtonItem>
</navigationItem>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="568"/>
<connections>
<outlet property="pickButton" destination="c4C-Sf-Krj" id="Bam-Wm-TRa"/>
</connections>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="u84-ka-8a6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1598" y="456"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="RR9-zr-Qlw">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="tkA-yz-hIq" sceneMemberID="viewController">
<toolbarItems/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="568"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="tdZ-Dj-Unr">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="KD8-91-yw0" kind="relationship" relationship="rootViewController" id="sZ8-aF-u4m"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="3iI-ph-cZF" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="297" y="449"/>
</scene>
<!--Moment View Controller-->
<scene sceneID="kJ6-NP-kmc">
<objects>
<collectionViewController id="FOz-K3-QuR" customClass="MomentViewController" customModule="NohanaImagePicker" customModuleProvider="target" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="J9r-MB-y52">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="2" minimumInteritemSpacing="2" id="smE-Ti-qUX">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="50" height="44"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="2" maxX="0.0" maxY="10"/>
</collectionViewFlowLayout>
<cells>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AssetCell" id="hn9-bM-eId" customClass="AssetCell" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="110" width="78" height="78"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TB3-Xf-nik">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
</imageView>
<view hidden="YES" alpha="0.30000001192092896" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SnZ-bd-Zia">
<rect key="frame" x="0.0" y="0.0" width="78" height="78"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vrt-tc-b54">
<rect key="frame" x="34" y="0.0" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="BX8-gP-86t"/>
<constraint firstAttribute="height" constant="44" id="RUG-gG-ieX"/>
</constraints>
<state key="normal" image="btn_select_m"/>
<connections>
<action selector="didPushPickButton:" destination="hn9-bM-eId" eventType="touchUpInside" id="g70-dv-o5C"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="SnZ-bd-Zia" secondAttribute="bottom" constant="-8" id="1hA-eS-4fw"/>
<constraint firstItem="SnZ-bd-Zia" firstAttribute="leading" secondItem="hn9-bM-eId" secondAttribute="leadingMargin" constant="-8" id="57F-oY-ykD"/>
<constraint firstItem="vrt-tc-b54" firstAttribute="top" secondItem="hn9-bM-eId" secondAttribute="topMargin" constant="-8" id="Djb-rK-T7y"/>
<constraint firstAttribute="trailingMargin" secondItem="vrt-tc-b54" secondAttribute="trailing" constant="-8" id="KEs-ry-rwP"/>
<constraint firstAttribute="trailingMargin" secondItem="TB3-Xf-nik" secondAttribute="trailing" constant="-8" id="XM8-uU-hsZ"/>
<constraint firstItem="TB3-Xf-nik" firstAttribute="top" secondItem="hn9-bM-eId" secondAttribute="topMargin" constant="-8" id="avn-LP-mMH"/>
<constraint firstAttribute="bottomMargin" secondItem="TB3-Xf-nik" secondAttribute="bottom" constant="-8" id="bvb-eR-krl"/>
<constraint firstAttribute="trailingMargin" secondItem="SnZ-bd-Zia" secondAttribute="trailing" constant="-8" id="kX3-02-UOR"/>
<constraint firstItem="SnZ-bd-Zia" firstAttribute="top" secondItem="hn9-bM-eId" secondAttribute="topMargin" constant="-8" id="vZV-H0-2Fs"/>
<constraint firstItem="TB3-Xf-nik" firstAttribute="leading" secondItem="hn9-bM-eId" secondAttribute="leadingMargin" constant="-8" id="wGD-kd-Ibg"/>
</constraints>
<size key="customSize" width="78" height="78"/>
<connections>
<outlet property="imageView" destination="TB3-Xf-nik" id="HVf-Zj-nzV"/>
<outlet property="overlayView" destination="SnZ-bd-Zia" id="27X-Fy-OP9"/>
<outlet property="pickButton" destination="vrt-tc-b54" id="MVQ-KJ-OAp"/>
<segue destination="r7t-t8-AuE" kind="show" id="Bed-w3-B1Q"/>
</connections>
</collectionViewCell>
</cells>
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="MomentHeader" id="BCa-t3-9XW" customClass="MomentSectionHeaderView" customModule="NohanaImagePicker" customModuleProvider="target">
<rect key="frame" x="0.0" y="64" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Date" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aNJ-Oh-VhZ">
<rect key="frame" x="12" y="11" width="120" height="21"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="21" id="KSm-hH-VZX"/>
<constraint firstAttribute="width" constant="120" id="QzC-0j-DZA"/>
<constraint firstAttribute="height" constant="21" id="XMh-5E-eAL"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
<variation key="default">
<mask key="constraints">
<exclude reference="KSm-hH-VZX"/>
</mask>
</variation>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Location" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Aa-PA-AHS">
<rect key="frame" x="132" y="12" width="176" height="20.5"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="QVr-qn-Rz7"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="aNJ-Oh-VhZ" firstAttribute="centerY" secondItem="BCa-t3-9XW" secondAttribute="centerY" id="MyW-U2-vOY"/>
<constraint firstItem="9Aa-PA-AHS" firstAttribute="trailing" secondItem="BCa-t3-9XW" secondAttribute="trailingMargin" constant="-4" id="Ufe-hG-OzN"/>
<constraint firstItem="aNJ-Oh-VhZ" firstAttribute="leading" secondItem="BCa-t3-9XW" secondAttribute="leadingMargin" constant="4" id="Ws5-lF-5jV"/>
<constraint firstItem="aNJ-Oh-VhZ" firstAttribute="leading" secondItem="BCa-t3-9XW" secondAttribute="leadingMargin" id="X0t-BQ-hqg"/>
<constraint firstItem="aNJ-Oh-VhZ" firstAttribute="centerY" secondItem="BCa-t3-9XW" secondAttribute="centerY" id="XbD-RN-36L"/>
<constraint firstItem="9Aa-PA-AHS" firstAttribute="centerY" secondItem="BCa-t3-9XW" secondAttribute="centerY" id="uN3-IU-mxA"/>
<constraint firstItem="9Aa-PA-AHS" firstAttribute="leading" secondItem="aNJ-Oh-VhZ" secondAttribute="trailing" id="zWA-MS-zg7"/>
</constraints>
<variation key="default">
<mask key="constraints">
<exclude reference="X0t-BQ-hqg"/>
<exclude reference="XbD-RN-36L"/>
</mask>
</variation>
<connections>
<outlet property="dateLabel" destination="aNJ-Oh-VhZ" id="Zr4-vz-aX3"/>
<outlet property="locationLabel" destination="9Aa-PA-AHS" id="fn1-wx-XK0"/>
</connections>
</collectionReusableView>
<connections>
<outlet property="dataSource" destination="FOz-K3-QuR" id="wch-Jd-Ads"/>
<outlet property="delegate" destination="FOz-K3-QuR" id="zvh-co-HCR"/>
</connections>
</collectionView>
<navigationItem key="navigationItem" id="Fcb-Wq-WEM">
<barButtonItem key="rightBarButtonItem" systemItem="done" id="Tfy-aE-Xjc">
<connections>
<action selector="didPushDone:" destination="FOz-K3-QuR" id="mHU-vd-pbC"/>
</connections>
</barButtonItem>
</navigationItem>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="568"/>
</collectionViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="A16-1a-z3b" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1156" y="914"/>
</scene>
</scenes>
<resources>
<image name="btn_select_l" width="28" height="28"/>
<image name="btn_select_m" width="44" height="44"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="Bed-w3-B1Q"/>
</inferredMetricsTieBreakers>
</document>

View File

@ -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)
}
}

View File

@ -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"
}
}
}

View File

@ -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
}
}
}

View File

@ -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))
}
}
}

View File

@ -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
}
}
}

View File

@ -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<AssetType> = []
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 }
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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";

View File

@ -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枚";

View File

@ -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:.
}
}

View File

@ -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"
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="NohanaImagePickerSample" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -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)")
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -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.
}
}
}