Everything
This commit is contained in:
parent
4760fed8c9
commit
1a1cc4f33a
|
@ -0,0 +1,78 @@
|
||||||
|
.DS_Store
|
||||||
|
MyPlayground.playground/
|
||||||
|
|
||||||
|
### Swift ###
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## Build generated
|
||||||
|
build/
|
||||||
|
DerivedData/
|
||||||
|
|
||||||
|
## Various settings
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata/
|
||||||
|
|
||||||
|
## Other
|
||||||
|
*.moved-aside
|
||||||
|
*.xccheckout
|
||||||
|
*.xcscmblueprint
|
||||||
|
|
||||||
|
## Obj-C/Swift specific
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.dSYM.zip
|
||||||
|
*.dSYM
|
||||||
|
|
||||||
|
## 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/
|
||||||
|
# Package.pins
|
||||||
|
.build/
|
||||||
|
|
||||||
|
# CocoaPods - Refactored to standalone file
|
||||||
|
|
||||||
|
# Carthage - Refactored to standalone file
|
||||||
|
|
||||||
|
# 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://docs.fastlane.tools/best-practices/source-control/#source-control
|
||||||
|
|
||||||
|
fastlane/report.xml
|
||||||
|
fastlane/Preview.html
|
||||||
|
fastlane/screenshots
|
||||||
|
fastlane/test_output
|
||||||
|
|
||||||
|
### Xcode ###
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||||
|
|
||||||
|
## Build generated
|
||||||
|
|
||||||
|
## Various settings
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
### Xcode Patch ###
|
||||||
|
*.xcodeproj/*
|
||||||
|
!*.xcodeproj/project.pbxproj
|
||||||
|
!*.xcodeproj/xcshareddata/
|
||||||
|
!*.xcworkspace/contents.xcworkspacedata
|
||||||
|
/*.gcno
|
|
@ -7,21 +7,49 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
D61299E122F8FC7900BC29AD /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D61299E022F8FC7900BC29AD /* Photos.framework */; };
|
||||||
|
D63DF79C23188D9000688258 /* RankingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63DF79A23188D9000688258 /* RankingTableViewCell.swift */; };
|
||||||
|
D63DF79D23188D9000688258 /* RankingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D63DF79B23188D9000688258 /* RankingTableViewCell.xib */; };
|
||||||
D66658CB22F655750053041D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66658CA22F655750053041D /* AppDelegate.swift */; };
|
D66658CB22F655750053041D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66658CA22F655750053041D /* AppDelegate.swift */; };
|
||||||
D66658CD22F655750053041D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66658CC22F655750053041D /* ViewController.swift */; };
|
|
||||||
D66658D022F655750053041D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66658CE22F655750053041D /* Main.storyboard */; };
|
|
||||||
D66658D222F655760053041D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D66658D122F655760053041D /* Assets.xcassets */; };
|
D66658D222F655760053041D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D66658D122F655760053041D /* Assets.xcassets */; };
|
||||||
D66658D522F655760053041D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66658D322F655760053041D /* LaunchScreen.storyboard */; };
|
D66658D522F655760053041D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66658D322F655760053041D /* LaunchScreen.storyboard */; };
|
||||||
|
D69E1CD422F6561B00FDB494 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69E1CD222F6561B00FDB494 /* RootViewController.swift */; };
|
||||||
|
D69E1CD522F6561B00FDB494 /* RootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D69E1CD322F6561B00FDB494 /* RootViewController.xib */; };
|
||||||
|
D69E1CDB22F656D700FDB494 /* AssetCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D69E1CD822F656D700FDB494 /* AssetCollectionViewCell.xib */; };
|
||||||
|
D69E1CDC22F656D700FDB494 /* AssetCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69E1CD922F656D700FDB494 /* AssetCollectionViewCell.swift */; };
|
||||||
|
D6AD654722F65DF9000148A3 /* AssetPickerViewControler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AD654622F65DF9000148A3 /* AssetPickerViewControler.swift */; };
|
||||||
|
D6AD654B22F680A2000148A3 /* AssetComparisonViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AD654922F680A2000148A3 /* AssetComparisonViewController.swift */; };
|
||||||
|
D6AD654C22F680A2000148A3 /* AssetComparisonViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6AD654A22F680A2000148A3 /* AssetComparisonViewController.xib */; };
|
||||||
|
D6AD654F22F681B0000148A3 /* LargeImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AD654D22F681B0000148A3 /* LargeImageViewController.swift */; };
|
||||||
|
D6AD655022F681B0000148A3 /* LargeImageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6AD654E22F681B0000148A3 /* LargeImageViewController.xib */; };
|
||||||
|
D6AD655222F68629000148A3 /* PhotosHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AD655122F68629000148A3 /* PhotosHelper.swift */; };
|
||||||
|
D6E5189622F8F44C00B4CCA1 /* RankingsTableViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6E5189422F8F44C00B4CCA1 /* RankingsTableViewController.xib */; };
|
||||||
|
D6E5189722F8F44C00B4CCA1 /* RankingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E5189522F8F44C00B4CCA1 /* RankingsTableViewController.swift */; };
|
||||||
|
D6EBEF5F22F68F19008EA503 /* Array+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6EBEF5E22F68F19008EA503 /* Array+Helpers.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
D61299E022F8FC7900BC29AD /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; };
|
||||||
|
D63DF79A23188D9000688258 /* RankingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
D63DF79B23188D9000688258 /* RankingTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RankingTableViewCell.xib; sourceTree = "<group>"; };
|
||||||
D66658C722F655750053041D /* PhotoRank.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhotoRank.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
D66658C722F655750053041D /* PhotoRank.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhotoRank.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D66658CA22F655750053041D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
D66658CA22F655750053041D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
D66658CC22F655750053041D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D66658CF22F655750053041D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
|
||||||
D66658D122F655760053041D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
D66658D122F655760053041D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
D66658D422F655760053041D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
D66658D422F655760053041D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
D66658D622F655760053041D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D66658D622F655760053041D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
D69E1CD222F6561B00FDB494 /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = RootViewController.swift; path = ../../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/Root/RootViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D69E1CD322F6561B00FDB494 /* RootViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = RootViewController.xib; path = ../../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/Root/RootViewController.xib; sourceTree = "<group>"; };
|
||||||
|
D69E1CD822F656D700FDB494 /* AssetCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AssetCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
|
D69E1CD922F656D700FDB494 /* AssetCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
D6AD654622F65DF9000148A3 /* AssetPickerViewControler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetPickerViewControler.swift; sourceTree = "<group>"; };
|
||||||
|
D6AD654922F680A2000148A3 /* AssetComparisonViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AssetComparisonViewController.swift; path = "../../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/Asset Comparison/AssetComparisonViewController.swift"; sourceTree = "<group>"; };
|
||||||
|
D6AD654A22F680A2000148A3 /* AssetComparisonViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = AssetComparisonViewController.xib; path = "../../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/Asset Comparison/AssetComparisonViewController.xib"; sourceTree = "<group>"; };
|
||||||
|
D6AD654D22F681B0000148A3 /* LargeImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LargeImageViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D6AD654E22F681B0000148A3 /* LargeImageViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LargeImageViewController.xib; sourceTree = "<group>"; };
|
||||||
|
D6AD655122F68629000148A3 /* PhotosHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PhotosHelper.swift; path = ../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/PhotosHelper.swift; sourceTree = "<group>"; };
|
||||||
|
D6E5189422F8F44C00B4CCA1 /* RankingsTableViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RankingsTableViewController.xib; sourceTree = "<group>"; };
|
||||||
|
D6E5189522F8F44C00B4CCA1 /* RankingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RankingsTableViewController.swift; sourceTree = "<group>"; };
|
||||||
|
D6EBEF5E22F68F19008EA503 /* Array+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Array+Helpers.swift"; path = "../../../../../../System/Volumes/Data/Users/shadowfacts/Dev/iOS/PhotoRank/PhotoRank/Array+Helpers.swift"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -29,17 +57,27 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D61299E122F8FC7900BC29AD /* Photos.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
D61299DF22F8FC7900BC29AD /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D61299E022F8FC7900BC29AD /* Photos.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D66658BE22F655750053041D = {
|
D66658BE22F655750053041D = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D66658C922F655750053041D /* PhotoRank */,
|
D66658C922F655750053041D /* PhotoRank */,
|
||||||
D66658C822F655750053041D /* Products */,
|
D66658C822F655750053041D /* Products */,
|
||||||
|
D61299DF22F8FC7900BC29AD /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -55,8 +93,12 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D66658CA22F655750053041D /* AppDelegate.swift */,
|
D66658CA22F655750053041D /* AppDelegate.swift */,
|
||||||
D66658CC22F655750053041D /* ViewController.swift */,
|
D6AD655122F68629000148A3 /* PhotosHelper.swift */,
|
||||||
D66658CE22F655750053041D /* Main.storyboard */,
|
D6EBEF5E22F68F19008EA503 /* Array+Helpers.swift */,
|
||||||
|
D69E1CD122F6560C00FDB494 /* Root */,
|
||||||
|
D69E1CD622F656C800FDB494 /* Asset Picker */,
|
||||||
|
D6AD654822F65E18000148A3 /* Asset Comparison */,
|
||||||
|
D6E5189322F8F44C00B4CCA1 /* Final Rankings */,
|
||||||
D66658D122F655760053041D /* Assets.xcassets */,
|
D66658D122F655760053041D /* Assets.xcassets */,
|
||||||
D66658D322F655760053041D /* LaunchScreen.storyboard */,
|
D66658D322F655760053041D /* LaunchScreen.storyboard */,
|
||||||
D66658D622F655760053041D /* Info.plist */,
|
D66658D622F655760053041D /* Info.plist */,
|
||||||
|
@ -64,6 +106,47 @@
|
||||||
path = PhotoRank;
|
path = PhotoRank;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D69E1CD122F6560C00FDB494 /* Root */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D69E1CD222F6561B00FDB494 /* RootViewController.swift */,
|
||||||
|
D69E1CD322F6561B00FDB494 /* RootViewController.xib */,
|
||||||
|
);
|
||||||
|
path = Root;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D69E1CD622F656C800FDB494 /* Asset Picker */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6AD654622F65DF9000148A3 /* AssetPickerViewControler.swift */,
|
||||||
|
D69E1CD922F656D700FDB494 /* AssetCollectionViewCell.swift */,
|
||||||
|
D69E1CD822F656D700FDB494 /* AssetCollectionViewCell.xib */,
|
||||||
|
);
|
||||||
|
path = "Asset Picker";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D6AD654822F65E18000148A3 /* Asset Comparison */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6AD654922F680A2000148A3 /* AssetComparisonViewController.swift */,
|
||||||
|
D6AD654A22F680A2000148A3 /* AssetComparisonViewController.xib */,
|
||||||
|
D6AD654D22F681B0000148A3 /* LargeImageViewController.swift */,
|
||||||
|
D6AD654E22F681B0000148A3 /* LargeImageViewController.xib */,
|
||||||
|
);
|
||||||
|
path = "Asset Comparison";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
D6E5189322F8F44C00B4CCA1 /* Final Rankings */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D6E5189522F8F44C00B4CCA1 /* RankingsTableViewController.swift */,
|
||||||
|
D6E5189422F8F44C00B4CCA1 /* RankingsTableViewController.xib */,
|
||||||
|
D63DF79A23188D9000688258 /* RankingTableViewCell.swift */,
|
||||||
|
D63DF79B23188D9000688258 /* RankingTableViewCell.xib */,
|
||||||
|
);
|
||||||
|
path = "Final Rankings";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -123,8 +206,13 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D66658D522F655760053041D /* LaunchScreen.storyboard in Resources */,
|
D66658D522F655760053041D /* LaunchScreen.storyboard in Resources */,
|
||||||
|
D6E5189622F8F44C00B4CCA1 /* RankingsTableViewController.xib in Resources */,
|
||||||
D66658D222F655760053041D /* Assets.xcassets in Resources */,
|
D66658D222F655760053041D /* Assets.xcassets in Resources */,
|
||||||
D66658D022F655750053041D /* Main.storyboard in Resources */,
|
D6AD654C22F680A2000148A3 /* AssetComparisonViewController.xib in Resources */,
|
||||||
|
D69E1CD522F6561B00FDB494 /* RootViewController.xib in Resources */,
|
||||||
|
D6AD655022F681B0000148A3 /* LargeImageViewController.xib in Resources */,
|
||||||
|
D63DF79D23188D9000688258 /* RankingTableViewCell.xib in Resources */,
|
||||||
|
D69E1CDB22F656D700FDB494 /* AssetCollectionViewCell.xib in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -135,22 +223,22 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D66658CD22F655750053041D /* ViewController.swift in Sources */,
|
D6AD654B22F680A2000148A3 /* AssetComparisonViewController.swift in Sources */,
|
||||||
D66658CB22F655750053041D /* AppDelegate.swift in Sources */,
|
D66658CB22F655750053041D /* AppDelegate.swift in Sources */,
|
||||||
|
D6E5189722F8F44C00B4CCA1 /* RankingsTableViewController.swift in Sources */,
|
||||||
|
D6AD654F22F681B0000148A3 /* LargeImageViewController.swift in Sources */,
|
||||||
|
D63DF79C23188D9000688258 /* RankingTableViewCell.swift in Sources */,
|
||||||
|
D6EBEF5F22F68F19008EA503 /* Array+Helpers.swift in Sources */,
|
||||||
|
D69E1CD422F6561B00FDB494 /* RootViewController.swift in Sources */,
|
||||||
|
D6AD655222F68629000148A3 /* PhotosHelper.swift in Sources */,
|
||||||
|
D6AD654722F65DF9000148A3 /* AssetPickerViewControler.swift in Sources */,
|
||||||
|
D69E1CDC22F656D700FDB494 /* AssetCollectionViewCell.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
D66658CE22F655750053041D /* Main.storyboard */ = {
|
|
||||||
isa = PBXVariantGroup;
|
|
||||||
children = (
|
|
||||||
D66658CF22F655750053041D /* Base */,
|
|
||||||
);
|
|
||||||
name = Main.storyboard;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D66658D322F655760053041D /* LaunchScreen.storyboard */ = {
|
D66658D322F655760053041D /* LaunchScreen.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -285,6 +373,7 @@
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||||
INFOPLIST_FILE = PhotoRank/Info.plist;
|
INFOPLIST_FILE = PhotoRank/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
@ -303,6 +392,7 @@
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
DEVELOPMENT_TEAM = HGYVAQA9FW;
|
||||||
INFOPLIST_FILE = PhotoRank/Info.plist;
|
INFOPLIST_FILE = PhotoRank/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
|
|
|
@ -15,7 +15,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
// Override point for customization after application launch.
|
window = UIWindow(frame: UIScreen.main.bounds)
|
||||||
|
window!.rootViewController = UINavigationController(rootViewController: RootViewController())
|
||||||
|
window!.makeKeyAndVisible()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// Array+Helpers.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
func allPairs() -> [[Element]] {
|
||||||
|
return self.enumerated().flatMap { (index, first) in
|
||||||
|
self[index..<self.endIndex].dropFirst().map { [first, $0] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
//
|
||||||
|
// AssetComparisonViewController.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
class AssetComparisonViewController: UIViewController {
|
||||||
|
|
||||||
|
let allAssets: [PHAsset]
|
||||||
|
var assetScores = [String: Int]()
|
||||||
|
let assetPairs: [[PHAsset]]
|
||||||
|
var currentPairIndex: Int = 0 {
|
||||||
|
didSet {
|
||||||
|
currentAssetPair = assetPairs[currentPairIndex]
|
||||||
|
currentlySelected = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentlySelected: Int = 0 {
|
||||||
|
didSet {
|
||||||
|
largeImage.image = currentImagePair[currentlySelected]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentAssetPair: [PHAsset]! {
|
||||||
|
didSet {
|
||||||
|
loadImages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var currentImagePair: [UIImage?] = [nil, nil] {
|
||||||
|
didSet {
|
||||||
|
largeImage.image = currentImagePair[currentlySelected]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let largeImage = LargeImageViewController()
|
||||||
|
|
||||||
|
@IBOutlet var imageContainerView: UIView!
|
||||||
|
|
||||||
|
init(assets: [PHAsset]) {
|
||||||
|
guard assets.count >= 2 else { fatalError("AssetComparisonViewController requires at least 2 assets") }
|
||||||
|
self.allAssets = assets
|
||||||
|
self.assetPairs = assets.allPairs()
|
||||||
|
self.currentAssetPair = assetPairs.first!
|
||||||
|
|
||||||
|
super.init(nibName: "AssetComparisonViewController", bundle: .main)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
largeImage.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
imageContainerView.addSubview(largeImage.view)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
largeImage.view.leadingAnchor.constraint(equalTo: imageContainerView.leadingAnchor),
|
||||||
|
largeImage.view.topAnchor.constraint(equalTo: imageContainerView.topAnchor),
|
||||||
|
imageContainerView.trailingAnchor.constraint(equalTo: largeImage.view.trailingAnchor),
|
||||||
|
imageContainerView.bottomAnchor.constraint(equalTo: largeImage.view.bottomAnchor),
|
||||||
|
])
|
||||||
|
addChild(largeImage)
|
||||||
|
|
||||||
|
toolbarItems = [
|
||||||
|
UIBarButtonItem(title: "Skip", style: .plain, target: self, action: #selector(moveToNextPair)),
|
||||||
|
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
|
||||||
|
UIBarButtonItem(title: "Switch", style: .plain, target: self, action: #selector(switchButtonPressed)),
|
||||||
|
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
|
||||||
|
UIBarButtonItem(title: "Pick", style: .plain, target: self, action: #selector(pickButtonPressed))
|
||||||
|
]
|
||||||
|
|
||||||
|
loadImages()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
navigationController!.isToolbarHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
navigationController!.isToolbarHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadImages() {
|
||||||
|
self.currentlySelected = 0
|
||||||
|
self.currentImagePair = [nil, nil]
|
||||||
|
PhotosHelper.requestFullSizeImage(for: currentAssetPair[0]) { (image) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.currentImagePair = [image, self.currentImagePair[1]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PhotosHelper.requestFullSizeImage(for: currentAssetPair[1]) { (image) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.currentImagePair = [self.currentImagePair[0], image]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func switchButtonPressed() {
|
||||||
|
currentlySelected = currentlySelected == 0 ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func moveToNextPair() {
|
||||||
|
if currentPairIndex == assetPairs.count - 1 {
|
||||||
|
print("finished: \(assetScores)")
|
||||||
|
// sorts assets by score in descending order
|
||||||
|
let sorted = allAssets
|
||||||
|
.map { ($0, assetScores[$0.localIdentifier] ?? 0) }
|
||||||
|
.sorted { (first, second) -> Bool in
|
||||||
|
return first.1 > second.1
|
||||||
|
}
|
||||||
|
let rankings = RankingsTableViewController(rankings: sorted)
|
||||||
|
show(rankings, sender: nil)
|
||||||
|
} else {
|
||||||
|
currentPairIndex += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func pickButtonPressed() {
|
||||||
|
let identifier = currentAssetPair[currentlySelected].localIdentifier
|
||||||
|
let current = assetScores[identifier] ?? 0
|
||||||
|
assetScores[identifier] = current + 1
|
||||||
|
|
||||||
|
moveToNextPair()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14845" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14799.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AssetComparisonViewController" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageContainerView" destination="Sjl-dv-Pzi" id="CYZ-X3-8MF"/>
|
||||||
|
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||||
|
</connections>
|
||||||
|
</placeholder>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Sjl-dv-Pzi">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Sjl-dv-Pzi" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="3B4-cg-ar5"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Sjl-dv-Pzi" secondAttribute="trailing" id="4Zd-Gu-21d"/>
|
||||||
|
<constraint firstItem="Sjl-dv-Pzi" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="QUe-M1-mc9"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Sjl-dv-Pzi" secondAttribute="bottom" id="tMN-WK-iHt"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
|
||||||
|
<point key="canvasLocation" x="139" y="115"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -0,0 +1,149 @@
|
||||||
|
//
|
||||||
|
// LargeImageViewController.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/31/18.
|
||||||
|
// Copyright © 2018 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
||||||
|
|
||||||
|
@IBOutlet weak var scrollView: UIScrollView!
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
@IBOutlet weak var imageViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var imageViewTopConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
var image: UIImage? {
|
||||||
|
didSet {
|
||||||
|
imageView.image = image
|
||||||
|
if oldValue == nil, let image = image {
|
||||||
|
imageView.bounds = CGRect(origin: .zero, size: image.size)
|
||||||
|
view.setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var prevZoomScale: CGFloat?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(nibName: "LargeImageViewController", bundle: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// imageView.image = image
|
||||||
|
|
||||||
|
scrollView.delegate = self
|
||||||
|
// imageView.bounds = CGRect(origin: .zero, size: imageView.image!.size)
|
||||||
|
|
||||||
|
// view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(scrollViewPressed(_:))))
|
||||||
|
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(scrollViewDoubleTapped(_:)))
|
||||||
|
doubleTap.numberOfTapsRequired = 2
|
||||||
|
view.addGestureRecognizer(doubleTap)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews() {
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
let maxHeight = view.bounds.height - view.safeAreaInsets.top - view.safeAreaInsets.bottom
|
||||||
|
let heightScale = maxHeight / imageView.bounds.height
|
||||||
|
let widthScale = view.bounds.width / imageView.bounds.width
|
||||||
|
let minScale = min(widthScale, heightScale)
|
||||||
|
scrollView.minimumZoomScale = minScale
|
||||||
|
scrollView.zoomScale = minScale
|
||||||
|
scrollView.maximumZoomScale = minScale * 4
|
||||||
|
|
||||||
|
centerImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||||
|
return imageView
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
||||||
|
centerImage()
|
||||||
|
|
||||||
|
// let prevZoomScale = self.prevZoomScale ?? scrollView.minimumZoomScale
|
||||||
|
// if scrollView.zoomScale <= scrollView.minimumZoomScale {
|
||||||
|
// controlsVisible = true
|
||||||
|
// } else if scrollView.zoomScale > prevZoomScale {
|
||||||
|
// controlsVisible = false
|
||||||
|
// }
|
||||||
|
// self.prevZoomScale = scrollView.zoomScale
|
||||||
|
}
|
||||||
|
|
||||||
|
func centerImage() {
|
||||||
|
let yOffset = max(0, (view.bounds.size.height - imageView.frame.height - scrollView.contentInset.top - scrollView.contentInset.bottom) / 2)
|
||||||
|
imageViewTopConstraint.constant = yOffset
|
||||||
|
|
||||||
|
let xOffset = max(0, (view.bounds.size.width - imageView.frame.width - scrollView.contentInset.left - scrollView.contentInset.right) / 2)
|
||||||
|
imageViewLeadingConstraint.constant = xOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
func zoomRectFor(scale: CGFloat, center: CGPoint) -> CGRect {
|
||||||
|
var zoomRect = CGRect.zero
|
||||||
|
zoomRect.size.width = imageView.frame.width / scale
|
||||||
|
zoomRect.size.height = imageView.frame.height / scale
|
||||||
|
let newCenter = scrollView.convert(center, to: imageView)
|
||||||
|
zoomRect.origin.x = newCenter.x - (zoomRect.width / 2)
|
||||||
|
zoomRect.origin.y = newCenter.y - (zoomRect.height / 2)
|
||||||
|
return zoomRect
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateZoomOut() {
|
||||||
|
UIView.animate(withDuration: 0.3, animations: {
|
||||||
|
self.scrollView.zoomScale = self.scrollView.minimumZoomScale
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @objc func scrollViewPressed(_ sender: UITapGestureRecognizer) {
|
||||||
|
// if scrollView.zoomScale > scrollView.minimumZoomScale {
|
||||||
|
// animateZoomOut()
|
||||||
|
// } else {
|
||||||
|
//// setControlsVisible(!controlsVisible, animated: true)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@objc func scrollViewDoubleTapped(_ recognizer: UITapGestureRecognizer) {
|
||||||
|
if scrollView.zoomScale <= scrollView.minimumZoomScale {
|
||||||
|
let point = recognizer.location(in: recognizer.view)
|
||||||
|
let scale: CGFloat
|
||||||
|
if scrollView.minimumZoomScale < 1 {
|
||||||
|
if 1 - scrollView.zoomScale <= 0.5 {
|
||||||
|
scale = scrollView.zoomScale + 1
|
||||||
|
} else {
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scale = scrollView.maximumZoomScale
|
||||||
|
}
|
||||||
|
let rect = zoomRectFor(scale: scale, center: point)
|
||||||
|
UIView.animate(withDuration: 0.3) {
|
||||||
|
self.scrollView.zoom(to: rect, animated: false)
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
animateZoomOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @IBAction func closeButtonPressed(_ sender: Any) {
|
||||||
|
// dismiss(animated: true)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @IBAction func sharePressed(_ sender: Any) {
|
||||||
|
// guard let image = image else { return }
|
||||||
|
// let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil)
|
||||||
|
// present(activityVC, animated: true)
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14845" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14799.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LargeImageViewController" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="qcn-1t-3sS" id="Q01-G2-y1c"/>
|
||||||
|
<outlet property="imageViewLeadingConstraint" destination="bI3-V8-M70" id="nIe-xI-E9u"/>
|
||||||
|
<outlet property="imageViewTopConstraint" destination="tfL-hp-2I2" id="EDV-RO-pTe"/>
|
||||||
|
<outlet property="scrollView" destination="Skj-xq-AgQ" id="TFb-zF-m1b"/>
|
||||||
|
<outlet property="view" destination="BJw-5C-9nT" id="1C2-VA-mNf"/>
|
||||||
|
</connections>
|
||||||
|
</placeholder>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view contentMode="scaleToFill" id="BJw-5C-9nT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" minimumZoomScale="0.25" maximumZoomScale="2" translatesAutoresizingMaskIntoConstraints="NO" id="Skj-xq-AgQ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qcn-1t-3sS">
|
||||||
|
<rect key="frame" x="0.0" y="-10" width="375" height="647"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="qcn-1t-3sS" firstAttribute="leading" secondItem="Skj-xq-AgQ" secondAttribute="leading" id="bI3-V8-M70"/>
|
||||||
|
<constraint firstItem="qcn-1t-3sS" firstAttribute="top" secondItem="Skj-xq-AgQ" secondAttribute="top" id="tfL-hp-2I2"/>
|
||||||
|
</constraints>
|
||||||
|
</scrollView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Skj-xq-AgQ" firstAttribute="centerY" secondItem="BJw-5C-9nT" secondAttribute="centerY" id="0Xb-ib-2hg"/>
|
||||||
|
<constraint firstItem="Skj-xq-AgQ" firstAttribute="centerX" secondItem="BJw-5C-9nT" secondAttribute="centerX" id="KMe-Zc-NZq"/>
|
||||||
|
<constraint firstItem="Skj-xq-AgQ" firstAttribute="width" secondItem="BJw-5C-9nT" secondAttribute="width" id="Onj-l9-fBu"/>
|
||||||
|
<constraint firstItem="Skj-xq-AgQ" firstAttribute="height" secondItem="BJw-5C-9nT" secondAttribute="height" id="jvz-QW-n9c"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="w1g-VC-Ll9"/>
|
||||||
|
<point key="canvasLocation" x="-164" y="476"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// AssetCollectionViewCell.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
class AssetCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
var assetIdentifier: String!
|
||||||
|
var thumbnailImage: UIImage? {
|
||||||
|
didSet {
|
||||||
|
imageView.image = thumbnailImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
@IBOutlet weak var visualEffectView: UIVisualEffectView!
|
||||||
|
|
||||||
|
override var isSelected: Bool {
|
||||||
|
didSet {
|
||||||
|
visualEffectView.isHidden = !isSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
visualEffectView.layer.masksToBounds = true
|
||||||
|
visualEffectView.layer.cornerRadius = 12
|
||||||
|
|
||||||
|
visualEffectView.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
super.prepareForReuse()
|
||||||
|
|
||||||
|
imageView.image = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="AssetCollectionViewCell" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="72u-oA-Xgb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||||
|
</imageView>
|
||||||
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0gA-uG-aFo">
|
||||||
|
<rect key="frame" x="52" y="4" width="24" height="24"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="IPu-KR-7jl">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="checkmark.circle.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="kxh-zz-34Y">
|
||||||
|
<rect key="frame" x="0.0" y="0.5" width="24" height="23.5"/>
|
||||||
|
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kxh-zz-34Y" secondAttribute="trailing" id="3cp-19-zeM"/>
|
||||||
|
<constraint firstItem="kxh-zz-34Y" firstAttribute="leading" secondItem="IPu-KR-7jl" secondAttribute="leading" id="52v-uP-gQi"/>
|
||||||
|
<constraint firstItem="kxh-zz-34Y" firstAttribute="top" secondItem="IPu-KR-7jl" secondAttribute="top" id="P1Y-M3-cYf"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="kxh-zz-34Y" secondAttribute="bottom" id="qq7-Mn-9V1"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="24" id="Dfp-qR-lpk"/>
|
||||||
|
<constraint firstAttribute="height" constant="24" id="vWb-4U-qx6"/>
|
||||||
|
</constraints>
|
||||||
|
<blurEffect style="regular"/>
|
||||||
|
</visualEffectView>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="0gA-uG-aFo" secondAttribute="trailing" constant="4" id="13X-9d-65f"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="72u-oA-Xgb" secondAttribute="trailing" id="FRb-Xl-kha"/>
|
||||||
|
<constraint firstItem="72u-oA-Xgb" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" id="HSg-2m-ibv"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="72u-oA-Xgb" secondAttribute="bottom" id="Ifb-pe-ay0"/>
|
||||||
|
<constraint firstItem="0gA-uG-aFo" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="4" id="iYA-Jd-K9D"/>
|
||||||
|
<constraint firstItem="72u-oA-Xgb" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" id="qwp-L5-gZT"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="72u-oA-Xgb" id="giL-3C-x8g"/>
|
||||||
|
<outlet property="visualEffectView" destination="0gA-uG-aFo" id="OPm-n4-jDw"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="139" y="153"/>
|
||||||
|
</collectionViewCell>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="checkmark.circle.fill" catalog="system" width="64" height="62"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
|
@ -0,0 +1,152 @@
|
||||||
|
//
|
||||||
|
// AssetCollectionViewController.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
private let reuseIdentifier = "AssetCell"
|
||||||
|
|
||||||
|
class AssetPickerViewController: UICollectionViewController {
|
||||||
|
|
||||||
|
var continueButton: UIBarButtonItem!
|
||||||
|
|
||||||
|
var flowLayout: UICollectionViewFlowLayout {
|
||||||
|
return collectionViewLayout as! UICollectionViewFlowLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
var availableWidth: CGFloat!
|
||||||
|
var thumbnailSize: CGSize!
|
||||||
|
|
||||||
|
var fetchResult: PHFetchResult<PHAsset>!
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(collectionViewLayout: UICollectionViewFlowLayout())
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
collectionView.alwaysBounceVertical = true
|
||||||
|
|
||||||
|
// Register cell classes
|
||||||
|
self.collectionView!.register(UINib(nibName: "AssetCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
let options = PHFetchOptions()
|
||||||
|
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
||||||
|
// get all photos except live photos
|
||||||
|
options.predicate = NSPredicate(format: "mediaType == %d && !((mediaSubtypes & %d) == %d)", PHAssetMediaType.image.rawValue, PHAssetMediaSubtype.photoLive.rawValue, PHAssetMediaSubtype.photoLive.rawValue)
|
||||||
|
fetchResult = PHAsset.fetchAssets(with: options)
|
||||||
|
|
||||||
|
collectionView.allowsMultipleSelection = true
|
||||||
|
setEditing(true, animated: false)
|
||||||
|
|
||||||
|
continueButton = UIBarButtonItem(title: "Continue", style: .done, target: self, action: #selector(continueButtonPressed))
|
||||||
|
updateItemsSelected()
|
||||||
|
navigationItem.rightBarButtonItem = continueButton
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillLayoutSubviews() {
|
||||||
|
super.viewWillLayoutSubviews()
|
||||||
|
|
||||||
|
let availableWidth = view.bounds.inset(by: view.safeAreaInsets).width
|
||||||
|
|
||||||
|
if self.availableWidth != availableWidth {
|
||||||
|
self.availableWidth = availableWidth
|
||||||
|
|
||||||
|
let size = (availableWidth - 8) / 3
|
||||||
|
flowLayout.itemSize = CGSize(width: size, height: size)
|
||||||
|
flowLayout.minimumInteritemSpacing = 4
|
||||||
|
flowLayout.minimumLineSpacing = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
let scale = UIScreen.main.scale
|
||||||
|
let cellSize = flowLayout.itemSize
|
||||||
|
thumbnailSize = CGSize(width: cellSize.width * scale, height: cellSize.height * scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateItemsSelected() {
|
||||||
|
let selected = collectionView.indexPathsForSelectedItems?.count ?? 0
|
||||||
|
continueButton.isEnabled = selected >= 2
|
||||||
|
|
||||||
|
navigationItem.title = "\(selected) selected"
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func continueButtonPressed() {
|
||||||
|
let indexPaths = collectionView.indexPathsForSelectedItems!
|
||||||
|
let assets = fetchResult.objects(at: IndexSet(indexPaths.map { $0.item }))
|
||||||
|
|
||||||
|
let comparison = AssetComparisonViewController(assets: assets)
|
||||||
|
show(comparison, sender: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UICollectionViewDataSource
|
||||||
|
|
||||||
|
override func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return fetchResult.count
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! AssetCollectionViewCell
|
||||||
|
|
||||||
|
let asset = fetchResult.object(at: indexPath.row)
|
||||||
|
|
||||||
|
cell.assetIdentifier = asset.localIdentifier
|
||||||
|
PhotosHelper.requestThumbnail(for: asset, targetSize: thumbnailSize) { (image) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if cell.assetIdentifier == asset.localIdentifier {
|
||||||
|
cell.thumbnailImage = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: UICollectionViewDelegate
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, shouldBeginMultipleSelectionInteractionAt indexPath: IndexPath) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
updateItemsSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
|
||||||
|
updateItemsSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="tne-QT-ifu">
|
|
||||||
<objects>
|
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// RankingTableViewCell.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/29/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
class RankingTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
@IBOutlet weak var assetImageView: UIImageView!
|
||||||
|
@IBOutlet weak var scoreLabel: UILabel!
|
||||||
|
@IBOutlet weak var favoriteButton: UIButton!
|
||||||
|
|
||||||
|
var assetIdentifier: String?
|
||||||
|
|
||||||
|
override func awakeFromNib() {
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
assetImageView.layer.masksToBounds = true
|
||||||
|
assetImageView.layer.cornerRadius = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUI(asset: PHAsset, score: Int) {
|
||||||
|
self.assetIdentifier = asset.localIdentifier
|
||||||
|
|
||||||
|
PhotosHelper.requestThumbnail(for: asset, targetSize: assetImageView.bounds.size) { (image) in
|
||||||
|
guard self.assetIdentifier == asset.localIdentifier else { return }
|
||||||
|
self.assetImageView.image = image
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreLabel.text = "Score: \(score)"
|
||||||
|
|
||||||
|
updateFavoriteButton(favorited: asset.isFavorite)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateFavoriteButton(favorited: Bool) {
|
||||||
|
let name = favorited ? "heart.fill" : "heart"
|
||||||
|
let image = UIImage(systemName: name)
|
||||||
|
favoriteButton.setImage(image, for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func favoriteButtonPressed() {
|
||||||
|
let generator = UINotificationFeedbackGenerator()
|
||||||
|
generator.prepare()
|
||||||
|
|
||||||
|
guard let assetIdentifier = assetIdentifier else {
|
||||||
|
generator.notificationOccurred(.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let result = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil)
|
||||||
|
guard result.count == 1 else {
|
||||||
|
generator.notificationOccurred(.error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let asset = result.object(at: 0)
|
||||||
|
|
||||||
|
|
||||||
|
PHPhotoLibrary.shared().performChanges({
|
||||||
|
let request = PHAssetChangeRequest(for: asset)
|
||||||
|
request.isFavorite = !asset.isFavorite
|
||||||
|
}) { (success, error) in
|
||||||
|
if success {
|
||||||
|
generator.notificationOccurred(.success)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
guard self.assetIdentifier == assetIdentifier else { return }
|
||||||
|
self.updateFavoriteButton(favorited: !asset.isFavorite)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generator.notificationOccurred(.error)
|
||||||
|
print("Error ocurred changing favorite: \(String(describing: error))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="116" id="KGk-i7-Jjw" customClass="RankingTableViewCell" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="96"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="320" height="96"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OsB-61-ZJs">
|
||||||
|
<rect key="frame" x="8" y="8" width="80" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="80" id="PeH-tP-ooH"/>
|
||||||
|
<constraint firstAttribute="width" constant="80" id="VCj-Je-0sM"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Score: 1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xba-9D-I33">
|
||||||
|
<rect key="frame" x="104" y="37.5" width="61" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="F4t-7A-TLx">
|
||||||
|
<rect key="frame" x="284.5" y="37" width="19.5" height="22"/>
|
||||||
|
<state key="normal" image="heart" catalog="system"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="favoriteButtonPressed" destination="KGk-i7-Jjw" eventType="touchUpInside" id="9Ar-jD-N0y"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="xba-9D-I33" firstAttribute="leading" secondItem="OsB-61-ZJs" secondAttribute="trailing" constant="16" id="Iyl-7T-iy4"/>
|
||||||
|
<constraint firstItem="F4t-7A-TLx" firstAttribute="trailing" secondItem="H2p-sc-9uM" secondAttribute="trailingMargin" id="OVA-8T-N7P"/>
|
||||||
|
<constraint firstItem="F4t-7A-TLx" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="cjb-mc-1ff"/>
|
||||||
|
<constraint firstItem="OsB-61-ZJs" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="8" id="fRg-ac-ljI"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="OsB-61-ZJs" secondAttribute="bottom" constant="8" id="jPG-Uj-1aL"/>
|
||||||
|
<constraint firstItem="OsB-61-ZJs" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="8" id="lOa-Y4-gxE"/>
|
||||||
|
<constraint firstItem="xba-9D-I33" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="sGL-MZ-Db9"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="assetImageView" destination="OsB-61-ZJs" id="HQe-4O-cNI"/>
|
||||||
|
<outlet property="favoriteButton" destination="F4t-7A-TLx" id="LJM-ZH-fX6"/>
|
||||||
|
<outlet property="scoreLabel" destination="xba-9D-I33" id="4Xh-j1-796"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="131.8840579710145" y="148.66071428571428"/>
|
||||||
|
</tableViewCell>
|
||||||
|
</objects>
|
||||||
|
<resources>
|
||||||
|
<image name="heart" catalog="system" width="64" height="58"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
|
@ -0,0 +1,111 @@
|
||||||
|
//
|
||||||
|
// RankingsTableViewController.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/5/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
private let reuseIdentifier = "AssetScoreCell"
|
||||||
|
|
||||||
|
class RankingsTableViewController: UITableViewController {
|
||||||
|
|
||||||
|
var rankings: [(PHAsset, Int)]
|
||||||
|
|
||||||
|
init(rankings: [(PHAsset, Int)]) {
|
||||||
|
self.rankings = rankings
|
||||||
|
|
||||||
|
super.init(nibName: "RankingsTableViewController", bundle: .main)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
tableView.register(UINib(nibName: "RankingTableViewCell", bundle: .main), forCellReuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
tableView.rowHeight = 96
|
||||||
|
|
||||||
|
tableView.alwaysBounceVertical = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Table view data source
|
||||||
|
|
||||||
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return rankings.count
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! RankingTableViewCell
|
||||||
|
|
||||||
|
let (asset, score) = rankings[indexPath.row]
|
||||||
|
cell.updateUI(asset: asset, score: score)
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override to support conditional editing of the table view.
|
||||||
|
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||||
|
// Return false if you do not want the specified item to be editable.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override to support editing the table view.
|
||||||
|
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||||
|
guard editingStyle == .delete else { return }
|
||||||
|
|
||||||
|
let (asset, _) = rankings[indexPath.row]
|
||||||
|
PHPhotoLibrary.shared().performChanges({
|
||||||
|
PHAssetChangeRequest.deleteAssets(NSArray(object: asset))
|
||||||
|
}) { (success, error) in
|
||||||
|
let generator = UINotificationFeedbackGenerator()
|
||||||
|
if success {
|
||||||
|
generator.notificationOccurred(.success)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.rankings.remove(at: indexPath.row)
|
||||||
|
self.tableView.deleteRows(at: [indexPath], with: .fade)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generator.notificationOccurred(.error)
|
||||||
|
print("Error occurred deleting asset: \(String(describing: error))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Override to support rearranging the table view.
|
||||||
|
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Override to support conditional rearranging of the table view.
|
||||||
|
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
||||||
|
// Return false if you do not want the item to be re-orderable.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// MARK: - Navigation
|
||||||
|
|
||||||
|
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
// Get the new view controller using segue.destination.
|
||||||
|
// Pass the selected object to the new view controller.
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RankingsTableViewController" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||||
|
</connections>
|
||||||
|
</placeholder>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="vLr-E1-eTs"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
|
||||||
|
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="133" y="154"/>
|
||||||
|
</tableView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -2,6 +2,8 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>This app must access your photo library to rank photos.</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
@ -22,8 +24,6 @@
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
|
@ -31,8 +31,8 @@
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<array>
|
<array>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// PhotosHelper.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Photos
|
||||||
|
|
||||||
|
struct PhotosHelper {
|
||||||
|
private static let imageManager = PHCachingImageManager()
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
static func requestFullSizeImage(for asset: PHAsset, completion: @escaping (UIImage) -> Void) {
|
||||||
|
imageManager.requestImageData(for: asset, options: nil) { (data, _, _, _) in
|
||||||
|
guard let data = data, let image = UIImage(data: data) else { fatalError() }
|
||||||
|
completion(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func requestThumbnail(for asset: PHAsset, targetSize: CGSize, completion: @escaping (UIImage) -> Void) {
|
||||||
|
imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: nil) { (image, _) in
|
||||||
|
guard let image = image else { return }
|
||||||
|
completion(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// RootViewController.swift
|
||||||
|
// PhotoRank
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 8/3/19.
|
||||||
|
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class RootViewController: UIViewController {
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(nibName: "RootViewController", bundle: .main)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
title = "PhotoRank"
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func choosePhotosPressed() {
|
||||||
|
show(AssetPickerViewController(), sender: nil)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14845" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14799.2"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RootViewController" customModule="PhotoRank" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||||
|
</connections>
|
||||||
|
</placeholder>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="WEK-nT-whA">
|
||||||
|
<rect key="frame" x="154.5" y="433" width="105" height="30"/>
|
||||||
|
<state key="normal" title="Choose Photos"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="choosePhotosPressed" destination="-1" eventType="touchUpInside" id="njJ-qR-beN"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="WEK-nT-whA" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="PJ3-tz-HJ6"/>
|
||||||
|
<constraint firstItem="WEK-nT-whA" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="YDs-MY-anw"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
|
||||||
|
<point key="canvasLocation" x="139" y="153"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
|
@ -1,20 +0,0 @@
|
||||||
//
|
|
||||||
// ViewController.swift
|
|
||||||
// PhotoRank
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 8/3/19.
|
|
||||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class ViewController: UIViewController {
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
// Do any additional setup after loading the view.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue