Add initial files
This commit is contained in:
commit
52e6ffc8d0
26 changed files with 2208 additions and 0 deletions
406
Gallery.xcodeproj/project.pbxproj
Normal file
406
Gallery.xcodeproj/project.pbxproj
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 50;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
03093523235660E100E44910 /* ReverseImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03093522235660E100E44910 /* ReverseImageViewController.swift */; };
|
||||||
|
030935252356833A00E44910 /* InfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030935242356833A00E44910 /* InfoViewController.swift */; };
|
||||||
|
0361A4F8234690C000639E67 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A4F7234690C000639E67 /* AppDelegate.swift */; };
|
||||||
|
0361A4FA234690C000639E67 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A4F9234690C000639E67 /* SceneDelegate.swift */; };
|
||||||
|
0361A4FC234690C000639E67 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A4FB234690C000639E67 /* ViewController.swift */; };
|
||||||
|
0361A4FF234690C000639E67 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0361A4FD234690C000639E67 /* Main.storyboard */; };
|
||||||
|
0361A502234690C000639E67 /* Gallery.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 0361A500234690C000639E67 /* Gallery.xcdatamodeld */; };
|
||||||
|
0361A504234690C100639E67 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0361A503234690C100639E67 /* Assets.xcassets */; };
|
||||||
|
0361A507234690C100639E67 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0361A505234690C100639E67 /* LaunchScreen.storyboard */; };
|
||||||
|
0361A5102346919A00639E67 /* PostViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A50F2346919A00639E67 /* PostViewCell.swift */; };
|
||||||
|
0361A5122346A38100639E67 /* PostViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A5112346A38100639E67 /* PostViewController.swift */; };
|
||||||
|
0361A514234828D000639E67 /* EditTagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A513234828D000639E67 /* EditTagsViewController.swift */; };
|
||||||
|
0361A51623482A7D00639E67 /* TagViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0361A51523482A7D00639E67 /* TagViewCell.swift */; };
|
||||||
|
03F92BA22349635C0000DC1C /* PostCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F92BA12349635C0000DC1C /* PostCollectionView.swift */; };
|
||||||
|
03F92BA4234967E00000DC1C /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F92BA3234967E00000DC1C /* SearchViewController.swift */; };
|
||||||
|
03F92BA6234969800000DC1C /* TagViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F92BA5234969800000DC1C /* TagViewController.swift */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
03093522235660E100E44910 /* ReverseImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReverseImageViewController.swift; sourceTree = "<group>"; };
|
||||||
|
030935242356833A00E44910 /* InfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0361A4F4234690C000639E67 /* Gallery.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Gallery.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
0361A4F7234690C000639E67 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
0361A4F9234690C000639E67 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
0361A4FB234690C000639E67 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0361A4FE234690C000639E67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
|
0361A501234690C000639E67 /* Gallery.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Gallery.xcdatamodel; sourceTree = "<group>"; };
|
||||||
|
0361A503234690C100639E67 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
0361A506234690C100639E67 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
0361A508234690C100639E67 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
0361A50E234690F200639E67 /* Gallery.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Gallery.entitlements; sourceTree = "<group>"; };
|
||||||
|
0361A50F2346919A00639E67 /* PostViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
0361A5112346A38100639E67 /* PostViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0361A513234828D000639E67 /* EditTagsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTagsViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0361A51523482A7D00639E67 /* TagViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagViewCell.swift; sourceTree = "<group>"; };
|
||||||
|
03F92BA12349635C0000DC1C /* PostCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCollectionView.swift; sourceTree = "<group>"; };
|
||||||
|
03F92BA3234967E00000DC1C /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = "<group>"; };
|
||||||
|
03F92BA5234969800000DC1C /* TagViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagViewController.swift; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
0361A4F1234690C000639E67 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
0361A4EB234690C000639E67 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0361A4F6234690C000639E67 /* Gallery */,
|
||||||
|
0361A4F5234690C000639E67 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0361A4F5234690C000639E67 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0361A4F4234690C000639E67 /* Gallery.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0361A4F6234690C000639E67 /* Gallery */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
0361A50E234690F200639E67 /* Gallery.entitlements */,
|
||||||
|
0361A4F7234690C000639E67 /* AppDelegate.swift */,
|
||||||
|
0361A4F9234690C000639E67 /* SceneDelegate.swift */,
|
||||||
|
0361A4FB234690C000639E67 /* ViewController.swift */,
|
||||||
|
0361A4FD234690C000639E67 /* Main.storyboard */,
|
||||||
|
0361A503234690C100639E67 /* Assets.xcassets */,
|
||||||
|
0361A505234690C100639E67 /* LaunchScreen.storyboard */,
|
||||||
|
0361A508234690C100639E67 /* Info.plist */,
|
||||||
|
0361A500234690C000639E67 /* Gallery.xcdatamodeld */,
|
||||||
|
0361A50F2346919A00639E67 /* PostViewCell.swift */,
|
||||||
|
0361A5112346A38100639E67 /* PostViewController.swift */,
|
||||||
|
0361A513234828D000639E67 /* EditTagsViewController.swift */,
|
||||||
|
0361A51523482A7D00639E67 /* TagViewCell.swift */,
|
||||||
|
03F92BA12349635C0000DC1C /* PostCollectionView.swift */,
|
||||||
|
03F92BA3234967E00000DC1C /* SearchViewController.swift */,
|
||||||
|
03F92BA5234969800000DC1C /* TagViewController.swift */,
|
||||||
|
03093522235660E100E44910 /* ReverseImageViewController.swift */,
|
||||||
|
030935242356833A00E44910 /* InfoViewController.swift */,
|
||||||
|
);
|
||||||
|
path = Gallery;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
0361A4F3234690C000639E67 /* Gallery */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 0361A50B234690C100639E67 /* Build configuration list for PBXNativeTarget "Gallery" */;
|
||||||
|
buildPhases = (
|
||||||
|
0361A4F0234690C000639E67 /* Sources */,
|
||||||
|
0361A4F1234690C000639E67 /* Frameworks */,
|
||||||
|
0361A4F2234690C000639E67 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = Gallery;
|
||||||
|
productName = Gallery;
|
||||||
|
productReference = 0361A4F4234690C000639E67 /* Gallery.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
0361A4EC234690C000639E67 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1100;
|
||||||
|
LastUpgradeCheck = 1100;
|
||||||
|
ORGANIZATIONNAME = "Joshua Goins";
|
||||||
|
TargetAttributes = {
|
||||||
|
0361A4F3234690C000639E67 = {
|
||||||
|
CreatedOnToolsVersion = 11.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 0361A4EF234690C000639E67 /* Build configuration list for PBXProject "Gallery" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = 0361A4EB234690C000639E67;
|
||||||
|
productRefGroup = 0361A4F5234690C000639E67 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
0361A4F3234690C000639E67 /* Gallery */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
0361A4F2234690C000639E67 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
0361A507234690C100639E67 /* LaunchScreen.storyboard in Resources */,
|
||||||
|
0361A504234690C100639E67 /* Assets.xcassets in Resources */,
|
||||||
|
0361A4FF234690C000639E67 /* Main.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
0361A4F0234690C000639E67 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
03F92BA4234967E00000DC1C /* SearchViewController.swift in Sources */,
|
||||||
|
03F92BA22349635C0000DC1C /* PostCollectionView.swift in Sources */,
|
||||||
|
0361A5122346A38100639E67 /* PostViewController.swift in Sources */,
|
||||||
|
0361A502234690C000639E67 /* Gallery.xcdatamodeld in Sources */,
|
||||||
|
0361A4FC234690C000639E67 /* ViewController.swift in Sources */,
|
||||||
|
0361A51623482A7D00639E67 /* TagViewCell.swift in Sources */,
|
||||||
|
03093523235660E100E44910 /* ReverseImageViewController.swift in Sources */,
|
||||||
|
03F92BA6234969800000DC1C /* TagViewController.swift in Sources */,
|
||||||
|
0361A4F8234690C000639E67 /* AppDelegate.swift in Sources */,
|
||||||
|
030935252356833A00E44910 /* InfoViewController.swift in Sources */,
|
||||||
|
0361A514234828D000639E67 /* EditTagsViewController.swift in Sources */,
|
||||||
|
0361A4FA234690C000639E67 /* SceneDelegate.swift in Sources */,
|
||||||
|
0361A5102346919A00639E67 /* PostViewCell.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
0361A4FD234690C000639E67 /* Main.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
0361A4FE234690C000639E67 /* Base */,
|
||||||
|
);
|
||||||
|
name = Main.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
0361A505234690C100639E67 /* LaunchScreen.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
0361A506234690C100639E67 /* Base */,
|
||||||
|
);
|
||||||
|
name = LaunchScreen.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
0361A509234690C100639E67 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
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 = 13.1;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
0361A50A234690C100639E67 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
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 = 13.1;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
0361A50C234690C100639E67 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Gallery/Gallery.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
|
||||||
|
DEVELOPMENT_TEAM = JM5LKVKH48;
|
||||||
|
INFOPLIST_FILE = Gallery/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = jorg.Gallery;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
0361A50D234690C100639E67 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = Gallery/Gallery.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
|
||||||
|
DEVELOPMENT_TEAM = JM5LKVKH48;
|
||||||
|
INFOPLIST_FILE = Gallery/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 13.1;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = jorg.Gallery;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SUPPORTS_MACCATALYST = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
0361A4EF234690C000639E67 /* Build configuration list for PBXProject "Gallery" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
0361A509234690C100639E67 /* Debug */,
|
||||||
|
0361A50A234690C100639E67 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
0361A50B234690C100639E67 /* Build configuration list for PBXNativeTarget "Gallery" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
0361A50C234690C100639E67 /* Debug */,
|
||||||
|
0361A50D234690C100639E67 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCVersionGroup section */
|
||||||
|
0361A500234690C000639E67 /* Gallery.xcdatamodeld */ = {
|
||||||
|
isa = XCVersionGroup;
|
||||||
|
children = (
|
||||||
|
0361A501234690C000639E67 /* Gallery.xcdatamodel */,
|
||||||
|
);
|
||||||
|
currentVersion = 0361A501234690C000639E67 /* Gallery.xcdatamodel */;
|
||||||
|
path = Gallery.xcdatamodeld;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
};
|
||||||
|
/* End XCVersionGroup section */
|
||||||
|
};
|
||||||
|
rootObject = 0361A4EC234690C000639E67 /* Project object */;
|
||||||
|
}
|
7
Gallery.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Gallery.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:Gallery.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?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>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
40
Gallery/AppDelegate.swift
Normal file
40
Gallery/AppDelegate.swift
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
@UIApplicationMain
|
||||||
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||||
|
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var persistentContainer: NSPersistentContainer = {
|
||||||
|
let container = NSPersistentContainer(name: "Gallery")
|
||||||
|
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||||
|
if let error = error as NSError? {
|
||||||
|
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return container
|
||||||
|
}()
|
||||||
|
|
||||||
|
func saveContext () {
|
||||||
|
let context = persistentContainer.viewContext
|
||||||
|
if context.hasChanges {
|
||||||
|
do {
|
||||||
|
try context.save()
|
||||||
|
} catch {
|
||||||
|
let nserror = error as NSError
|
||||||
|
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
98
Gallery/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
98
Gallery/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "20x20",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "iphone",
|
||||||
|
"size" : "20x20",
|
||||||
|
"scale" : "3x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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" : "20x20",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ipad",
|
||||||
|
"size" : "20x20",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "ios-marketing",
|
||||||
|
"size" : "1024x1024",
|
||||||
|
"scale" : "1x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
6
Gallery/Assets.xcassets/Contents.json
Normal file
6
Gallery/Assets.xcassets/Contents.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
25
Gallery/Base.lproj/LaunchScreen.storyboard
Normal file
25
Gallery/Base.lproj/LaunchScreen.storyboard
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||||
|
<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="EHf-IW-A2E">
|
||||||
|
<objects>
|
||||||
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
|
</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>
|
485
Gallery/Base.lproj/Main.storyboard
Normal file
485
Gallery/Base.lproj/Main.storyboard
Normal file
|
@ -0,0 +1,485 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Q6f-oJ-eRP">
|
||||||
|
<device id="mac" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="collection view cell content view" minToolsVersion="11.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Navigation Controller-->
|
||||||
|
<scene sceneID="jm1-yw-Rkx">
|
||||||
|
<objects>
|
||||||
|
<navigationController id="Q6f-oJ-eRP" sceneMemberID="viewController">
|
||||||
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="bva-S3-Kph">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="800" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</navigationBar>
|
||||||
|
<connections>
|
||||||
|
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="ukP-91-aOn"/>
|
||||||
|
</connections>
|
||||||
|
</navigationController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="snU-Pv-qdo" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-643" y="89"/>
|
||||||
|
</scene>
|
||||||
|
<!--Home-->
|
||||||
|
<scene sceneID="tne-QT-ifu">
|
||||||
|
<objects>
|
||||||
|
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="nHo-hC-uT9" customClass="PostCollectionView" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="50" width="800" height="550"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="N2K-Me-YT7">
|
||||||
|
<size key="itemSize" width="50" height="50"/>
|
||||||
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<cells>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="postCell" id="eZg-2I-ubl" customClass="PostViewCell" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="69R-kF-95b">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="phY-ea-yfA">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
</collectionViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="phY-ea-yfA" id="1c7-5d-yIu"/>
|
||||||
|
<segue destination="jQf-RA-c2I" kind="show" identifier="showPost" id="s4Q-Cr-1uF"/>
|
||||||
|
</connections>
|
||||||
|
</collectionViewCell>
|
||||||
|
</cells>
|
||||||
|
</collectionView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="nHo-hC-uT9" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" constant="25" id="715-Gf-Neb"/>
|
||||||
|
<constraint firstItem="nHo-hC-uT9" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="PKo-Re-WQy"/>
|
||||||
|
<constraint firstItem="nHo-hC-uT9" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="UdF-XX-6F1"/>
|
||||||
|
<constraint firstItem="nHo-hC-uT9" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" constant="20" id="ZpI-FT-bld"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" title="Home" id="jQ3-8r-PXH">
|
||||||
|
<barButtonItem key="leftBarButtonItem" systemItem="add" id="7SV-nk-ZV3">
|
||||||
|
<connections>
|
||||||
|
<action selector="importAction:" destination="BYZ-38-t0r" id="dZW-hp-FWf"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem key="rightBarButtonItem" systemItem="search" id="6aR-5j-kcX">
|
||||||
|
<connections>
|
||||||
|
<segue destination="E6A-NM-9w9" kind="show" identifier="search" id="CBy-4f-b7S"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
|
<connections>
|
||||||
|
<outlet property="collectionView" destination="nHo-hC-uT9" id="mmZ-7N-1va"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="132.75" y="89"/>
|
||||||
|
</scene>
|
||||||
|
<!--Post-->
|
||||||
|
<scene sceneID="El6-Kv-Gbq">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="PostViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="jQf-RA-c2I" customClass="PostViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="8Jm-eh-tzq">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="7Hd-Ax-kkQ">
|
||||||
|
<rect key="frame" x="0.0" y="50" width="800" height="550"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<connections>
|
||||||
|
<outletCollection property="gestureRecognizers" destination="uQa-Iu-LrE" appends="YES" id="Qec-Ks-5Aj"/>
|
||||||
|
</connections>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="7Hd-Ax-kkQ" firstAttribute="bottom" secondItem="NYh-YZ-QnJ" secondAttribute="bottom" id="3hq-ul-aHb"/>
|
||||||
|
<constraint firstItem="7Hd-Ax-kkQ" firstAttribute="centerX" secondItem="8Jm-eh-tzq" secondAttribute="centerX" id="GEN-bq-TDj"/>
|
||||||
|
<constraint firstItem="7Hd-Ax-kkQ" firstAttribute="leading" secondItem="NYh-YZ-QnJ" secondAttribute="leading" id="Lek-GS-1nx"/>
|
||||||
|
<constraint firstItem="7Hd-Ax-kkQ" firstAttribute="top" secondItem="NYh-YZ-QnJ" secondAttribute="top" id="Pz5-1j-hxY"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="NYh-YZ-QnJ"/>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" title="Post" id="zrg-Ib-oQd">
|
||||||
|
<rightBarButtonItems>
|
||||||
|
<barButtonItem title="Item" image="square.and.arrow.up" catalog="system" id="NNI-g2-aTY">
|
||||||
|
<connections>
|
||||||
|
<action selector="shareAction:" destination="jQf-RA-c2I" id="H7r-Iw-ZQ6"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem title="Tags" image="pencil" catalog="system" id="8d3-sz-ihb">
|
||||||
|
<connections>
|
||||||
|
<segue destination="3TL-qD-pbO" kind="popoverPresentation" identifier="showTags" popoverAnchorBarButtonItem="8d3-sz-ihb" id="QRP-ua-ucM">
|
||||||
|
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
|
||||||
|
</segue>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem title="Info" id="4FI-KY-8yL">
|
||||||
|
<connections>
|
||||||
|
<segue destination="MaR-nc-wxI" kind="popoverPresentation" identifier="showInfo" popoverAnchorBarButtonItem="4FI-KY-8yL" id="OSs-lm-jsP">
|
||||||
|
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
|
||||||
|
</segue>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</rightBarButtonItems>
|
||||||
|
</navigationItem>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="7Hd-Ax-kkQ" id="Gq6-1m-LOs"/>
|
||||||
|
<outlet property="shareButton" destination="NNI-g2-aTY" id="Kji-Gj-9pN"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="VII-uM-HmI" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<tapGestureRecognizer id="uQa-Iu-LrE">
|
||||||
|
<connections>
|
||||||
|
<action selector="playAction:" destination="jQf-RA-c2I" id="SfZ-fD-BfC"/>
|
||||||
|
</connections>
|
||||||
|
</tapGestureRecognizer>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="998" y="89"/>
|
||||||
|
</scene>
|
||||||
|
<!--Edit Tags View Controller-->
|
||||||
|
<scene sceneID="xfh-Ag-H7b">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="EditTagsViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="3TL-qD-pbO" customClass="EditTagsViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="FH2-sj-H4t">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="333" height="480"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="bb4-UL-VAQ">
|
||||||
|
<rect key="frame" x="29" y="111" width="288" height="353"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<prototypes>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="tagCell" id="c7T-0r-L5w" customClass="TagViewCell" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="28" width="288" height="44.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="c7T-0r-L5w" id="WjD-Nb-tif">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="288" height="44.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Cei-95-iOR">
|
||||||
|
<rect key="frame" x="15" y="12" width="258" height="21"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Cei-95-iOR" firstAttribute="trailing" secondItem="WjD-Nb-tif" secondAttribute="trailingMargin" id="VaJ-GJ-hFJ"/>
|
||||||
|
<constraint firstItem="Cei-95-iOR" firstAttribute="top" secondItem="WjD-Nb-tif" secondAttribute="topMargin" constant="2" id="f84-FI-m0u"/>
|
||||||
|
<constraint firstItem="Cei-95-iOR" firstAttribute="leading" secondItem="WjD-Nb-tif" secondAttribute="leadingMargin" id="ggu-hW-gB3"/>
|
||||||
|
<constraint firstItem="Cei-95-iOR" firstAttribute="centerY" secondItem="WjD-Nb-tif" secondAttribute="centerY" id="vzg-jU-quQ"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="label" destination="Cei-95-iOR" id="gA4-H1-RJF"/>
|
||||||
|
<segue destination="1Lq-DE-RSc" kind="show" identifier="showTag" id="WEP-it-u9v"/>
|
||||||
|
</connections>
|
||||||
|
</tableViewCell>
|
||||||
|
</prototypes>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="3TL-qD-pbO" id="qkX-W1-3Rb"/>
|
||||||
|
<outlet property="delegate" destination="3TL-qD-pbO" id="BK9-Bn-TJy"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tags" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Avc-oj-cgH">
|
||||||
|
<rect key="frame" x="148" y="21" width="38" height="20"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vru-rx-keE">
|
||||||
|
<rect key="frame" x="280" y="15" width="38" height="31"/>
|
||||||
|
<state key="normal" title="Done"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="doneAction:" destination="3TL-qD-pbO" eventType="touchDown" id="qAw-6K-ZZF"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Enter new tag..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="2po-UQ-V0K">
|
||||||
|
<rect key="frame" x="29" y="61" width="288" height="34"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="editingFinished:" destination="3TL-qD-pbO" eventType="editingDidEnd" id="MGp-b4-ufV"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iWK-vF-Vze">
|
||||||
|
<rect key="frame" x="29" y="15" width="30" height="31"/>
|
||||||
|
<state key="normal" title="Edit"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="editAction:" destination="3TL-qD-pbO" eventType="touchDown" id="5er-HM-8S2"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Avc-oj-cgH" firstAttribute="centerX" secondItem="FH2-sj-H4t" secondAttribute="centerX" id="0Gc-7b-iOz"/>
|
||||||
|
<constraint firstItem="iWK-vF-Vze" firstAttribute="top" secondItem="QZA-mC-Y5U" secondAttribute="top" constant="15" id="1GH-Ph-jLQ"/>
|
||||||
|
<constraint firstItem="2po-UQ-V0K" firstAttribute="top" secondItem="iWK-vF-Vze" secondAttribute="bottom" constant="15" id="Tfw-tD-NIh"/>
|
||||||
|
<constraint firstItem="bb4-UL-VAQ" firstAttribute="top" secondItem="2po-UQ-V0K" secondAttribute="bottom" constant="16" id="Tza-Yu-GJy"/>
|
||||||
|
<constraint firstItem="vru-rx-keE" firstAttribute="top" secondItem="QZA-mC-Y5U" secondAttribute="top" constant="15" id="Vie-or-57j"/>
|
||||||
|
<constraint firstItem="QZA-mC-Y5U" firstAttribute="trailing" secondItem="bb4-UL-VAQ" secondAttribute="trailing" constant="16" id="Y0t-Xb-GeO"/>
|
||||||
|
<constraint firstItem="bb4-UL-VAQ" firstAttribute="leading" secondItem="QZA-mC-Y5U" secondAttribute="leading" constant="16" id="dU7-AM-rdu"/>
|
||||||
|
<constraint firstItem="QZA-mC-Y5U" firstAttribute="trailing" secondItem="vru-rx-keE" secondAttribute="trailing" constant="15" id="fQA-zb-Ggk"/>
|
||||||
|
<constraint firstItem="2po-UQ-V0K" firstAttribute="leading" secondItem="QZA-mC-Y5U" secondAttribute="leading" constant="16" id="h3a-gQ-AeR"/>
|
||||||
|
<constraint firstItem="iWK-vF-Vze" firstAttribute="leading" secondItem="QZA-mC-Y5U" secondAttribute="leading" constant="16" id="hJB-8r-k89"/>
|
||||||
|
<constraint firstItem="QZA-mC-Y5U" firstAttribute="trailing" secondItem="2po-UQ-V0K" secondAttribute="trailing" constant="16" id="mcC-aj-xcR"/>
|
||||||
|
<constraint firstItem="QZA-mC-Y5U" firstAttribute="bottom" secondItem="bb4-UL-VAQ" secondAttribute="bottom" constant="16" id="o9M-an-8qu"/>
|
||||||
|
<constraint firstItem="Avc-oj-cgH" firstAttribute="top" secondItem="QZA-mC-Y5U" secondAttribute="top" constant="21" id="sTL-Nv-S4n"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="QZA-mC-Y5U"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="tableView" destination="bb4-UL-VAQ" id="Vq5-Vb-i3L"/>
|
||||||
|
<outlet property="tagField" destination="2po-UQ-V0K" id="Mrv-6H-7QS"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="tmO-Po-quZ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="997.5" y="850"/>
|
||||||
|
</scene>
|
||||||
|
<!--Info View Controller-->
|
||||||
|
<scene sceneID="w3u-pt-BtZ">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="InfoViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="MaR-nc-wxI" customClass="InfoViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="VFL-0t-HSi">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="333" height="480"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yPq-yv-LyM">
|
||||||
|
<rect key="frame" x="279" y="15" width="38" height="30"/>
|
||||||
|
<state key="normal" title="Done"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="exitAction:" destination="MaR-nc-wxI" eventType="touchDown" id="VYZ-ym-Qcz"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" numberOfLines="5" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NDx-zx-hmw">
|
||||||
|
<rect key="frame" x="33" y="57" width="280" height="423"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="NDx-zx-hmw" firstAttribute="leading" secondItem="lo4-uV-JuH" secondAttribute="leading" constant="20" id="8qu-VF-O1C"/>
|
||||||
|
<constraint firstItem="lo4-uV-JuH" firstAttribute="trailing" secondItem="yPq-yv-LyM" secondAttribute="trailing" constant="16" id="eL9-wr-Ckn"/>
|
||||||
|
<constraint firstItem="yPq-yv-LyM" firstAttribute="top" secondItem="lo4-uV-JuH" secondAttribute="top" constant="15" id="hgP-Wl-Ugt"/>
|
||||||
|
<constraint firstItem="NDx-zx-hmw" firstAttribute="top" secondItem="yPq-yv-LyM" secondAttribute="bottom" constant="12" id="jtW-aZ-fN6"/>
|
||||||
|
<constraint firstItem="lo4-uV-JuH" firstAttribute="trailing" secondItem="NDx-zx-hmw" secondAttribute="trailing" constant="20" id="lA8-3s-iso"/>
|
||||||
|
<constraint firstItem="NDx-zx-hmw" firstAttribute="bottom" secondItem="lo4-uV-JuH" secondAttribute="bottom" id="uGJ-74-Xc1"/>
|
||||||
|
<constraint firstItem="NDx-zx-hmw" firstAttribute="top" secondItem="lo4-uV-JuH" secondAttribute="top" constant="57" id="yYl-PH-q7f"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="lo4-uV-JuH"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="infoLabel" destination="NDx-zx-hmw" id="S1H-PB-0sL"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="MNn-jG-FmI" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="330.43478260869568" y="1633.9285714285713"/>
|
||||||
|
</scene>
|
||||||
|
<!--Reverse Image View Controller-->
|
||||||
|
<scene sceneID="Si7-Hr-Nu4">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="ReverseImageViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="VhH-X2-v6b" customClass="ReverseImageViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="TNu-lY-cd3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<wkWebView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uq3-56-0hX">
|
||||||
|
<rect key="frame" x="0.0" y="59" width="800" height="541"/>
|
||||||
|
<color key="backgroundColor" red="0.36078431370000003" green="0.38823529410000002" blue="0.4039215686" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<wkWebViewConfiguration key="configuration">
|
||||||
|
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
|
||||||
|
<wkPreferences key="preferences"/>
|
||||||
|
</wkWebViewConfiguration>
|
||||||
|
</wkWebView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rUi-3B-MYc">
|
||||||
|
<rect key="frame" x="20" y="20" width="49" height="31"/>
|
||||||
|
<state key="normal" title="Cancel"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="cancelAction:" destination="VhH-X2-v6b" eventType="touchDown" id="ggg-rw-euW"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dtc-GD-q7J">
|
||||||
|
<rect key="frame" x="734" y="20" width="46" height="31"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="46" id="jzo-YK-PFI"/>
|
||||||
|
</constraints>
|
||||||
|
<state key="normal" title="Done"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="rUi-3B-MYc" firstAttribute="top" secondItem="ZJg-bs-auM" secondAttribute="top" constant="20" id="9rH-KP-fyh"/>
|
||||||
|
<constraint firstItem="uq3-56-0hX" firstAttribute="top" secondItem="dtc-GD-q7J" secondAttribute="bottom" constant="8" id="Rrv-D5-qvg"/>
|
||||||
|
<constraint firstItem="uq3-56-0hX" firstAttribute="leading" secondItem="ZJg-bs-auM" secondAttribute="leading" id="Si3-C6-jg1"/>
|
||||||
|
<constraint firstItem="uq3-56-0hX" firstAttribute="bottom" secondItem="ZJg-bs-auM" secondAttribute="bottom" id="TqS-rI-R3m"/>
|
||||||
|
<constraint firstItem="uq3-56-0hX" firstAttribute="centerX" secondItem="TNu-lY-cd3" secondAttribute="centerX" id="XC5-nd-kiz"/>
|
||||||
|
<constraint firstItem="rUi-3B-MYc" firstAttribute="leading" secondItem="TNu-lY-cd3" secondAttribute="leadingMargin" id="m4y-yq-AIi"/>
|
||||||
|
<constraint firstItem="rUi-3B-MYc" firstAttribute="baseline" secondItem="dtc-GD-q7J" secondAttribute="baseline" id="pgb-bf-IaK"/>
|
||||||
|
<constraint firstItem="dtc-GD-q7J" firstAttribute="trailing" secondItem="TNu-lY-cd3" secondAttribute="trailingMargin" id="tkL-yp-n5c"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="ZJg-bs-auM"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="webView" destination="uq3-56-0hX" id="5LV-9f-ZMn"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="v6u-Ow-alL" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="997.5" y="1634"/>
|
||||||
|
</scene>
|
||||||
|
<!--Search View Controller-->
|
||||||
|
<scene sceneID="Uih-gS-AlG">
|
||||||
|
<objects>
|
||||||
|
<viewController id="E6A-NM-9w9" customClass="SearchViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="ZwQ-d4-u0T">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Bba-zG-RTo" customClass="PostCollectionView" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="102" width="800" height="498"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" automaticEstimatedItemSize="YES" minimumLineSpacing="10" minimumInteritemSpacing="10" id="aRU-y0-ffy">
|
||||||
|
<size key="itemSize" width="50" height="50"/>
|
||||||
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<cells>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="postCell" id="hUs-QH-sBl" customClass="PostViewCell" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="Epj-EF-X2t">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dNV-SX-dFx">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
</collectionViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="dNV-SX-dFx" id="IqG-SD-Dog"/>
|
||||||
|
<segue destination="jQf-RA-c2I" kind="show" identifier="showPost" id="ofA-CT-ukd"/>
|
||||||
|
</connections>
|
||||||
|
</collectionViewCell>
|
||||||
|
</cells>
|
||||||
|
</collectionView>
|
||||||
|
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Search..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="hMR-QW-233">
|
||||||
|
<rect key="frame" x="20" y="60" width="760" height="34"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||||
|
<textInputTraits key="textInputTraits"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="searchAction:" destination="E6A-NM-9w9" eventType="editingDidEnd" id="2xV-et-aTt"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Bba-zG-RTo" firstAttribute="bottom" secondItem="XAJ-Us-ebW" secondAttribute="bottom" id="CKC-xg-anw"/>
|
||||||
|
<constraint firstItem="hMR-QW-233" firstAttribute="top" secondItem="XAJ-Us-ebW" secondAttribute="top" constant="10" id="JgY-nz-Twd"/>
|
||||||
|
<constraint firstItem="hMR-QW-233" firstAttribute="leading" secondItem="ZwQ-d4-u0T" secondAttribute="leadingMargin" id="YYK-cm-BvJ"/>
|
||||||
|
<constraint firstItem="Bba-zG-RTo" firstAttribute="top" secondItem="hMR-QW-233" secondAttribute="bottom" constant="8" id="lBc-kc-K2A"/>
|
||||||
|
<constraint firstItem="Bba-zG-RTo" firstAttribute="centerX" secondItem="hMR-QW-233" secondAttribute="centerX" id="ny9-KE-3qb"/>
|
||||||
|
<constraint firstItem="Bba-zG-RTo" firstAttribute="leading" secondItem="XAJ-Us-ebW" secondAttribute="leading" id="tWf-Qt-3Pe"/>
|
||||||
|
<constraint firstItem="Bba-zG-RTo" firstAttribute="centerX" secondItem="ZwQ-d4-u0T" secondAttribute="centerX" id="xrL-uW-uAg"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="XAJ-Us-ebW"/>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" id="Qx5-wh-yg8"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="collectionView" destination="Bba-zG-RTo" id="udm-J1-pcl"/>
|
||||||
|
<outlet property="searchField" destination="hMR-QW-233" id="apd-Ge-h1s"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="rr3-fO-vUC" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="132.75" y="850"/>
|
||||||
|
</scene>
|
||||||
|
<!--Tag View Controller-->
|
||||||
|
<scene sceneID="3aP-dA-v7Z">
|
||||||
|
<objects>
|
||||||
|
<viewController id="1Lq-DE-RSc" customClass="TagViewController" customModule="Gallery" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" contentMode="scaleToFill" id="YEa-YJ-CTQ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="720" height="560"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="5op-NT-lmm" customClass="PostCollectionView" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="720" height="560"/>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" automaticEstimatedItemSize="YES" minimumLineSpacing="10" minimumInteritemSpacing="10" id="d7Y-hv-w27">
|
||||||
|
<size key="itemSize" width="50" height="50"/>
|
||||||
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<cells>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="postCell" id="dEY-Wd-6C8" customClass="PostViewCell" customModule="Gallery" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<collectionViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="L9Y-GL-V4q">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zR3-iK-0nS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
</collectionViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageView" destination="zR3-iK-0nS" id="Nyl-KY-G07"/>
|
||||||
|
</connections>
|
||||||
|
</collectionViewCell>
|
||||||
|
</cells>
|
||||||
|
</collectionView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="5op-NT-lmm" firstAttribute="leading" secondItem="kWa-6F-UbO" secondAttribute="leading" id="4IM-cW-hgG"/>
|
||||||
|
<constraint firstItem="5op-NT-lmm" firstAttribute="top" secondItem="kWa-6F-UbO" secondAttribute="top" id="asN-7m-A2j"/>
|
||||||
|
<constraint firstItem="5op-NT-lmm" firstAttribute="bottom" secondItem="kWa-6F-UbO" secondAttribute="bottom" id="gF4-Hd-rgB"/>
|
||||||
|
<constraint firstItem="5op-NT-lmm" firstAttribute="centerX" secondItem="YEa-YJ-CTQ" secondAttribute="centerX" id="kTe-tp-KLp"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="kWa-6F-UbO"/>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" id="X08-yv-ta4"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="collectionView" destination="5op-NT-lmm" id="0rb-w4-kqs"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iEs-Vf-Pai" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1747.5" y="497"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="pencil" catalog="system" width="64" height="56"/>
|
||||||
|
<image name="square.and.arrow.up" catalog="system" width="56" height="64"/>
|
||||||
|
</resources>
|
||||||
|
<inferredMetricsTieBreakers>
|
||||||
|
<segue reference="s4Q-Cr-1uF"/>
|
||||||
|
</inferredMetricsTieBreakers>
|
||||||
|
</document>
|
70
Gallery/EditTagsViewController.swift
Normal file
70
Gallery/EditTagsViewController.swift
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
class EditTagsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
|
||||||
|
var post: Post?
|
||||||
|
|
||||||
|
@IBOutlet weak var tableView: UITableView!
|
||||||
|
@IBOutlet weak var tagField: UITextField!
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return post!.tags!.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "tagCell", for: indexPath as IndexPath) as! TagViewCell
|
||||||
|
|
||||||
|
cell.label.text = (post?.tags![indexPath.row] as! Tag).name
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
|
||||||
|
if(editingStyle == .delete) {
|
||||||
|
post?.removeFromTags(post?.tags![indexPath.row] as! Tag)
|
||||||
|
|
||||||
|
tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func editingFinished(_ sender: Any) {
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext = appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
let tag = Tag(context: managedContext)
|
||||||
|
tag.name = tagField.text
|
||||||
|
|
||||||
|
post?.addToTags(tag)
|
||||||
|
|
||||||
|
tableView.reloadData()
|
||||||
|
tagField.text?.removeAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func editAction(_ sender: Any) {
|
||||||
|
tableView.setEditing(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func doneAction(_ sender: Any) {
|
||||||
|
dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if segue.identifier == "showTag" {
|
||||||
|
let newViewController = segue.destination as! TagViewController
|
||||||
|
let index = self.tableView.indexPathForSelectedRow
|
||||||
|
|
||||||
|
newViewController.tag = (post?.tags![index!.row] as! Tag).name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EditTagsViewController {
|
||||||
|
static func loadFromStoryboard() -> EditTagsViewController? {
|
||||||
|
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||||
|
return storyboard.instantiateViewController(withIdentifier: "EditTagsViewController") as? EditTagsViewController
|
||||||
|
}
|
||||||
|
}
|
10
Gallery/Gallery.entitlements
Normal file
10
Gallery/Gallery.entitlements
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?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>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
8
Gallery/Gallery.xcdatamodeld/.xccurrentversion
Normal file
8
Gallery/Gallery.xcdatamodeld/.xccurrentversion
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?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>_XCCurrentVersionName</key>
|
||||||
|
<string>Gallery.xcdatamodel</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
17
Gallery/Gallery.xcdatamodeld/Gallery.xcdatamodel/contents
Normal file
17
Gallery/Gallery.xcdatamodeld/Gallery.xcdatamodel/contents
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="15400" systemVersion="19A602" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||||
|
<entity name="Post" representedClassName="Post" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||||
|
<attribute name="name" attributeType="String"/>
|
||||||
|
<attribute name="type" optional="YES" attributeType="String"/>
|
||||||
|
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="Tag" inverseName="posts" inverseEntity="Tag"/>
|
||||||
|
</entity>
|
||||||
|
<entity name="Tag" representedClassName="Tag" syncable="YES" codeGenerationType="class">
|
||||||
|
<attribute name="name" attributeType="String"/>
|
||||||
|
<relationship name="posts" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="Post" inverseName="tags" inverseEntity="Post"/>
|
||||||
|
</entity>
|
||||||
|
<elements>
|
||||||
|
<element name="Post" positionX="-63" positionY="-18" width="128" height="103"/>
|
||||||
|
<element name="Tag" positionX="128.46875" positionY="58.08203125" width="128" height="73"/>
|
||||||
|
</elements>
|
||||||
|
</model>
|
70
Gallery/Info.plist
Normal file
70
Gallery/Info.plist
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?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>$(DEVELOPMENT_LANGUAGE)</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>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>LSApplicationCategoryType</key>
|
||||||
|
<string>public.app-category.utilities</string>
|
||||||
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSUserActivityTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>post</string>
|
||||||
|
</array>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
<true/>
|
||||||
|
<key>UISceneConfigurations</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIWindowSceneSessionRoleApplication</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>Default Configuration</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||||
|
<key>UISceneStoryboardFile</key>
|
||||||
|
<string>Main</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<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>
|
64
Gallery/InfoViewController.swift
Normal file
64
Gallery/InfoViewController.swift
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
class InfoViewController: UIViewController {
|
||||||
|
var post: Post?
|
||||||
|
var image: UIImage?
|
||||||
|
|
||||||
|
@IBOutlet weak var infoLabel: UILabel!
|
||||||
|
|
||||||
|
private func resolutionForLocalVideo(url: URL) -> CGSize? {
|
||||||
|
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else { return nil }
|
||||||
|
let size = track.naturalSize.applying(track.preferredTransform)
|
||||||
|
return CGSize(width: abs(size.width), height: abs(size.height))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let documentsPath : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteURL
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(true)
|
||||||
|
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||||
|
|
||||||
|
var type = "Unavailable"
|
||||||
|
if(post!.type != nil) {
|
||||||
|
if(post!.type == "public.image") {
|
||||||
|
type = "Image"
|
||||||
|
} else if(post!.type == "public.mpeg-4") {
|
||||||
|
type = "Video"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var date = "Unavailable"
|
||||||
|
if(post!.date != nil) {
|
||||||
|
date = dateFormatter.string(from: post!.date!)
|
||||||
|
}
|
||||||
|
|
||||||
|
var size = "Unavailable"
|
||||||
|
if(post?.type == "public.mpeg-4") {
|
||||||
|
let videoPath = documentsPath.appendingPathComponent(post!.value(forKey: "name") as! String).path
|
||||||
|
|
||||||
|
let resolution = resolutionForLocalVideo(url: URL(fileURLWithPath: videoPath))
|
||||||
|
|
||||||
|
size = String(format: "%.0f", (resolution?.width)!) + "x" + String(format: "%.0f", (resolution?.height)!)
|
||||||
|
} else if(image != nil) {
|
||||||
|
size = String(format: "%.0f", (image?.size.width)!) + "x" + String(format: "%.0f", (image?.size.height)!)
|
||||||
|
}
|
||||||
|
|
||||||
|
infoLabel.text = "Type: " + type + "\nDate: " + date + "\nSize: " + size
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func exitAction(_ sender: Any) {
|
||||||
|
dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension InfoViewController {
|
||||||
|
static func loadFromStoryboard() -> InfoViewController? {
|
||||||
|
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||||
|
return storyboard.instantiateViewController(withIdentifier: "InfoViewController") as? InfoViewController
|
||||||
|
}
|
||||||
|
}
|
286
Gallery/PostCollectionView.swift
Normal file
286
Gallery/PostCollectionView.swift
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
extension UIActivity.ActivityType {
|
||||||
|
static let reverseImageSearch =
|
||||||
|
UIActivity.ActivityType("jorg.Gallery.reverseImage")
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReverseImageSearchService: UIActivity {
|
||||||
|
var viewController: UIViewController?
|
||||||
|
var post: Post?
|
||||||
|
|
||||||
|
override class var activityCategory: UIActivity.Category {
|
||||||
|
return .action
|
||||||
|
}
|
||||||
|
|
||||||
|
override var activityType: UIActivity.ActivityType? {
|
||||||
|
return .reverseImageSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
override var activityTitle: String? {
|
||||||
|
return NSLocalizedString("Reverse Image Search", comment: "activity title")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override var activityViewController: UIViewController? {
|
||||||
|
return ReverseImageViewController.loadFromStoryboard(post: self.post!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateThumbnail(path: URL) -> UIImage? {
|
||||||
|
do {
|
||||||
|
let asset = AVURLAsset(url: path, options: nil)
|
||||||
|
let imgGenerator = AVAssetImageGenerator(asset: asset)
|
||||||
|
imgGenerator.appliesPreferredTrackTransform = true
|
||||||
|
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
|
||||||
|
let thumbnail = UIImage(cgImage: cgImage)
|
||||||
|
return thumbnail
|
||||||
|
} catch let error {
|
||||||
|
print("*** Error generating thumbnail: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PostCollectionView: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDropDelegate, UICollectionViewDragDelegate {
|
||||||
|
var posts: [NSManagedObject] = []
|
||||||
|
|
||||||
|
weak var viewController: UIViewController?
|
||||||
|
|
||||||
|
private let itemsPerRow: CGFloat = 4
|
||||||
|
|
||||||
|
private let sectionInsets = UIEdgeInsets(top: 10.0,
|
||||||
|
left: 10.0,
|
||||||
|
bottom: 10.0,
|
||||||
|
right: 10.0)
|
||||||
|
|
||||||
|
let documentsPath : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteURL
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return posts.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let post = posts[indexPath.row]
|
||||||
|
|
||||||
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath as IndexPath) as! PostViewCell
|
||||||
|
|
||||||
|
let imagePath = documentsPath.appendingPathComponent(post.value(forKey: "name") as! String).path
|
||||||
|
|
||||||
|
if(FileManager.default.fileExists(atPath: imagePath)) {
|
||||||
|
let type = (post.value(forKey: "type") as? String)
|
||||||
|
|
||||||
|
if(type == "public.mpeg-4") {
|
||||||
|
let imagePath = documentsPath.appendingPathComponent(post.value(forKey: "name") as! String).path
|
||||||
|
cell.imageView.image = generateThumbnail(path: URL(fileURLWithPath: imagePath))
|
||||||
|
} else {
|
||||||
|
cell.imageView.image = UIImage(contentsOfFile: imagePath)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("could not read " + imagePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func actualInit(tag: String?) {
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext = appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Post")
|
||||||
|
|
||||||
|
if(tag != nil) {
|
||||||
|
let predicate = NSPredicate(format: "ANY tags.name in %@", [tag])
|
||||||
|
|
||||||
|
fetchRequest.predicate = predicate
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
posts = try managedContext.fetch(fetchRequest)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.reloadData()
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
print("Could not fetch. \(error), \(error.userInfo)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
|
||||||
|
super.init(frame: frame, collectionViewLayout: layout)
|
||||||
|
|
||||||
|
self.dragInteractionEnabled = true
|
||||||
|
self.dataSource = self
|
||||||
|
self.delegate = self
|
||||||
|
self.dropDelegate = self
|
||||||
|
self.dragDelegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
super.init(coder: coder)
|
||||||
|
|
||||||
|
self.dragInteractionEnabled = true
|
||||||
|
self.dataSource = self
|
||||||
|
self.delegate = self
|
||||||
|
self.dropDelegate = self
|
||||||
|
self.dragDelegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView,
|
||||||
|
layout collectionViewLayout: UICollectionViewLayout,
|
||||||
|
sizeForItemAt indexPath: IndexPath) -> CGSize {
|
||||||
|
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
|
||||||
|
let availableWidth = frame.width - paddingSpace
|
||||||
|
let widthPerItem = availableWidth / itemsPerRow
|
||||||
|
|
||||||
|
return CGSize(width: widthPerItem, height: widthPerItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView,
|
||||||
|
layout collectionViewLayout: UICollectionViewLayout,
|
||||||
|
insetForSectionAt section: Int) -> UIEdgeInsets {
|
||||||
|
return sectionInsets
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView,
|
||||||
|
layout collectionViewLayout: UICollectionViewLayout,
|
||||||
|
minimumLineSpacingForSectionAt section: Int) -> CGFloat {
|
||||||
|
return sectionInsets.left
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
|
||||||
|
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in
|
||||||
|
return self.makeContextMenu(post: self.posts[indexPath.row])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContextMenu(post: NSManagedObject) -> UIMenu {
|
||||||
|
let newWindow = UIAction(title: "Open in New Window", image: UIImage(systemName: "plus.square.on.square")) { action in
|
||||||
|
|
||||||
|
let activity = NSUserActivity(activityType: "post")
|
||||||
|
activity.userInfo = ["name": post.value(forKey: "name") as! String]
|
||||||
|
activity.isEligibleForHandoff = true
|
||||||
|
|
||||||
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let share = UIAction(title: "Share", image: UIImage(systemName: "square.and.arrow.up")) { action in
|
||||||
|
let index = self.posts.firstIndex(of: post)
|
||||||
|
|
||||||
|
let cell = self.cellForItem(at: IndexPath(row: index!, section: 0)) as! PostViewCell
|
||||||
|
|
||||||
|
let imageSearch = ReverseImageSearchService()
|
||||||
|
imageSearch.viewController = self.window?.rootViewController
|
||||||
|
imageSearch.post = self.posts[index!] as? Post
|
||||||
|
|
||||||
|
let activityViewController = UIActivityViewController(activityItems: [cell.imageView.image!], applicationActivities: [imageSearch])
|
||||||
|
activityViewController.popoverPresentationController?.sourceView = cell.contentView
|
||||||
|
|
||||||
|
self.viewController?.present(activityViewController, animated: true, completion:nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
let editTags = UIAction(title: "Tags", image: UIImage(systemName: "tag")) { action in
|
||||||
|
let viewController = EditTagsViewController.loadFromStoryboard()
|
||||||
|
viewController!.post = post as? Post
|
||||||
|
|
||||||
|
self.viewController?.present(viewController!, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
let info = UIAction(title: "Info", image: UIImage(systemName: "info.circle")) { action in
|
||||||
|
let index = self.posts.firstIndex(of: post)
|
||||||
|
|
||||||
|
let cell = self.cellForItem(at: IndexPath(row: index!, section: 0)) as! PostViewCell
|
||||||
|
|
||||||
|
let viewController = InfoViewController.loadFromStoryboard()
|
||||||
|
viewController!.post = post as? Post
|
||||||
|
viewController!.image = cell.imageView.image
|
||||||
|
|
||||||
|
self.viewController?.present(viewController!, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
let delete = UIAction(title: "Delete", image: UIImage(systemName: "trash"), attributes: .destructive) { action in
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext = appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
managedContext.delete(post)
|
||||||
|
self.posts.removeAll { (obj: NSManagedObject) -> Bool in
|
||||||
|
return obj == post
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
return UIMenu(title: "", children: [info, editTags, share, delete])
|
||||||
|
#else
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||||
|
return UIMenu(title: "", children: [newWindow, share, info, editTags, delete])
|
||||||
|
} else {
|
||||||
|
return UIMenu(title: "", children: [share, info, editTags, delete])
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
|
||||||
|
for item in coordinator.items {
|
||||||
|
item.dragItem.itemProvider.loadObject(ofClass: UIImage.self,
|
||||||
|
completionHandler: {(newImage, error) -> Void in
|
||||||
|
if let image = newImage as? UIImage {
|
||||||
|
if let data = image.jpegData(compressionQuality: 0.8) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext = appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
let entity = NSEntityDescription.entity(forEntityName: "Post", in: managedContext)!
|
||||||
|
|
||||||
|
let post = NSManagedObject(entity: entity, insertInto: managedContext)
|
||||||
|
|
||||||
|
let uuid = UUID().uuidString
|
||||||
|
|
||||||
|
let filename = uuid + ".jpg"
|
||||||
|
|
||||||
|
let newPath = self.documentsPath.appendingPathComponent(filename)
|
||||||
|
|
||||||
|
try? data.write(to: newPath)
|
||||||
|
|
||||||
|
post.setValue(filename, forKeyPath: "name")
|
||||||
|
|
||||||
|
try? managedContext.save()
|
||||||
|
self.posts.append(post)
|
||||||
|
|
||||||
|
self.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||||
|
let model = posts[indexPath.item]
|
||||||
|
let itemProvider = NSItemProvider(object: (cellForItem(at: indexPath) as! PostViewCell).imageView.image!)
|
||||||
|
itemProvider.suggestedName = model.value(forKey: "name") as? String
|
||||||
|
let dragItem = UIDragItem(itemProvider: itemProvider)
|
||||||
|
dragItem.localObject = model //We can set the localObject property for convenience
|
||||||
|
return [dragItem]
|
||||||
|
}
|
||||||
|
}
|
7
Gallery/PostViewCell.swift
Normal file
7
Gallery/PostViewCell.swift
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class PostViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
}
|
||||||
|
|
147
Gallery/PostViewController.swift
Normal file
147
Gallery/PostViewController.swift
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import AVFoundation
|
||||||
|
import AVKit
|
||||||
|
|
||||||
|
class PostViewController: UIViewController {
|
||||||
|
@IBOutlet weak var imageView: UIImageView!
|
||||||
|
|
||||||
|
var post: NSManagedObject?
|
||||||
|
var image: UIImage?
|
||||||
|
var isPopup: Bool = false
|
||||||
|
@IBOutlet weak var shareButton: UIBarButtonItem!
|
||||||
|
|
||||||
|
let documentsPath : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteURL
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
if(image == nil) {
|
||||||
|
let imagePath = documentsPath.appendingPathComponent(post!.value(forKey: "name") as! String).path
|
||||||
|
|
||||||
|
if((post?.value(forKey: "type") as? String) == "public.mpeg-4") {
|
||||||
|
self.image = generateThumbnail(path: URL(fileURLWithPath: imagePath))
|
||||||
|
} else {
|
||||||
|
self.image = UIImage(contentsOfFile: imagePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageView.image = self.image
|
||||||
|
|
||||||
|
if(isPopup) {
|
||||||
|
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(closePopup))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func closePopup() {
|
||||||
|
UIApplication.shared.requestSceneSessionDestruction((self.view.window?.windowScene!.session)!, options: nil, errorHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func shareAction(_ sender: Any) {
|
||||||
|
let imageSearch = ReverseImageSearchService()
|
||||||
|
imageSearch.viewController = parent
|
||||||
|
imageSearch.post = self.post as? Post
|
||||||
|
|
||||||
|
let activityViewController = UIActivityViewController(activityItems: [self.image!], applicationActivities: [imageSearch])
|
||||||
|
activityViewController.popoverPresentationController?.barButtonItem = shareButton
|
||||||
|
|
||||||
|
self.present(activityViewController, animated: true, completion:nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if segue.identifier == "showTags" {
|
||||||
|
let newViewController = segue.destination as! EditTagsViewController
|
||||||
|
|
||||||
|
newViewController.post = self.post as? Post
|
||||||
|
} else if segue.identifier == "showInfo" {
|
||||||
|
let newViewController = segue.destination as! InfoViewController
|
||||||
|
|
||||||
|
newViewController.post = self.post as? Post
|
||||||
|
newViewController.image = self.image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func playAction(_ sender: Any) {
|
||||||
|
let imagePath = documentsPath.appendingPathComponent(post!.value(forKey: "name") as! String).path
|
||||||
|
|
||||||
|
// Create an AVPlayer, passing it the HTTP Live Streaming URL.
|
||||||
|
let player = AVPlayer(url: URL(fileURLWithPath: imagePath))
|
||||||
|
|
||||||
|
// Create a new AVPlayerViewController and pass it a reference to the player.
|
||||||
|
let controller = AVPlayerViewController()
|
||||||
|
controller.player = player
|
||||||
|
|
||||||
|
// Modally present the player and call the player's play() method when complete.
|
||||||
|
present(controller, animated: true) {
|
||||||
|
player.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PostViewController {
|
||||||
|
static func loadFromStoryboard() -> PostViewController? {
|
||||||
|
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||||
|
return storyboard.instantiateViewController(withIdentifier: "PostViewController") as? PostViewController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
private let EditButtonToolbarIdentifier = NSToolbarItem.Identifier(rawValue: "OurButton")
|
||||||
|
private let ShareButtonToolbarIdentifier = NSToolbarItem.Identifier(rawValue: "OurButton2")
|
||||||
|
private let InfoButtonToolbarIdentifier = NSToolbarItem.Identifier(rawValue: "OurButton3")
|
||||||
|
|
||||||
|
extension PostViewController: NSToolbarDelegate {
|
||||||
|
@objc func editTagsAction() {
|
||||||
|
if(navigationController?.topViewController != self) {
|
||||||
|
navigationController?.popViewController(animated: true)
|
||||||
|
} else {
|
||||||
|
performSegue(withIdentifier: "showTags", sender: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func infoAction() {
|
||||||
|
if(navigationController?.topViewController != self) {
|
||||||
|
navigationController?.popViewController(animated: true)
|
||||||
|
} else {
|
||||||
|
performSegue(withIdentifier: "showInfo", sender: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||||
|
return [NSToolbarItem.Identifier.flexibleSpace, InfoButtonToolbarIdentifier, EditButtonToolbarIdentifier, ShareButtonToolbarIdentifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||||
|
return toolbarDefaultItemIdentifiers(toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||||
|
if (itemIdentifier == InfoButtonToolbarIdentifier) {
|
||||||
|
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.camera,
|
||||||
|
target: self,
|
||||||
|
action: #selector(self.infoAction))
|
||||||
|
let button = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: barButtonItem)
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIdentifier == EditButtonToolbarIdentifier) {
|
||||||
|
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.edit,
|
||||||
|
target: self,
|
||||||
|
action: #selector(self.editTagsAction))
|
||||||
|
let button = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: barButtonItem)
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIdentifier == ShareButtonToolbarIdentifier) {
|
||||||
|
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.action,
|
||||||
|
target: self,
|
||||||
|
action: #selector(self.shareAction))
|
||||||
|
let button = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: barButtonItem)
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
108
Gallery/ReverseImageViewController.swift
Normal file
108
Gallery/ReverseImageViewController.swift
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
import WebKit
|
||||||
|
import CoreServices
|
||||||
|
|
||||||
|
extension Data {
|
||||||
|
|
||||||
|
/// Append string to Data
|
||||||
|
///
|
||||||
|
/// Rather than littering my code with calls to `data(using: .utf8)` to convert `String` values to `Data`, this wraps it in a nice convenient little extension to Data. This defaults to converting using UTF-8.
|
||||||
|
///
|
||||||
|
/// - parameter string: The string to be added to the `Data`.
|
||||||
|
|
||||||
|
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
|
||||||
|
if let data = string.data(using: encoding) {
|
||||||
|
append(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReverseImageViewController: UIViewController {
|
||||||
|
var post: Post?
|
||||||
|
@IBOutlet weak var webView: WKWebView!
|
||||||
|
|
||||||
|
let documentsPath : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteURL
|
||||||
|
|
||||||
|
private func mimeType(for path: String) -> String {
|
||||||
|
let url = URL(fileURLWithPath: path)
|
||||||
|
let pathExtension = url.pathExtension
|
||||||
|
|
||||||
|
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() {
|
||||||
|
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
|
||||||
|
return mimetype as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createBody(with parameters: [String: String]?, filePathKey: String, paths: [String], boundary: String) throws -> Data {
|
||||||
|
var body = Data()
|
||||||
|
|
||||||
|
if parameters != nil {
|
||||||
|
for (key, value) in parameters! {
|
||||||
|
body.append("--\(boundary)\r\n")
|
||||||
|
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
|
||||||
|
body.append("\(value)\r\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
let url = URL(fileURLWithPath: path)
|
||||||
|
let filename = url.lastPathComponent
|
||||||
|
let data = try Data(contentsOf: url)
|
||||||
|
let mimetype = mimeType(for: path)
|
||||||
|
|
||||||
|
body.append("--\(boundary)\r\n")
|
||||||
|
body.append("Content-Disposition: form-data; name=\"\(filePathKey)\"; filename=\"\(filename)\"\r\n")
|
||||||
|
body.append("Content-Type: \(mimetype)\r\n\r\n")
|
||||||
|
body.append(data)
|
||||||
|
body.append("\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
body.append("--\(boundary)--\r\n")
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
private func generateBoundaryString() -> String {
|
||||||
|
return "Boundary-\(UUID().uuidString)"
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
var components = URLComponents(string: "https://iqdb.org")
|
||||||
|
components?.queryItems = [URLQueryItem(name: "url", value: "true")]
|
||||||
|
if let result = components?.url {
|
||||||
|
let boundary = generateBoundaryString()
|
||||||
|
|
||||||
|
var request = URLRequest(url: result)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
|
||||||
|
|
||||||
|
let imagePath = documentsPath.appendingPathComponent(post!.value(forKey: "name") as! String).path
|
||||||
|
|
||||||
|
do {
|
||||||
|
request.httpBody = try createBody(with: nil, filePathKey: "file", paths: [imagePath], boundary: boundary)
|
||||||
|
} catch let error as NSError {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
webView!.load(request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func cancelAction(_ sender: Any) {
|
||||||
|
dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ReverseImageViewController {
|
||||||
|
static func loadFromStoryboard(post: Post) -> ReverseImageViewController? {
|
||||||
|
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||||
|
let viewController = storyboard.instantiateViewController(withIdentifier: "ReverseImageViewController") as? ReverseImageViewController
|
||||||
|
viewController!.post = post
|
||||||
|
|
||||||
|
return viewController
|
||||||
|
}
|
||||||
|
}
|
85
Gallery/SceneDelegate.swift
Normal file
85
Gallery/SceneDelegate.swift
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
var window: UIWindow?
|
||||||
|
|
||||||
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||||
|
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||||
|
if !configure(window: window, with: userActivity) {
|
||||||
|
print("Failed to restore from \(userActivity)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
guard let windowScene = (scene as? UIWindowScene) else { return }
|
||||||
|
let toolbar = NSToolbar(identifier: "MyToolbar")
|
||||||
|
toolbar.delegate = (window?.rootViewController as! UINavigationController).topViewController as? NSToolbarDelegate
|
||||||
|
windowScene.titlebar!.toolbar = toolbar
|
||||||
|
windowScene.titlebar!.titleVisibility = .hidden
|
||||||
|
toolbar.allowsUserCustomization = true
|
||||||
|
|
||||||
|
(window?.rootViewController as! UINavigationController).navigationBar.isHidden = true
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidDisconnect(_ scene: UIScene) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillResignActive(_ scene: UIScene) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||||
|
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(window: UIWindow?, with activity: NSUserActivity) -> Bool {
|
||||||
|
if activity.activityType == "post" {
|
||||||
|
if let photoID = activity.userInfo?["name"] as? String {
|
||||||
|
if let photoDetailViewController = PostViewController.loadFromStoryboard() {
|
||||||
|
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext =
|
||||||
|
appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Post")
|
||||||
|
|
||||||
|
request.predicate = NSPredicate(format: "name = %@", photoID)
|
||||||
|
request.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
|
do {
|
||||||
|
let result = try managedContext.fetch(request)
|
||||||
|
|
||||||
|
photoDetailViewController.post = result[0] as? NSManagedObject
|
||||||
|
photoDetailViewController.isPopup = true
|
||||||
|
|
||||||
|
if let navigationController = window?.rootViewController as? UINavigationController {
|
||||||
|
navigationController.pushViewController(photoDetailViewController, animated: false)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch _ as NSError {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
50
Gallery/SearchViewController.swift
Normal file
50
Gallery/SearchViewController.swift
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
class SearchViewController: UIViewController {
|
||||||
|
@IBOutlet weak var collectionView: PostCollectionView!
|
||||||
|
|
||||||
|
@IBOutlet weak var searchField: UITextField!
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
collectionView.viewController = self
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if segue.identifier == "showPost" {
|
||||||
|
let newViewController = segue.destination as! PostViewController
|
||||||
|
let index = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
|
|
||||||
|
newViewController.post = self.collectionView.posts[index!.row]
|
||||||
|
newViewController.image = (self.collectionView.cellForItem(at: index!) as! PostViewCell).imageView.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func searchAction(_ sender: Any) {
|
||||||
|
collectionView.actualInit(tag: searchField.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
|
if identifier == "showPost" {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
let index = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
|
|
||||||
|
let post = self.collectionView.posts[index!.row]
|
||||||
|
|
||||||
|
let activity = NSUserActivity(activityType: "post")
|
||||||
|
activity.userInfo = ["name": post.value(forKey: "name") as! String]
|
||||||
|
activity.isEligibleForHandoff = true
|
||||||
|
|
||||||
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)
|
||||||
|
|
||||||
|
return false
|
||||||
|
#else
|
||||||
|
return true
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||||
|
}
|
||||||
|
}
|
8
Gallery/TagViewCell.swift
Normal file
8
Gallery/TagViewCell.swift
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class TagViewCell: UITableViewCell {
|
||||||
|
|
||||||
|
@IBOutlet weak var label: UILabel!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
25
Gallery/TagViewController.swift
Normal file
25
Gallery/TagViewController.swift
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
class TagViewController: UIViewController {
|
||||||
|
var tag: String?
|
||||||
|
|
||||||
|
@IBOutlet weak var collectionView: PostCollectionView!
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
collectionView.actualInit(tag: self.tag)
|
||||||
|
collectionView.viewController = self
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if segue.identifier == "showPost" {
|
||||||
|
let newViewController = segue.destination as! PostViewController
|
||||||
|
let index = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
|
|
||||||
|
newViewController.post = self.collectionView.posts[index!.row]
|
||||||
|
newViewController.image = (self.collectionView.cellForItem(at: index!) as! PostViewCell).imageView.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
Gallery/ViewController.swift
Normal file
150
Gallery/ViewController.swift
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import UIKit
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
class ViewController: UIViewController, UIDocumentPickerDelegate {
|
||||||
|
@IBOutlet weak var collectionView: PostCollectionView!
|
||||||
|
|
||||||
|
let documentsPath : URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].absoluteURL
|
||||||
|
|
||||||
|
func importFile(path: URL) {
|
||||||
|
guard let appDelegate =
|
||||||
|
UIApplication.shared.delegate as? AppDelegate else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let managedContext = appDelegate.persistentContainer.viewContext
|
||||||
|
|
||||||
|
let entity = NSEntityDescription.entity(forEntityName: "Post", in: managedContext)!
|
||||||
|
|
||||||
|
let post = NSManagedObject(entity: entity, insertInto: managedContext)
|
||||||
|
|
||||||
|
let oldPath = path
|
||||||
|
let newPath = documentsPath.appendingPathComponent(path.lastPathComponent)
|
||||||
|
|
||||||
|
do {
|
||||||
|
try FileManager.default.copyItem(at: oldPath, to: newPath)
|
||||||
|
|
||||||
|
if let resourceValues = try? path.resourceValues(forKeys: [.typeIdentifierKey]),
|
||||||
|
let uti = resourceValues.typeIdentifier {
|
||||||
|
post.setValue(uti, forKeyPath: "type")
|
||||||
|
}
|
||||||
|
|
||||||
|
post.setValue(Date(), forKeyPath: "date")
|
||||||
|
post.setValue(path.lastPathComponent, forKeyPath: "name")
|
||||||
|
|
||||||
|
try managedContext.save()
|
||||||
|
collectionView.posts.append(post)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
} catch let error as NSError {
|
||||||
|
print("Could not save. \(error), \(error.userInfo)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
collectionView.actualInit(tag: nil)
|
||||||
|
collectionView.viewController = self
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
self.view.window?.windowScene!.title = "Home"
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func importAction(_ sender: Any) {
|
||||||
|
let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.image", "public.movie"], in: .import)
|
||||||
|
documentPicker.delegate = self as UIDocumentPickerDelegate
|
||||||
|
documentPicker.allowsMultipleSelection = true
|
||||||
|
|
||||||
|
present(documentPicker, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||||
|
for url in urls {
|
||||||
|
importFile(path: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
|
if segue.identifier == "showPost" {
|
||||||
|
let newViewController = segue.destination as! PostViewController
|
||||||
|
let index = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
|
|
||||||
|
newViewController.post = self.collectionView.posts[index!.row]
|
||||||
|
newViewController.image = (self.collectionView.cellForItem(at: index!) as! PostViewCell).imageView.image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
|
if identifier == "showPost" {
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
let index = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
|
|
||||||
|
let post = self.collectionView.posts[index!.row]
|
||||||
|
|
||||||
|
let activity = NSUserActivity(activityType: "post")
|
||||||
|
activity.userInfo = ["name": post.value(forKey: "name") as! String]
|
||||||
|
activity.isEligibleForHandoff = true
|
||||||
|
|
||||||
|
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)
|
||||||
|
|
||||||
|
return false
|
||||||
|
#else
|
||||||
|
return true
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.shouldPerformSegue(withIdentifier: identifier, sender: sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if targetEnvironment(macCatalyst)
|
||||||
|
private let OurButtonToolbarIdentifier = NSToolbarItem.Identifier(rawValue: "OurButton")
|
||||||
|
private let OurButtonToolbarIdentifier2 = NSToolbarItem.Identifier(rawValue: "OurButton2")
|
||||||
|
|
||||||
|
extension ViewController: NSToolbarDelegate {
|
||||||
|
@objc func searchAction() {
|
||||||
|
if(navigationController?.topViewController != self) {
|
||||||
|
navigationController?.popViewController(animated: true)
|
||||||
|
} else {
|
||||||
|
performSegue(withIdentifier: "search", sender: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||||
|
return [OurButtonToolbarIdentifier2, NSToolbarItem.Identifier.flexibleSpace,
|
||||||
|
OurButtonToolbarIdentifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||||
|
return toolbarDefaultItemIdentifiers(toolbar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||||
|
if (itemIdentifier == OurButtonToolbarIdentifier2) {
|
||||||
|
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.add,
|
||||||
|
target: self,
|
||||||
|
action: #selector(self.importAction))
|
||||||
|
let button = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: barButtonItem)
|
||||||
|
button.label = "Add";
|
||||||
|
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemIdentifier == OurButtonToolbarIdentifier) {
|
||||||
|
let barButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.search,
|
||||||
|
target: self,
|
||||||
|
action: #selector(self.searchAction))
|
||||||
|
let button = NSToolbarItem(itemIdentifier: itemIdentifier, barButtonItem: barButtonItem)
|
||||||
|
button.label = "Search";
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Joshua Goins
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Gallery
|
||||||
|
|
||||||
|
A local media gallery. You can import images and videos, and even tag them for searching and filtering.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The app is written in Swift, and uses UIKit and CoreData. Thanks to Catalyst, it can run on macOS as well as iOS and iPadOS.
|
BIN
misc/showcase.png
Normal file
BIN
misc/showcase.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 714 KiB |
Reference in a new issue