Compare commits
408 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c73d8292b5 | |||
| de59c814c6 | |||
| f588347f58 | |||
| dea5e8a4bd | |||
| 398ee143f0 | |||
| b7166be79f | |||
| 0119309a25 | |||
| 2ac28e0497 | |||
| 44c91b41c6 | |||
| 619cbba91e | |||
| 2fbb4ba5af | |||
| 9cc8c1972d | |||
| 88b08b262d | |||
| a3825a081a | |||
| 63004497df | |||
| daca12b64a | |||
| 3ac333b6a8 | |||
| 64573991e0 | |||
| 7e06642187 | |||
| 256610707e | |||
| a07de07adb | |||
| fbd246db01 | |||
| 7b1b9370ab | |||
| 502f9a6f92 | |||
| 2cac1cf4b2 | |||
| 918918649c | |||
| e36c794651 | |||
| 1f42c0229c | |||
| 6f0ead5af5 | |||
| 4a70c08f37 | |||
| 7830a7512a | |||
| 2bb85661b1 | |||
| dbffe4f688 | |||
| ed69a9fdee | |||
| 6344062be2 | |||
| 8dcf50b722 | |||
| 3c145a392b | |||
| 25e4877614 | |||
| 18743a36a5 | |||
| 8e254accd6 | |||
| d3e8d79d27 | |||
| e3b2df4363 | |||
| c25bbf4671 | |||
| c0e7c31844 | |||
| e43ba2b58f | |||
| 6d92df2dad | |||
| 5b85a492a6 | |||
| 774947dea5 | |||
| 21d6b335fc | |||
| b45f490bec | |||
| b37e139060 | |||
| 1e3636cebf | |||
| 54bb598dd8 | |||
| 1aa9643feb | |||
| 1bccfe2cc1 | |||
| 1b16b2bb74 | |||
| 32c45514c8 | |||
| e73f58ced5 | |||
| 54b73d3cc0 | |||
| 8fc34e8d12 | |||
| 6af9321ea9 | |||
| 1584281c41 | |||
| 5ac3f8ae18 | |||
| 5d93293649 | |||
| a4f113d066 | |||
| 41436d8083 | |||
| 22785514bd | |||
| d8f6836c0f | |||
| 20ea9e3e1c | |||
| 64dee3b6bc | |||
| 911d0c5df8 | |||
| cc85786658 | |||
| 3b695da3ee | |||
| 5a73ca4c6b | |||
| 90d31b75ca | |||
| 9d6d6e3798 | |||
| 81e13530aa | |||
| f11af83808 | |||
| 162f6d29c3 | |||
| cb97450102 | |||
| 9de5f16735 | |||
| 418da11c82 | |||
| 90fe59ffc6 | |||
| 422b23b4c2 | |||
| 08a4c284f4 | |||
| b775f12fe5 | |||
| 9f75ddffdf | |||
| 225fac033a | |||
| fe3229278d | |||
| de0d273258 | |||
| d1231f19ff | |||
| 20f927f338 | |||
| 8144d96129 | |||
| 0d9e3ef7ab | |||
| 888067cae0 | |||
| 0180ca76c7 | |||
| 951b95b0fb | |||
| 1803722b6a | |||
| 4f5ee72f97 | |||
| 9e17ebb58f | |||
| 28acb5a8d6 | |||
| 5c6fa430e0 | |||
| 5108f3651f | |||
| 05ec840ffd | |||
| 5189d880c4 | |||
| 308051a898 | |||
| 56481d5f8a | |||
| 3882befd7b | |||
| cf88115eb6 | |||
| 663bafce52 | |||
| 6911104a98 | |||
| 369252ee7c | |||
| 3322107688 | |||
| 7731adc79c | |||
| 5c48b2482a | |||
| 83416d6870 | |||
| 4f9540b4df | |||
| 304c3e61f6 | |||
| 026153f502 | |||
| 6e53ee01c7 | |||
| 9882c474a6 | |||
| 593c2ac5f3 | |||
| 23340fc075 | |||
| 0bd57097aa | |||
| 9ede29331a | |||
| 8f51e95e35 | |||
| eb4b12f633 | |||
| c00d8b62dd | |||
| 0d60426da2 | |||
| fec46dbd52 | |||
| 54da63c4a1 | |||
| 9d0a0a9811 | |||
| 401cfe185a | |||
| db80c7384c | |||
| d03d724746 | |||
| cb583ef202 | |||
| d92b2c3e79 | |||
| d0923f2660 | |||
| 961020ea94 | |||
| 8867836ff5 | |||
| aec061594d | |||
| 45abbf66ad | |||
| 59d59bd532 | |||
| 29f5e05f1b | |||
| 7885828fa3 | |||
| a69fb3da17 | |||
| 0e16f68dc1 | |||
| 5cd6b4b698 | |||
| b5d86f1f79 | |||
| 68a429be43 | |||
| ec1200faea | |||
| f0825e09d5 | |||
| 1e354c2675 | |||
| eade5ac4c8 | |||
| 8a33f9529b | |||
| 4abeb93ac7 | |||
| 619f84d89e | |||
| 9531315c94 | |||
| cfafbbcb14 | |||
| 6a6096e0b8 | |||
| 1480a44dfb | |||
| 2687d175c1 | |||
| c84682d3d9 | |||
| 6627936d23 | |||
| b26ee135bc | |||
| 96d7da82bb | |||
| 9707c7cfe4 | |||
| 4968853f8c | |||
| 053735d408 | |||
| 6052398040 | |||
| df48ce7b85 | |||
| eb44646978 | |||
| 6686d8f83b | |||
| 03be7479f4 | |||
| 3b54e8959d | |||
| 69813ee7fe | |||
| 4ccab620d6 | |||
| 3ea5a88e01 | |||
| 2b9216b1b8 | |||
| 48f307b003 | |||
| eb44c67f69 | |||
| e6452f116e | |||
| 96c660a424 | |||
| 4833dcde35 | |||
| 8f907c1fe9 | |||
| e314fea3e7 | |||
| b4ec75ca27 | |||
| f483716859 | |||
| 83397dc491 | |||
| f2319d70f8 | |||
| 2d03d8a7e1 | |||
| 04437d8450 | |||
| d31d74d0b5 | |||
| 40730ff2c5 | |||
| fa965b8a15 | |||
| 90c8c87565 | |||
| dbac1b89e3 | |||
| 770af6fcdc | |||
| c1be7f8d71 | |||
| c8a2b2be8a | |||
| b525124b3e | |||
| a74e9ff7ee | |||
| 40f6bd91c7 | |||
| e465067217 | |||
| 002dd5558b | |||
| cfacb850f9 | |||
| 98e3f62ebe | |||
| b6d1a36ee5 | |||
| 345bbd55b3 | |||
| 701ae968b9 | |||
| 01d697f1f5 | |||
| f0589a307c | |||
| 23b49e9535 | |||
| 66f6d69121 | |||
| 7bb0628b42 | |||
| 5b439af899 | |||
| c4f2df441f | |||
| b1c7001b3b | |||
| d70305993a | |||
| a6119a3cc0 | |||
| d1b82ee0d6 | |||
| 0495a8c547 | |||
| b3abbd1f7a | |||
| 7edaaa9e69 | |||
| e6b3e5a23b | |||
| a0d45b7faf | |||
| ec74f08998 | |||
| 87949871e1 | |||
| 597181b672 | |||
| c6795ad84d | |||
| ddc67ff70b | |||
| ee9fc3dac9 | |||
| e46ac10e84 | |||
| da39d8a72e | |||
| 222414ecaf | |||
| 5af42a50ad | |||
| 84db79b258 | |||
| 9328d6af4e | |||
| 513d988939 | |||
| 35997d1e33 | |||
| 4cef861fe9 | |||
| 981fe7a222 | |||
| 1499bf5ecf | |||
| b8eacf9682 | |||
| 6a36b73105 | |||
| 7f71186b9f | |||
| 94c0969f6a | |||
| 92768bec5c | |||
| a4e4f1363f | |||
| 2050f6d220 | |||
| c0b5dc713f | |||
| d805d1ff99 | |||
| 2752b4c213 | |||
| f625c8dcf5 | |||
| 67d3ad03d4 | |||
| 67d52df14d | |||
| a6d21f4488 | |||
| 7f0a6198f7 | |||
| b1e2191471 | |||
| 289954e998 | |||
| 012ac4f7a0 | |||
| 3a2f9293a9 | |||
| 36ef02a69a | |||
| 737ccdc826 | |||
| b37a1267d4 | |||
| de3405b655 | |||
| a52c22c4fd | |||
| 236b3fb8ea | |||
| 97de34d528 | |||
| 2e8b8d83b3 | |||
| 44dde5db10 | |||
| 3d783a7cd1 | |||
| fd98192516 | |||
| 15287809aa | |||
| 976df91288 | |||
| 28c8d37588 | |||
| b15fc10a53 | |||
| a64d12bfcd | |||
| 74a000d632 | |||
| 947c49008a | |||
| 764f2e3bd4 | |||
| 07231ff480 | |||
| 70167b93c2 | |||
| 133ca2db71 | |||
| d68fa03a90 | |||
| c144d329e3 | |||
| 29cd3b8156 | |||
| 92ae29af6b | |||
| de0aa6399a | |||
| 4bfdc39619 | |||
| 5ecb642592 | |||
| 217977b4d0 | |||
| e9f1178a41 | |||
| 30c9097671 | |||
| f22fa5fd12 | |||
| d98e49b619 | |||
| aa23e5eae5 | |||
| f1797e4aa0 | |||
| 3e04ad1ef1 | |||
| 0d4bb14d7e | |||
| db24073a03 | |||
| 9c8f008c9a | |||
| 4a4d40194f | |||
| e069779449 | |||
| f6bad9afe7 | |||
| 327f48b380 | |||
| 4d3407f366 | |||
| e7c511644f | |||
| b0f99a8529 | |||
| 7e9a7aae75 | |||
| b8682ba8ff | |||
| 3d3f7c36c2 | |||
| c8b046d3c1 | |||
| 151aa54e41 | |||
| 7fa25f1b8a | |||
| 57874700bc | |||
| 0abd6b7eee | |||
| 6a06b0f504 | |||
| cbbb2b1553 | |||
| f806688547 | |||
| ba60f3eaf3 | |||
| 6065273c22 | |||
| 50e1ed3b3a | |||
| bf7dbe02b5 | |||
| fbaa24860a | |||
| c7d52ad61f | |||
| 6ef8b11817 | |||
| 46018422a4 | |||
| 8f750a4501 | |||
| 996b519a27 | |||
| f0ef356066 | |||
| 9990486cf9 | |||
| 73c34d36d7 | |||
| 6e430013ee | |||
| f3e7c36599 | |||
| bcdaf4c013 | |||
| 27eb4f08cf | |||
| 20dc66f5a8 | |||
| 247fbcbd37 | |||
| 2abe24b17b | |||
| 138a9de748 | |||
| 5f1bc2a3ef | |||
| 7e160a2382 | |||
| c24e4b3dbe | |||
| 4b699d70f5 | |||
| 02376b6313 | |||
| 1c8dddfa65 | |||
| 6f098603fe | |||
| 7a190be28d | |||
| 0f2ea3673a | |||
| 2b0f12f581 | |||
| 3312be7a70 | |||
| 49b370f4a5 | |||
| 68d5cff2b4 | |||
| 7b124968bd | |||
| f18a0d87e2 | |||
| 2c94be7958 | |||
| 4aec366007 | |||
| 268ba7a845 | |||
| 0f1e5bed8a | |||
| 9901ef2031 | |||
| 9b26d1ae88 | |||
| 8505cdd8dc | |||
| f7c8801958 | |||
| 18fa934091 | |||
| d2fcfe83e9 | |||
| 3d34271eca | |||
| 1aff585870 | |||
| c2ac20cac5 | |||
| 7e1ea31e87 | |||
| 2c24e1e9e0 | |||
| 020df1ef9c | |||
| ba5edcf2c0 | |||
| f6fb1bdc44 | |||
| b9fbaa95df | |||
| 8bf2ea3a3c | |||
| 9f31ca056a | |||
| bda3e3b87f | |||
| 9607b24c34 | |||
| c623ba869d | |||
| b2e9da3dbd | |||
| f60deafe0e | |||
| 87cbe6b48c | |||
| 0e7189ae17 | |||
| f3895b8eca | |||
| 18484ac111 | |||
| 11bd59c5ea | |||
| bc9689125b | |||
| 495fc0aab4 | |||
| c6044cecfe | |||
| 940b44f14f | |||
| 75ad50d74a | |||
| 8eb367b662 | |||
| b7a89b80f4 | |||
| 27d58fdced | |||
| 647ab2f558 | |||
| 9fd3421998 | |||
| 23c1e8dadf | |||
| 1f1209983f | |||
| 6946a9941e | |||
| fac4325d51 | |||
| 8a2f229c53 | |||
| 8edf8a70fa | |||
| d181b28e91 | |||
| 270f4e887f | |||
| 49766f0833 | |||
| 56664f0b58 | |||
| a259b35826 |
@@ -28,3 +28,6 @@ DerivedData
|
||||
|
||||
# CocoaPods
|
||||
Pods
|
||||
|
||||
# Carthage
|
||||
Carthage
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
ci_service: travis_ci
|
||||
coverage_service: coveralls
|
||||
xcodeproj: Tests/Tests.xcodeproj
|
||||
source_directory: Source
|
||||
@@ -0,0 +1,47 @@
|
||||
opt_in_rules: # some rules are only opt-in
|
||||
- empty_count
|
||||
# Find all the available rules by running:
|
||||
# swiftlint rules
|
||||
included: # paths to include during linting. `--path` is ignored if present.
|
||||
- Source
|
||||
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
||||
- Carthage
|
||||
- Pods
|
||||
|
||||
# configurable rules can be customized from this configuration file
|
||||
# binary rules can set their severity level
|
||||
force_cast: warning # implicitly
|
||||
force_try:
|
||||
severity: warning # explicitly
|
||||
# rules that have both warning and error levels, can set just the warning level
|
||||
# implicitly
|
||||
line_length: 200
|
||||
# they can set both implicitly with an array
|
||||
type_body_length:
|
||||
- 300 # warning
|
||||
- 400 # error
|
||||
# or they can set both explicitly
|
||||
file_length:
|
||||
warning: 500
|
||||
error: 1200
|
||||
# naming rules can set warnings/errors for min_length and max_length
|
||||
# additionally they can set excluded names
|
||||
type_name:
|
||||
min_length: 3 # only warning
|
||||
max_length: # warning and error
|
||||
warning: 40
|
||||
error: 50
|
||||
excluded: iPhone # excluded via string
|
||||
variable_name:
|
||||
min_length: # only min_length
|
||||
error: 2 # only error
|
||||
excluded: # excluded via string array
|
||||
- x
|
||||
- y
|
||||
- id
|
||||
- URL
|
||||
- GlobalAPIKey
|
||||
- i
|
||||
- lb
|
||||
- rb
|
||||
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle)
|
||||
@@ -1,12 +0,0 @@
|
||||
language: objective-c
|
||||
cache: cocoapods
|
||||
before_install: gem install cocoapods obcd slather -N
|
||||
|
||||
# Use when you don't have third party dependencies
|
||||
script: xctool -project Pod/Pod.xcodeproj -scheme Tests -sdk iphonesimulator build test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES clean test
|
||||
|
||||
# Use when you have third party dependencies (CocoaPods generates a workspace)
|
||||
# podfile: Pod/Podfile
|
||||
# script: xctool -workspace Pod/Pod.xcworkspace -scheme Tests -sdk iphonesimulator build test GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES clean test
|
||||
|
||||
after_success: slather
|
||||
@@ -0,0 +1,2 @@
|
||||
github "hyperoslo/Imaginary" ~> 5.0.0
|
||||
github "hyperoslo/Cache" ~> 6.0.0
|
||||
@@ -0,0 +1,2 @@
|
||||
github "hyperoslo/Cache" "6.0.0"
|
||||
github "hyperoslo/Imaginary" "5.0.0"
|
||||
@@ -1,367 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0C9AD201C04B66C0716FF73F /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8728C35FD683AF84D7CC1A5D /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
298CC3D01B5685B300BA502F /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 298CC3CF1B5685B300BA502F /* MainViewController.swift */; };
|
||||
D5C0BDFB1B42CCBC009E6446 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C0BDFA1B42CCBC009E6446 /* AppDelegate.swift */; };
|
||||
D5C0BE021B42CCBC009E6446 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D5C0BE011B42CCBC009E6446 /* Images.xcassets */; };
|
||||
D5C0BE051B42CCBC009E6446 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D5C0BE031B42CCBC009E6446 /* LaunchScreen.xib */; };
|
||||
D5C0BE1B1B42CF62009E6446 /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = D5C0BE1A1B42CF62009E6446 /* Podfile */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
23BFEEC636035C79B9D935A8 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
298CC3CF1B5685B300BA502F /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
|
||||
76BF2975E45497D931B2B9C8 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
8728C35FD683AF84D7CC1A5D /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D5C0BDF51B42CCBC009E6446 /* LightboxDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LightboxDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D5C0BDF91B42CCBC009E6446 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D5C0BDFA1B42CCBC009E6446 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
D5C0BE011B42CCBC009E6446 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
D5C0BE041B42CCBC009E6446 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
D5C0BE1A1B42CF62009E6446 /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D5C0BDF21B42CCBC009E6446 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C9AD201C04B66C0716FF73F /* Pods.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0A38C4CB9ED946C3E5379ACD /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8728C35FD683AF84D7CC1A5D /* Pods.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7CC838A3597315C7F8990A20 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
23BFEEC636035C79B9D935A8 /* Pods.debug.xcconfig */,
|
||||
76BF2975E45497D931B2B9C8 /* Pods.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D5C0BDEC1B42CCBC009E6446 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D5C0BE1A1B42CF62009E6446 /* Podfile */,
|
||||
D5C0BDF71B42CCBC009E6446 /* LightboxDemo */,
|
||||
D5C0BDF61B42CCBC009E6446 /* Products */,
|
||||
7CC838A3597315C7F8990A20 /* Pods */,
|
||||
0A38C4CB9ED946C3E5379ACD /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D5C0BDF61B42CCBC009E6446 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D5C0BDF51B42CCBC009E6446 /* LightboxDemo.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D5C0BDF71B42CCBC009E6446 /* LightboxDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D5C0BDFA1B42CCBC009E6446 /* AppDelegate.swift */,
|
||||
298CC3CF1B5685B300BA502F /* MainViewController.swift */,
|
||||
D5C0BE011B42CCBC009E6446 /* Images.xcassets */,
|
||||
D5C0BE031B42CCBC009E6446 /* LaunchScreen.xib */,
|
||||
D5C0BDF81B42CCBC009E6446 /* Supporting Files */,
|
||||
);
|
||||
path = LightboxDemo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D5C0BDF81B42CCBC009E6446 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D5C0BDF91B42CCBC009E6446 /* Info.plist */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D5C0BDF41B42CCBC009E6446 /* LightboxDemo */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D5C0BE141B42CCBC009E6446 /* Build configuration list for PBXNativeTarget "LightboxDemo" */;
|
||||
buildPhases = (
|
||||
33BD1BD41EDB7355D8EEE7B2 /* Check Pods Manifest.lock */,
|
||||
D5C0BDF11B42CCBC009E6446 /* Sources */,
|
||||
D5C0BDF21B42CCBC009E6446 /* Frameworks */,
|
||||
D5C0BDF31B42CCBC009E6446 /* Resources */,
|
||||
BC0AD25D0149B45E9C4A9A25 /* Embed Pods Frameworks */,
|
||||
856840040ECE7A9C6538CCF0 /* Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = LightboxDemo;
|
||||
productName = LightboxDemo;
|
||||
productReference = D5C0BDF51B42CCBC009E6446 /* LightboxDemo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
D5C0BDED1B42CCBC009E6446 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0710;
|
||||
LastUpgradeCheck = 0630;
|
||||
ORGANIZATIONNAME = Hyper;
|
||||
TargetAttributes = {
|
||||
D5C0BDF41B42CCBC009E6446 = {
|
||||
CreatedOnToolsVersion = 6.3.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = D5C0BDF01B42CCBC009E6446 /* Build configuration list for PBXProject "LightboxDemo" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = D5C0BDEC1B42CCBC009E6446;
|
||||
productRefGroup = D5C0BDF61B42CCBC009E6446 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
D5C0BDF41B42CCBC009E6446 /* LightboxDemo */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
D5C0BDF31B42CCBC009E6446 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D5C0BE1B1B42CF62009E6446 /* Podfile in Resources */,
|
||||
D5C0BE051B42CCBC009E6446 /* LaunchScreen.xib in Resources */,
|
||||
D5C0BE021B42CCBC009E6446 /* Images.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
33BD1BD41EDB7355D8EEE7B2 /* Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
856840040ECE7A9C6538CCF0 /* Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BC0AD25D0149B45E9C4A9A25 /* Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D5C0BDF11B42CCBC009E6446 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D5C0BDFB1B42CCBC009E6446 /* AppDelegate.swift in Sources */,
|
||||
298CC3D01B5685B300BA502F /* MainViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
D5C0BE031B42CCBC009E6446 /* LaunchScreen.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
D5C0BE041B42CCBC009E6446 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
D5C0BE121B42CCBC009E6446 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
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 = 8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D5C0BE131B42CCBC009E6446 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D5C0BE151B42CCBC009E6446 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 23BFEEC636035C79B9D935A8 /* Pods.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = LightboxDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D5C0BE161B42CCBC009E6446 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 76BF2975E45497D931B2B9C8 /* Pods.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
INFOPLIST_FILE = LightboxDemo/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
D5C0BDF01B42CCBC009E6446 /* Build configuration list for PBXProject "LightboxDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D5C0BE121B42CCBC009E6446 /* Debug */,
|
||||
D5C0BE131B42CCBC009E6446 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D5C0BE141B42CCBC009E6446 /* Build configuration list for PBXNativeTarget "LightboxDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D5C0BE151B42CCBC009E6446 /* Debug */,
|
||||
D5C0BE161B42CCBC009E6446 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = D5C0BDED1B42CCBC009E6446 /* Project object */;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:LightboxDemo.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:LightboxDemo.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -1,20 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
lazy var window: UIWindow? = {
|
||||
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
|
||||
return window
|
||||
}()
|
||||
|
||||
func application(application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
|
||||
window!.rootViewController = MainViewController()
|
||||
window!.backgroundColor = UIColor.whiteColor()
|
||||
window!.makeKeyAndVisible()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2015 Hyper. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="20" y="439" width="441" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="LightboxDemo" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
|
||||
<rect key="frame" x="20" y="140" width="441" height="43"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
|
||||
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
|
||||
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="548" y="455"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 309 KiB |
|
Before Width: | Height: | Size: 320 KiB |
@@ -1,40 +0,0 @@
|
||||
import UIKit
|
||||
import Lightbox
|
||||
|
||||
class MainViewController: UIViewController {
|
||||
|
||||
lazy var galleryButton: UIButton = { [unowned self] in
|
||||
let button = UIButton()
|
||||
button.setTitle("Show the gallery", forState: .Normal)
|
||||
button.setTitleColor(UIColor(red:0.98, green:0.18, blue:0.36, alpha:1), forState: .Normal)
|
||||
button.titleLabel!.font = UIFont(name: "AvenirNextCondensed-DemiBold", size: 24)
|
||||
button.addTarget(self, action: "galleryButtonDidPress:", forControlEvents: .TouchUpInside)
|
||||
button.frame = CGRectMake(0, 0,
|
||||
UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.addSubview(galleryButton)
|
||||
}
|
||||
|
||||
// MARK: Action handlers
|
||||
|
||||
func galleryButtonDidPress(button: UIButton) {
|
||||
let controller = LightboxController(images: ["photo1", "photo2", "photo3"])
|
||||
controller.dismissalDelegate = self
|
||||
presentViewController(controller, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Lightbox delegate methods
|
||||
|
||||
extension MainViewController : LightboxControllerDismissalDelegate {
|
||||
|
||||
func lightboxControllerDidDismiss(controller: LightboxController) {
|
||||
controller.dismissViewControllerAnimated(true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
platform :ios, '8.0'
|
||||
|
||||
use_frameworks!
|
||||
inhibit_all_warnings!
|
||||
|
||||
pod 'Lightbox', path: '../../'
|
||||
@@ -1,14 +0,0 @@
|
||||
PODS:
|
||||
- Lightbox (0.1.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Lightbox (from `../../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Lightbox:
|
||||
:path: ../../
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Lightbox: 4f31e0609376469443c9b0df85efe1c737209aa5
|
||||
|
||||
COCOAPODS: 0.39.0.beta.4
|
||||
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 296 KiB |
@@ -1,15 +1,18 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Lightbox"
|
||||
s.summary = "A short description of Lightbox."
|
||||
s.version = "0.1.0"
|
||||
s.summary = "A a convenient and easy to use image viewer for your iOS app, packed with all the features you expect"
|
||||
s.version = "2.5.0"
|
||||
s.homepage = "https://github.com/hyperoslo/Lightbox"
|
||||
s.license = 'MIT'
|
||||
s.author = { "Hyper Interaktiv AS" => "ios@hyper.no" }
|
||||
s.source = { :git => "https://github.com/hyperoslo/Lightbox.git", :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/hyperoslo'
|
||||
s.platform = :ios, '8.0'
|
||||
s.platform = :ios, '11.0'
|
||||
s.requires_arc = true
|
||||
s.source_files = 'Source/**/*'
|
||||
# s.frameworks = 'UIKit', 'MapKit'
|
||||
# s.dependency 'AFNetworking', '~> 2.3'
|
||||
s.ios.resource = 'Resources/Lightbox.bundle'
|
||||
|
||||
s.frameworks = 'UIKit', 'AVFoundation', 'AVKit'
|
||||
s.swift_version = '5.0'
|
||||
|
||||
end
|
||||
|
||||
@@ -3,55 +3,150 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 52;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D523B0BC1C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B51C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift */; };
|
||||
166E3BA920333E04006799C1 /* LightboxImageStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 166E3BA820333E04006799C1 /* LightboxImageStub.swift */; };
|
||||
44E6A64A2495BFAB00543CF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E6A6492495BFAB00543CF0 /* AppDelegate.swift */; };
|
||||
44E6A6532495BFAC00543CF0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44E6A6522495BFAC00543CF0 /* Assets.xcassets */; };
|
||||
44E6A6562495BFAC00543CF0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44E6A6542495BFAC00543CF0 /* LaunchScreen.storyboard */; };
|
||||
44E6A65C2495BFD400543CF0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E6A65B2495BFD400543CF0 /* ViewController.swift */; };
|
||||
44E6A6652495C0EB00543CF0 /* Lightbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D523B0A91C43AA2A001AD1EC /* Lightbox.framework */; };
|
||||
44E6A6662495C0EB00543CF0 /* Lightbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D523B0A91C43AA2A001AD1EC /* Lightbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
85CB007027B16C6900A47BB3 /* SDWebImage in Frameworks */ = {isa = PBXBuildFile; productRef = 85CB006F27B16C6900A47BB3 /* SDWebImage */; };
|
||||
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D22006731DFB4D9700E92898 /* Lightbox.bundle */; };
|
||||
D2258CC4215CD035005A9A1C /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2258CC3215CD035005A9A1C /* Color+Extensions.swift */; };
|
||||
D2D71BBC1D54DA77006AB907 /* AssetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D71BBB1D54DA77006AB907 /* AssetManager.swift */; };
|
||||
D5026B3C1C5BF3FD003BC1A3 /* LightboxImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5026B3B1C5BF3FD003BC1A3 /* LightboxImage.swift */; };
|
||||
D523B0BD1C43AA8B001AD1EC /* LightboxConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B61C43AA8A001AD1EC /* LightboxConfig.swift */; };
|
||||
D523B0BE1C43AA8B001AD1EC /* LightboxController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B71C43AA8A001AD1EC /* LightboxController.swift */; };
|
||||
D523B0BF1C43AA8B001AD1EC /* LightboxDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B81C43AA8A001AD1EC /* LightboxDataSource.swift */; };
|
||||
D523B0C01C43AA8B001AD1EC /* LightboxTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B91C43AA8A001AD1EC /* LightboxTransition.swift */; };
|
||||
D523B0C11C43AA8B001AD1EC /* LightboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0BA1C43AA8A001AD1EC /* LightboxView.swift */; };
|
||||
D523B0C21C43AA8B001AD1EC /* LightboxViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0BB1C43AA8A001AD1EC /* LightboxViewCell.swift */; };
|
||||
D54DFCBE1C5AAAD600ADEA0E /* InfoLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DFCBC1C5AAAD600ADEA0E /* InfoLabel.swift */; };
|
||||
D54DFCBF1C5AAAD600ADEA0E /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54DFCBD1C5AAAD600ADEA0E /* PageView.swift */; };
|
||||
D56F15C81E0AB79800F128AF /* LoadingIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D56F15C71E0AB79800F128AF /* LoadingIndicator.swift */; };
|
||||
D573A2F01C5B5605006053DD /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D573A2EF1C5B5605006053DD /* HeaderView.swift */; };
|
||||
D573A2F31C5B5C7B006053DD /* LayoutConfigurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D573A2F21C5B5C7B006053DD /* LayoutConfigurable.swift */; };
|
||||
D573A2F51C5B5CA4006053DD /* LightboxTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D573A2F41C5B5CA4006053DD /* LightboxTransition.swift */; };
|
||||
D573A2F71C5B5E55006053DD /* UIView+Gradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D573A2F61C5B5E55006053DD /* UIView+Gradient.swift */; };
|
||||
D58A18CB1C5ABF8F000024BB /* FooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58A18CA1C5ABF8F000024BB /* FooterView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
44E6A6672495C0EB00543CF0 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D523B0A01C43AA2A001AD1EC /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D523B0A81C43AA2A001AD1EC;
|
||||
remoteInfo = "Lightbox-iOS";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
44E6A6692495C0EB00543CF0 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
44E6A6662495C0EB00543CF0 /* Lightbox.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
166E3BA820333E04006799C1 /* LightboxImageStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightboxImageStub.swift; sourceTree = "<group>"; };
|
||||
44E6A6472495BFAB00543CF0 /* iOSDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
44E6A6492495BFAB00543CF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
44E6A6522495BFAC00543CF0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
44E6A6552495BFAC00543CF0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
44E6A6572495BFAC00543CF0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
44E6A65B2495BFD400543CF0 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
D22006731DFB4D9700E92898 /* Lightbox.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Lightbox.bundle; sourceTree = "<group>"; };
|
||||
D2258CC3215CD035005A9A1C /* Color+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D2D71BBB1D54DA77006AB907 /* AssetManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetManager.swift; sourceTree = "<group>"; };
|
||||
D5026B3B1C5BF3FD003BC1A3 /* LightboxImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxImage.swift; sourceTree = "<group>"; };
|
||||
D523B0A91C43AA2A001AD1EC /* Lightbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Lightbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D523B0AE1C43AA2A001AD1EC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D523B0B51C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CenterCellCollectionViewFlowLayout.swift; sourceTree = "<group>"; };
|
||||
D523B0B61C43AA8A001AD1EC /* LightboxConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxConfig.swift; sourceTree = "<group>"; };
|
||||
D523B0B71C43AA8A001AD1EC /* LightboxController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxController.swift; sourceTree = "<group>"; };
|
||||
D523B0B81C43AA8A001AD1EC /* LightboxDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxDataSource.swift; sourceTree = "<group>"; };
|
||||
D523B0B91C43AA8A001AD1EC /* LightboxTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxTransition.swift; sourceTree = "<group>"; };
|
||||
D523B0BA1C43AA8A001AD1EC /* LightboxView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxView.swift; sourceTree = "<group>"; };
|
||||
D523B0BB1C43AA8A001AD1EC /* LightboxViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxViewCell.swift; sourceTree = "<group>"; };
|
||||
D54DFCBC1C5AAAD600ADEA0E /* InfoLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InfoLabel.swift; sourceTree = "<group>"; };
|
||||
D54DFCBD1C5AAAD600ADEA0E /* PageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageView.swift; sourceTree = "<group>"; };
|
||||
D56F15C71E0AB79800F128AF /* LoadingIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingIndicator.swift; sourceTree = "<group>"; };
|
||||
D573A2EF1C5B5605006053DD /* HeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
||||
D573A2F21C5B5C7B006053DD /* LayoutConfigurable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutConfigurable.swift; sourceTree = "<group>"; };
|
||||
D573A2F41C5B5CA4006053DD /* LightboxTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxTransition.swift; sourceTree = "<group>"; };
|
||||
D573A2F61C5B5E55006053DD /* UIView+Gradient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Gradient.swift"; sourceTree = "<group>"; };
|
||||
D58A18CA1C5ABF8F000024BB /* FooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FooterView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
44E6A6442495BFAB00543CF0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
44E6A6652495C0EB00543CF0 /* Lightbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D523B0A51C43AA2A001AD1EC /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
85CB007027B16C6900A47BB3 /* SDWebImage in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
44E6A6482495BFAB00543CF0 /* iOSDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
44E6A6492495BFAB00543CF0 /* AppDelegate.swift */,
|
||||
44E6A65B2495BFD400543CF0 /* ViewController.swift */,
|
||||
44E6A6522495BFAC00543CF0 /* Assets.xcassets */,
|
||||
44E6A6542495BFAC00543CF0 /* LaunchScreen.storyboard */,
|
||||
44E6A6572495BFAC00543CF0 /* Info.plist */,
|
||||
);
|
||||
path = iOSDemo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D22006721DFB4D9700E92898 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D22006731DFB4D9700E92898 /* Lightbox.bundle */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D2A58F5C1F7943A30064F14E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D523B09F1C43AA2A001AD1EC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D22006721DFB4D9700E92898 /* Resources */,
|
||||
D523B0B41C43AA8A001AD1EC /* Source */,
|
||||
D523B0AB1C43AA2A001AD1EC /* Lightbox */,
|
||||
44E6A6482495BFAB00543CF0 /* iOSDemo */,
|
||||
D523B0AA1C43AA2A001AD1EC /* Products */,
|
||||
D2A58F5C1F7943A30064F14E /* Frameworks */,
|
||||
);
|
||||
indentWidth = 4;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 4;
|
||||
};
|
||||
D523B0AA1C43AA2A001AD1EC /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D523B0A91C43AA2A001AD1EC /* Lightbox.framework */,
|
||||
44E6A6472495BFAB00543CF0 /* iOSDemo.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -67,17 +162,40 @@
|
||||
D523B0B41C43AA8A001AD1EC /* Source */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D523B0B51C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift */,
|
||||
D573A2F11C5B5C72006053DD /* Library */,
|
||||
D54DFCBB1C5AAAD600ADEA0E /* Views */,
|
||||
D523B0B61C43AA8A001AD1EC /* LightboxConfig.swift */,
|
||||
D523B0B71C43AA8A001AD1EC /* LightboxController.swift */,
|
||||
D523B0B81C43AA8A001AD1EC /* LightboxDataSource.swift */,
|
||||
D523B0B91C43AA8A001AD1EC /* LightboxTransition.swift */,
|
||||
D523B0BA1C43AA8A001AD1EC /* LightboxView.swift */,
|
||||
D523B0BB1C43AA8A001AD1EC /* LightboxViewCell.swift */,
|
||||
D5026B3B1C5BF3FD003BC1A3 /* LightboxImage.swift */,
|
||||
166E3BA820333E04006799C1 /* LightboxImageStub.swift */,
|
||||
D2D71BBB1D54DA77006AB907 /* AssetManager.swift */,
|
||||
);
|
||||
path = Source;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D54DFCBB1C5AAAD600ADEA0E /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D56F15C71E0AB79800F128AF /* LoadingIndicator.swift */,
|
||||
D573A2EF1C5B5605006053DD /* HeaderView.swift */,
|
||||
D58A18CA1C5ABF8F000024BB /* FooterView.swift */,
|
||||
D54DFCBD1C5AAAD600ADEA0E /* PageView.swift */,
|
||||
D54DFCBC1C5AAAD600ADEA0E /* InfoLabel.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D573A2F11C5B5C72006053DD /* Library */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D573A2F41C5B5CA4006053DD /* LightboxTransition.swift */,
|
||||
D573A2F21C5B5C7B006053DD /* LayoutConfigurable.swift */,
|
||||
D573A2F61C5B5E55006053DD /* UIView+Gradient.swift */,
|
||||
D2258CC3215CD035005A9A1C /* Color+Extensions.swift */,
|
||||
);
|
||||
path = Library;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -91,6 +209,25 @@
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
44E6A6462495BFAB00543CF0 /* iOSDemo */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 44E6A6582495BFAC00543CF0 /* Build configuration list for PBXNativeTarget "iOSDemo" */;
|
||||
buildPhases = (
|
||||
44E6A6432495BFAB00543CF0 /* Sources */,
|
||||
44E6A6442495BFAB00543CF0 /* Frameworks */,
|
||||
44E6A6452495BFAB00543CF0 /* Resources */,
|
||||
44E6A6692495C0EB00543CF0 /* Embed Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
44E6A6682495C0EB00543CF0 /* PBXTargetDependency */,
|
||||
);
|
||||
name = iOSDemo;
|
||||
productName = iOSDemo;
|
||||
productReference = 44E6A6472495BFAB00543CF0 /* iOSDemo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D523B0B11C43AA2A001AD1EC /* Build configuration list for PBXNativeTarget "Lightbox-iOS" */;
|
||||
@@ -105,6 +242,9 @@
|
||||
dependencies = (
|
||||
);
|
||||
name = "Lightbox-iOS";
|
||||
packageProductDependencies = (
|
||||
85CB006F27B16C6900A47BB3 /* SDWebImage */,
|
||||
);
|
||||
productName = Lightbox;
|
||||
productReference = D523B0A91C43AA2A001AD1EC /* Lightbox.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
@@ -115,74 +255,204 @@
|
||||
D523B0A01C43AA2A001AD1EC /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0720;
|
||||
LastSwiftUpdateCheck = 1150;
|
||||
LastUpgradeCheck = 1320;
|
||||
ORGANIZATIONNAME = "Hyper Interaktiv AS";
|
||||
TargetAttributes = {
|
||||
44E6A6462495BFAB00543CF0 = {
|
||||
CreatedOnToolsVersion = 11.5;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
D523B0A81C43AA2A001AD1EC = {
|
||||
CreatedOnToolsVersion = 7.2;
|
||||
LastSwiftMigration = 1150;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = D523B0A31C43AA2A001AD1EC /* Build configuration list for PBXProject "Lightbox" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = D523B09F1C43AA2A001AD1EC;
|
||||
packageReferences = (
|
||||
85CB006E27B16C6900A47BB3 /* XCRemoteSwiftPackageReference "SDWebImage" */,
|
||||
);
|
||||
productRefGroup = D523B0AA1C43AA2A001AD1EC /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */,
|
||||
44E6A6462495BFAB00543CF0 /* iOSDemo */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
44E6A6452495BFAB00543CF0 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
44E6A6562495BFAC00543CF0 /* LaunchScreen.storyboard in Resources */,
|
||||
44E6A6532495BFAC00543CF0 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D523B0A71C43AA2A001AD1EC /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
44E6A6432495BFAB00543CF0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
44E6A65C2495BFD400543CF0 /* ViewController.swift in Sources */,
|
||||
44E6A64A2495BFAB00543CF0 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D523B0A41C43AA2A001AD1EC /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D56F15C81E0AB79800F128AF /* LoadingIndicator.swift in Sources */,
|
||||
D54DFCBF1C5AAAD600ADEA0E /* PageView.swift in Sources */,
|
||||
D573A2F71C5B5E55006053DD /* UIView+Gradient.swift in Sources */,
|
||||
D523B0BD1C43AA8B001AD1EC /* LightboxConfig.swift in Sources */,
|
||||
D523B0C11C43AA8B001AD1EC /* LightboxView.swift in Sources */,
|
||||
D573A2F31C5B5C7B006053DD /* LayoutConfigurable.swift in Sources */,
|
||||
D5026B3C1C5BF3FD003BC1A3 /* LightboxImage.swift in Sources */,
|
||||
D523B0BE1C43AA8B001AD1EC /* LightboxController.swift in Sources */,
|
||||
D523B0C21C43AA8B001AD1EC /* LightboxViewCell.swift in Sources */,
|
||||
D523B0BC1C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift in Sources */,
|
||||
D523B0C01C43AA8B001AD1EC /* LightboxTransition.swift in Sources */,
|
||||
D523B0BF1C43AA8B001AD1EC /* LightboxDataSource.swift in Sources */,
|
||||
D54DFCBE1C5AAAD600ADEA0E /* InfoLabel.swift in Sources */,
|
||||
D58A18CB1C5ABF8F000024BB /* FooterView.swift in Sources */,
|
||||
D573A2F01C5B5605006053DD /* HeaderView.swift in Sources */,
|
||||
D573A2F51C5B5CA4006053DD /* LightboxTransition.swift in Sources */,
|
||||
D2258CC4215CD035005A9A1C /* Color+Extensions.swift in Sources */,
|
||||
166E3BA920333E04006799C1 /* LightboxImageStub.swift in Sources */,
|
||||
D2D71BBC1D54DA77006AB907 /* AssetManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
44E6A6682495C0EB00543CF0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */;
|
||||
targetProxy = 44E6A6672495C0EB00543CF0 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
44E6A6542495BFAC00543CF0 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
44E6A6552495BFAC00543CF0 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
44E6A6592495BFAC00543CF0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Carthage/Build/iOS",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = iOSDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.elvisnunez.iOSDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
44E6A65A2495BFAC00543CF0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Carthage/Build/iOS",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = iOSDemo/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.elvisnunez.iOSDemo;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D523B0AF1C43AA2A001AD1EC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -205,11 +475,12 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -220,17 +491,29 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = 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_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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -247,9 +530,12 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -260,40 +546,73 @@
|
||||
D523B0B21C43AA2A001AD1EC /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Carthage/Build/iOS",
|
||||
);
|
||||
INFOPLIST_FILE = Lightbox/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D523B0B31C43AA2A001AD1EC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Carthage/Build/iOS",
|
||||
);
|
||||
INFOPLIST_FILE = Lightbox/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
44E6A6582495BFAC00543CF0 /* Build configuration list for PBXNativeTarget "iOSDemo" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
44E6A6592495BFAC00543CF0 /* Debug */,
|
||||
44E6A65A2495BFAC00543CF0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D523B0A31C43AA2A001AD1EC /* Build configuration list for PBXProject "Lightbox" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@@ -313,6 +632,25 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
85CB006E27B16C6900A47BB3 /* XCRemoteSwiftPackageReference "SDWebImage" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SDWebImage/SDWebImage.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.0.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
85CB006F27B16C6900A47BB3 /* SDWebImage */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 85CB006E27B16C6900A47BB3 /* XCRemoteSwiftPackageReference "SDWebImage" */;
|
||||
productName = SDWebImage;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = D523B0A01C43AA2A001AD1EC /* Project object */;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Lightbox.xcodeproj">
|
||||
location = "self:">
|
||||
</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>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -15,7 +15,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
@@ -27,10 +27,29 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES"
|
||||
testExecutionOrdering = "random">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D229B5E01FC3123F00F04123"
|
||||
BuildableName = "Lightbox-iOS-Tests.xctest"
|
||||
BlueprintName = "Lightbox-iOS-Tests"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@@ -46,13 +65,11 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@@ -64,7 +81,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// swift-tools-version:5.1
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "Lightbox",
|
||||
products: [
|
||||
.library(
|
||||
name: "Lightbox",
|
||||
targets: ["Lightbox"]),
|
||||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "Lightbox",
|
||||
dependencies: [],
|
||||
path: "Source"
|
||||
)
|
||||
],
|
||||
swiftLanguageVersions: [.v5]
|
||||
)
|
||||
@@ -1,14 +1,165 @@
|
||||
# Lightbox
|
||||
|
||||
[](https://travis-ci.org/hyperoslo/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](https://circleci.com/gh/hyperoslo/Lightbox)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||

|
||||
|
||||
<img src="https://raw.githubusercontent.com/hyperoslo/Lightbox/master/Images/Icon.png" alt="Lightbox Icon" align="right" />
|
||||
|
||||
**Lightbox** is a convenient and easy to use image viewer for your iOS app,
|
||||
packed with all the features you expect:
|
||||
|
||||
- [x] Paginated image slideshow.
|
||||
- [x] Video support.
|
||||
- [x] Double-tap to zoom.
|
||||
- [x] Image caption.
|
||||
- [x] Dynamic background based on [Hue](https://github.com/hyperoslo/Hue)
|
||||
- [x] Remote image loading and caching based on [Imaginary](https://github.com/hyperoslo/Imaginary)
|
||||
- [x] Interactive transition animations.
|
||||
- [x] Powerful configuration.
|
||||
- [x] [Live Demo](https://appetize.io/app/wfgwc2uvg82m9pzbt17p4rrgh4?device=iphone5s&scale=75&orientation=portrait&osVersion=9.3)
|
||||
|
||||
<div align="center">
|
||||
<img src="Images/demo.png" height="500">
|
||||
<img src="Images/demo2.png" height="500">
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Usage](#usage)
|
||||
* [Controller](#controller)
|
||||
* [Delegates](#delegates)
|
||||
* [Image loading](#image-loading)
|
||||
* [Video](#video)
|
||||
* [Configuration](#configuration)
|
||||
* [Installation](#installation)
|
||||
* [Author](#author)
|
||||
* [Contributing](#contributing)
|
||||
* [License](#license)
|
||||
|
||||
## Usage
|
||||
|
||||
### Controller
|
||||
|
||||
To start your slideshow just instantiate `LightboxController`, set needed
|
||||
delegates and present it:
|
||||
|
||||
|
||||
```swift
|
||||
<API>
|
||||
|
||||
// Create an array of images.
|
||||
let images = [
|
||||
LightboxImage(imageURL: URL(string: "https://cdn.arstechnica.net/2011/10/05/iphone4s_sample_apple-4e8c706-intro.jpg")!),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo1")!,
|
||||
text: "This is an example of a remote image loaded from URL"
|
||||
),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo2")!,
|
||||
text: "",
|
||||
videoURL: URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo3")!,
|
||||
text: "This is an example of a local image."
|
||||
)
|
||||
]
|
||||
|
||||
// Create an instance of LightboxController.
|
||||
let controller = LightboxController(images: images)
|
||||
|
||||
// Set delegates.
|
||||
controller.pageDelegate = self
|
||||
controller.dismissalDelegate = self
|
||||
|
||||
// Use dynamic background.
|
||||
controller.dynamicBackground = true
|
||||
|
||||
// Present your controller.
|
||||
present(controller, animated: true, completion: nil)
|
||||
```
|
||||
|
||||
### Delegates
|
||||
|
||||
Use `LightboxControllerPageDelegate` if you want to be notified about page
|
||||
navigation changes.
|
||||
|
||||
```swift
|
||||
extension ViewController: LightboxControllerPageDelegate {
|
||||
|
||||
func lightboxController(_ controller: LightboxController, didMoveToPage page: Int) {
|
||||
print(page)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use `LightboxControllerDismissalDelegate` to be notified when controller is
|
||||
about to be dismissed. Please note that `LightboxController` dismisses itself
|
||||
if it was presented initially.
|
||||
|
||||
```swift
|
||||
extension ViewController: LightboxControllerDismissalDelegate: class {
|
||||
|
||||
func lightboxControllerWillDismiss(_ controller: LightboxController) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Image loading
|
||||
|
||||
By default images are loaded using [Imaginary](https://github.com/hyperoslo/Imaginary) for reliable loading and caching. But it's easy to change this behavior using **LightboxConfig**
|
||||
|
||||
```swift
|
||||
LightboxConfig.loadImage = {
|
||||
imageView, URL, completion in
|
||||
// Custom image loading
|
||||
}
|
||||
```
|
||||
|
||||
### Video
|
||||
|
||||
**Lightbox** can show and plays video using default `AVPlayerViewController`. Showning video by using `videoURL`:
|
||||
|
||||
```swift
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo2")!,
|
||||
text: "",
|
||||
videoURL: NSURL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
)
|
||||
```
|
||||
|
||||
Override video handling if needed:
|
||||
|
||||
```swift
|
||||
LightboxConfig.handleVideo = { from, videoURL in
|
||||
// Custom video handling
|
||||
let videoController = AVPlayerViewController()
|
||||
videoController.player = AVPlayer(url: videoURL)
|
||||
|
||||
from.present(videoController, animated: true) {
|
||||
videoController.player?.play()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Configure text, colors, fonts of UI elements by overriding the static
|
||||
variables in the **Lightbox** [configuration](https://github.com/hyperoslo/Lightbox/blob/master/Source/LightboxConfig.swift) struct. As an example:
|
||||
|
||||
```swift
|
||||
LightboxConfig.CloseButton.image = UIImage(named: ImageList.Lightbox.closeButton)
|
||||
LightboxConfig.CloseButton.textAttributes = TextAttributes.Lightbox.closeButton
|
||||
LightboxConfig.CloseButton.text = "Finish"
|
||||
|
||||
LightboxConfig.DeleteButton.image = UIImage(named: ImageList.Lightbox.deleteButton)
|
||||
LightboxConfig.DeleteButton.textAttributes = TextAttributes.Lightbox.deleteButton
|
||||
LightboxConfig.DeleteButton.text = "Delete"
|
||||
|
||||
LightboxConfig.InfoLabel.ellipsisText = "Show more"
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -20,10 +171,27 @@ it, simply add the following line to your Podfile:
|
||||
pod 'Lightbox'
|
||||
```
|
||||
|
||||
In order to quickly try the demo project of a **Lightbox** just run
|
||||
`pod try Lightbox` in your terminal.
|
||||
|
||||
**Lightbox** is also available through [Carthage](https://github.com/Carthage/Carthage).
|
||||
To install just write into your Cartfile:
|
||||
|
||||
```ruby
|
||||
github "hyperoslo/Lightbox"
|
||||
```
|
||||
|
||||
To install **Lightbox** manually just download and drop `Sources` and
|
||||
`Images` folders in your project.
|
||||
|
||||
## Author
|
||||
|
||||
Hyper Interaktiv AS, ios@hyper.no
|
||||
|
||||
## Contributing
|
||||
|
||||
We would love you to contribute to **Lightbox**, check the [CONTRIBUTING](https://github.com/hyperoslo/Lightbox/blob/master/CONTRIBUTING.md) file for more info.
|
||||
|
||||
## License
|
||||
|
||||
**Lightbox** is available under the MIT license. See the LICENSE file for more info.
|
||||
**Lightbox** is available under the MIT license. See the [LICENSE](https://github.com/hyperoslo/Lightbox/blob/master/LICENSE.md) file for more info.
|
||||
|
||||
|
After Width: | Height: | Size: 6.5 KiB |
@@ -0,0 +1,10 @@
|
||||
import UIKit
|
||||
|
||||
/// Used to load assets from Lightbox bundle
|
||||
class AssetManager {
|
||||
|
||||
static func image(_ named: String) -> UIImage? {
|
||||
let bundle = Bundle(for: AssetManager.self)
|
||||
return UIImage(named: "Lightbox.bundle/\(named)", in: bundle, compatibleWith: nil)
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
class CenterCellCollectionViewFlowLayout: UICollectionViewFlowLayout {
|
||||
|
||||
override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
|
||||
|
||||
if let collectionView = self.collectionView {
|
||||
let collectionViewBounds = collectionView.bounds
|
||||
let halfWidth = collectionViewBounds.size.width / 2
|
||||
let proposedContentOffsetCenterX = proposedContentOffset.x + halfWidth
|
||||
|
||||
if let attributesForVisibleCells = layoutAttributesForElementsInRect(collectionViewBounds) {
|
||||
var candidateAttributes : UICollectionViewLayoutAttributes?
|
||||
|
||||
for attributes in attributesForVisibleCells.filter({
|
||||
$0.representedElementCategory == UICollectionElementCategory.Cell}) {
|
||||
|
||||
if let candAttrs = candidateAttributes {
|
||||
let a = attributes.center.x - proposedContentOffsetCenterX
|
||||
let b = candAttrs.center.x - proposedContentOffsetCenterX
|
||||
|
||||
if velocity.x < 0 {
|
||||
continue
|
||||
} else if velocity.x > 0 {
|
||||
candidateAttributes = attributes
|
||||
} else if fabsf(Float(a)) < fabsf(Float(b)) {
|
||||
candidateAttributes = attributes
|
||||
}
|
||||
|
||||
} else { // First time in the loop
|
||||
candidateAttributes = attributes
|
||||
continue
|
||||
}
|
||||
}
|
||||
return CGPoint(
|
||||
x: candidateAttributes!.center.x - halfWidth,
|
||||
y: proposedContentOffset.y)
|
||||
}
|
||||
}
|
||||
|
||||
return super.targetContentOffsetForProposedContentOffset(proposedContentOffset)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import UIKit
|
||||
|
||||
internal extension UIColor {
|
||||
/// Constructing color from hex string
|
||||
///
|
||||
/// - Parameter hex: A hex string, can either contain # or not
|
||||
convenience init(hex string: String) {
|
||||
var hex = string.hasPrefix("#")
|
||||
? String(string.dropFirst())
|
||||
: string
|
||||
guard hex.count == 3 || hex.count == 6
|
||||
else {
|
||||
self.init(white: 1.0, alpha: 0.0)
|
||||
return
|
||||
}
|
||||
if hex.count == 3 {
|
||||
for (index, char) in hex.enumerated() {
|
||||
hex.insert(char, at: hex.index(hex.startIndex, offsetBy: index * 2))
|
||||
}
|
||||
}
|
||||
|
||||
self.init(
|
||||
red: CGFloat((Int(hex, radix: 16)! >> 16) & 0xFF) / 255.0,
|
||||
green: CGFloat((Int(hex, radix: 16)! >> 8) & 0xFF) / 255.0,
|
||||
blue: CGFloat((Int(hex, radix: 16)!) & 0xFF) / 255.0, alpha: 1.0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
protocol LayoutConfigurable: AnyObject {
|
||||
@MainActor
|
||||
func configureLayout()
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import UIKit
|
||||
|
||||
class LightboxTransition: UIPercentDrivenInteractiveTransition {
|
||||
|
||||
lazy var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in
|
||||
let gesture = UIPanGestureRecognizer()
|
||||
gesture.addTarget(self, action: #selector(handlePanGesture(_:)))
|
||||
gesture.delegate = self
|
||||
|
||||
return gesture
|
||||
}()
|
||||
|
||||
var interactive = false
|
||||
var dismissing = false
|
||||
var initialOrigin = CGPoint(x: 0, y: 0)
|
||||
|
||||
var scrollView: UIScrollView? {
|
||||
didSet {
|
||||
guard let scrollView = scrollView else { return }
|
||||
scrollView.addGestureRecognizer(panGestureRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
weak var lightboxController: LightboxController?
|
||||
|
||||
// MARK: - Transition
|
||||
|
||||
func transition(_ show: Bool) {
|
||||
guard let controller = lightboxController else { return }
|
||||
|
||||
if interactive {
|
||||
controller.view.backgroundColor = UIColor.black.withAlphaComponent(show ? 1 : 0)
|
||||
} else {
|
||||
controller.view.alpha = show ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Pan gesture recognizer
|
||||
|
||||
@objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) {
|
||||
let translation = gesture.translation(in: scrollView)
|
||||
let percentage = abs(translation.y) / UIScreen.main.bounds.height / 1.5
|
||||
let velocity = gesture.velocity(in: scrollView)
|
||||
|
||||
switch gesture.state {
|
||||
case .began:
|
||||
interactive = true
|
||||
lightboxController?.presented = false
|
||||
lightboxController?.dismiss(animated: true, completion: nil)
|
||||
if let origin = scrollView?.frame.origin { initialOrigin = origin }
|
||||
case .changed:
|
||||
update(percentage)
|
||||
scrollView?.frame.origin.y = initialOrigin.y + translation.y
|
||||
case .ended, .cancelled:
|
||||
|
||||
var time = translation.y * 3 / abs(velocity.y)
|
||||
if time > 1 { time = 0.7 }
|
||||
|
||||
interactive = false
|
||||
lightboxController?.presented = true
|
||||
|
||||
if percentage > 0.1 {
|
||||
finish()
|
||||
guard let controller = lightboxController else { return }
|
||||
|
||||
controller.headerView.alpha = 0
|
||||
controller.footerView.alpha = 0
|
||||
|
||||
UIView.animate(withDuration: TimeInterval(time), delay: 0, options: [.allowUserInteraction], animations: {
|
||||
self.scrollView?.frame.origin.y = translation.y * 3
|
||||
controller.view.alpha = 0
|
||||
controller.view.backgroundColor = UIColor.black.withAlphaComponent(0)
|
||||
}, completion: { _ in })
|
||||
} else {
|
||||
cancel()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.035) {
|
||||
UIView.animate(withDuration: 0.35, animations: {
|
||||
self.scrollView?.frame.origin = self.initialOrigin
|
||||
})
|
||||
}
|
||||
}
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
override func finish() {
|
||||
super.finish()
|
||||
|
||||
guard let lightboxController = lightboxController else { return }
|
||||
lightboxController.dismissalDelegate?.lightboxControllerWillDismiss(lightboxController)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIViewControllerAnimatedTransitioning
|
||||
|
||||
extension LightboxTransition: UIViewControllerAnimatedTransitioning {
|
||||
|
||||
func transitionDuration(using transitionContext: (any UIViewControllerContextTransitioning)?) -> TimeInterval {
|
||||
return 0.25
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: any UIViewControllerContextTransitioning) {
|
||||
let container = transitionContext.containerView
|
||||
|
||||
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
|
||||
let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)
|
||||
else { return }
|
||||
|
||||
let firstView = dismissing ? toView : fromView
|
||||
let secondView = dismissing ? fromView : toView
|
||||
|
||||
if !dismissing { transition(false) }
|
||||
|
||||
container.addSubview(firstView)
|
||||
container.addSubview(secondView)
|
||||
|
||||
toView.frame = container.bounds
|
||||
|
||||
let duration = transitionDuration(using: transitionContext)
|
||||
|
||||
UIView.animate(withDuration: duration, animations: {
|
||||
self.transition(!self.dismissing)
|
||||
}, completion: { _ in
|
||||
transitionContext.transitionWasCancelled
|
||||
? transitionContext.completeTransition(false)
|
||||
: transitionContext.completeTransition(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIViewControllerTransitioningDelegate
|
||||
|
||||
extension LightboxTransition: UIViewControllerTransitioningDelegate {
|
||||
|
||||
func animationController(forDismissed dismissed: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? {
|
||||
dismissing = true
|
||||
return self
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> (any UIViewControllerAnimatedTransitioning)? {
|
||||
dismissing = false
|
||||
return self
|
||||
}
|
||||
|
||||
func interactionControllerForDismissal(using animator: any UIViewControllerAnimatedTransitioning) -> (any UIViewControllerInteractiveTransitioning)? {
|
||||
return interactive ? self : nil
|
||||
}
|
||||
|
||||
func interactionControllerForPresentation(using animator: any UIViewControllerAnimatedTransitioning) -> (any UIViewControllerInteractiveTransitioning)? {
|
||||
return interactive ? self : nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIGestureRecognizerDelegate
|
||||
|
||||
extension LightboxTransition: UIGestureRecognizerDelegate {
|
||||
|
||||
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
var result = false
|
||||
|
||||
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
||||
let translation = panGestureRecognizer.translation(in: gestureRecognizer.view)
|
||||
if abs(translation.x) < abs(translation.y) {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
@discardableResult func addGradientLayer(_ colors: [UIColor]) -> CAGradientLayer {
|
||||
if let gradientLayer = gradientLayer { return gradientLayer }
|
||||
|
||||
let gradient = CAGradientLayer()
|
||||
|
||||
gradient.frame = bounds
|
||||
gradient.colors = colors.map { $0.cgColor }
|
||||
layer.insertSublayer(gradient, at: 0)
|
||||
|
||||
return gradient
|
||||
}
|
||||
|
||||
func removeGradientLayer() -> CAGradientLayer? {
|
||||
gradientLayer?.removeFromSuperlayer()
|
||||
|
||||
return gradientLayer
|
||||
}
|
||||
|
||||
func resizeGradientLayer() {
|
||||
gradientLayer?.frame = bounds
|
||||
}
|
||||
|
||||
fileprivate var gradientLayer: CAGradientLayer? {
|
||||
return layer.sublayers?.first as? CAGradientLayer
|
||||
}
|
||||
}
|
||||
@@ -1,90 +1,101 @@
|
||||
import UIKit
|
||||
import AVKit
|
||||
import AVFoundation
|
||||
|
||||
class LightboxConfig {
|
||||
|
||||
var config = Config()
|
||||
|
||||
static let sharedInstance = LightboxConfig()
|
||||
}
|
||||
|
||||
public struct Config {
|
||||
|
||||
public var hideStatusBar = true
|
||||
public var backgroundColor = UIColor.clearColor()
|
||||
public var pageIndicator = PageIndicator()
|
||||
public var closeButton = CloseButton()
|
||||
public var deleteButton = DeleteButton()
|
||||
public var zoom = Zoom()
|
||||
public var remoteImages = false
|
||||
public var spacing: CGFloat = 20
|
||||
|
||||
public typealias LoadImageCompletion = (error: NSError?) -> Void
|
||||
|
||||
public var loadImage: (imageView: UIImageView, URL: NSURL, completion: LoadImageCompletion?) -> Void = {
|
||||
imageView, URL, completion in
|
||||
let imageRequest: NSURLRequest = NSURLRequest(URL: URL)
|
||||
public class LightboxConfig {
|
||||
/// Whether to show status bar while Lightbox is presented
|
||||
public static var hideStatusBar = true
|
||||
|
||||
NSURLConnection.sendAsynchronousRequest(imageRequest,
|
||||
queue: NSOperationQueue.mainQueue(),
|
||||
completionHandler: { response, data, error in
|
||||
if let data = data, image = UIImage(data: data) {
|
||||
imageView.image = image
|
||||
}
|
||||
public static var imageBackgroundColor = UIColor.black
|
||||
|
||||
completion?(error: error)
|
||||
})
|
||||
/// Provide a closure to handle selected video
|
||||
@MainActor
|
||||
public static var handleVideo: (_ from: UIViewController, _ videoURL: URL) -> Void = { from, videoURL in
|
||||
let videoController = AVPlayerViewController()
|
||||
videoController.player = AVPlayer(url: videoURL)
|
||||
|
||||
from.present(videoController, animated: true) {
|
||||
videoController.player?.play()
|
||||
}
|
||||
}
|
||||
|
||||
public init() { }
|
||||
|
||||
/// How to load image onto SDAnimatedImageView
|
||||
public static var loadImage: ((UIImageView, URL, ((UIImage?) -> Void)?) -> Void)?
|
||||
|
||||
/// Indicator is used to show while image is being fetched
|
||||
@MainActor
|
||||
public static var makeLoadingIndicator: () -> UIView = {
|
||||
return LoadingIndicator()
|
||||
}
|
||||
|
||||
/// Number of images to preload.
|
||||
///
|
||||
/// 0 - Preload all images (default).
|
||||
public static var preload = 0
|
||||
|
||||
public struct PageIndicator {
|
||||
public var enabled = true
|
||||
public var textAttributes = [
|
||||
NSFontAttributeName: UIFont.systemFontOfSize(18),
|
||||
NSForegroundColorAttributeName: UIColor.lightGrayColor(),
|
||||
NSParagraphStyleAttributeName: {
|
||||
public static var enabled = true
|
||||
public static var separatorColor = UIColor(hex: "3D4757")
|
||||
|
||||
public static var textAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: UIFont.systemFont(ofSize: 12),
|
||||
.foregroundColor: UIColor(hex: "899AB8"),
|
||||
.paragraphStyle: {
|
||||
var style = NSMutableParagraphStyle()
|
||||
style.alignment = .Center
|
||||
style.alignment = .center
|
||||
return style
|
||||
}()
|
||||
}()
|
||||
]
|
||||
}
|
||||
|
||||
public struct CloseButton {
|
||||
public var enabled = true
|
||||
public var textAttributes = [
|
||||
NSFontAttributeName: UIFont.boldSystemFontOfSize(12),
|
||||
NSForegroundColorAttributeName: UIColor.whiteColor(),
|
||||
NSParagraphStyleAttributeName: {
|
||||
public static var enabled = true
|
||||
public static var size: CGSize?
|
||||
public static var text = NSLocalizedString("Close", comment: "")
|
||||
public static var image: UIImage?
|
||||
|
||||
public static var textAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: UIFont.boldSystemFont(ofSize: 16),
|
||||
.foregroundColor: UIColor.white,
|
||||
.paragraphStyle: {
|
||||
var style = NSMutableParagraphStyle()
|
||||
style.alignment = .Center
|
||||
style.alignment = .center
|
||||
return style
|
||||
}()
|
||||
}()
|
||||
]
|
||||
public var size = CGSize(width: 60, height: 25)
|
||||
public var text = NSLocalizedString("Close", comment: "")
|
||||
public var image: UIImage?
|
||||
}
|
||||
|
||||
public struct DeleteButton {
|
||||
public var enabled = true
|
||||
public var alpha: CGFloat = 0
|
||||
public var textAttributes = [
|
||||
NSFontAttributeName: UIFont.boldSystemFontOfSize(12),
|
||||
NSForegroundColorAttributeName: UIColor(red:0.99, green:0.26, blue:0.18, alpha:1),
|
||||
NSParagraphStyleAttributeName: {
|
||||
public static var enabled = false
|
||||
public static var size: CGSize?
|
||||
public static var text = NSLocalizedString("Delete", comment: "")
|
||||
public static var image: UIImage?
|
||||
|
||||
public static var textAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: UIFont.boldSystemFont(ofSize: 16),
|
||||
.foregroundColor: UIColor(hex: "FA2F5B"),
|
||||
.paragraphStyle: {
|
||||
var style = NSMutableParagraphStyle()
|
||||
style.alignment = .Center
|
||||
style.alignment = .center
|
||||
return style
|
||||
}()
|
||||
}()
|
||||
]
|
||||
}
|
||||
|
||||
public struct InfoLabel {
|
||||
public static var enabled = true
|
||||
public static var textColor = UIColor.white
|
||||
public static var ellipsisText = NSLocalizedString("Show more", comment: "")
|
||||
public static var ellipsisColor = UIColor(hex: "899AB9")
|
||||
|
||||
public static var textAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: UIFont.systemFont(ofSize: 12),
|
||||
.foregroundColor: UIColor(hex: "DBDBDB")
|
||||
]
|
||||
public var size = CGSize(width: 70, height: 25)
|
||||
public var text = NSLocalizedString("Delete", comment: "")
|
||||
public var image: UIImage?
|
||||
}
|
||||
|
||||
public struct Zoom {
|
||||
public var minimumScale: CGFloat = 1.0
|
||||
public var maximumScale: CGFloat = 3.0
|
||||
public static var minimumScale: CGFloat = 1.0
|
||||
public static var maximumScale: CGFloat = 3.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,599 +1,492 @@
|
||||
import UIKit
|
||||
|
||||
public protocol LightboxControllerPageDelegate: class {
|
||||
|
||||
func lightboxControllerDidMoveToPage(controller: LightboxController, page: Int)
|
||||
public protocol LightboxControllerPageDelegate: AnyObject {
|
||||
@MainActor
|
||||
func lightboxController(_ controller: LightboxController, didMoveToPage page: Int)
|
||||
}
|
||||
|
||||
public protocol LightboxControllerDismissalDelegate: class {
|
||||
|
||||
func lightboxControllerDidDismiss(controller: LightboxController)
|
||||
public protocol LightboxControllerDismissalDelegate: AnyObject {
|
||||
@MainActor
|
||||
func lightboxControllerWillDismiss(_ controller: LightboxController)
|
||||
}
|
||||
|
||||
public class LightboxController: UIViewController {
|
||||
public protocol LightboxControllerTouchDelegate: AnyObject {
|
||||
@MainActor
|
||||
func lightboxController(_ controller: LightboxController, didTouch image: LightboxImage, at index: Int)
|
||||
}
|
||||
|
||||
public weak var pageDelegate: LightboxControllerPageDelegate?
|
||||
public weak var dismissalDelegate: LightboxControllerDismissalDelegate?
|
||||
public protocol LightboxControllerTapDelegate: AnyObject {
|
||||
@MainActor
|
||||
func lightboxController(_ controller: LightboxController, didTap image: LightboxImage, at index: Int)
|
||||
@MainActor
|
||||
func lightboxController(_ controller: LightboxController, didDoubleTap image: LightboxImage, at index: Int)
|
||||
}
|
||||
|
||||
lazy var transitionManager: LightboxTransition = { [unowned self] in
|
||||
let manager = LightboxTransition()
|
||||
manager.sourceViewController = self
|
||||
public protocol LightboxControllerDeleteDelegate: AnyObject {
|
||||
@MainActor
|
||||
func lightboxController(_ controller: LightboxController, willDeleteAt index: Int)
|
||||
}
|
||||
|
||||
return manager
|
||||
}()
|
||||
open class LightboxController: UIViewController {
|
||||
|
||||
public var images = []
|
||||
public var collectionSize = CGSizeZero
|
||||
var pageLabelBottom: NSLayoutConstraint?
|
||||
var pageLabelAlternative: NSLayoutConstraint?
|
||||
var collectionViewHeight: NSLayoutConstraint?
|
||||
var collectionViewWidth: NSLayoutConstraint?
|
||||
var closeButtonTop: NSLayoutConstraint?
|
||||
var closeButtonRight: NSLayoutConstraint?
|
||||
var physics = false
|
||||
// MARK: - Internal views
|
||||
|
||||
lazy var config: Config = {
|
||||
return LightboxConfig.sharedInstance.config
|
||||
}()
|
||||
lazy var scrollView: UIScrollView = { [unowned self] in
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.isPagingEnabled = false
|
||||
scrollView.delegate = self
|
||||
scrollView.showsHorizontalScrollIndicator = false
|
||||
scrollView.decelerationRate = UIScrollView.DecelerationRate.fast
|
||||
|
||||
var pageLabelBottomConstant: CGFloat {
|
||||
return collectionSize.width < collectionSize.height ? -20 : -2
|
||||
}
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
var currentOrientation: UIDeviceOrientation?
|
||||
var beforeFaceOrientation: UIDeviceOrientation?
|
||||
lazy var overlayTapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
|
||||
let gesture = UITapGestureRecognizer()
|
||||
gesture.addTarget(self, action: #selector(overlayViewDidTap(_:)))
|
||||
|
||||
var rotating = false
|
||||
return gesture
|
||||
}()
|
||||
|
||||
public private(set) var page = 0 {
|
||||
lazy var effectView: UIVisualEffectView = {
|
||||
let effect = UIBlurEffect(style: .dark)
|
||||
let view = UIVisualEffectView(effect: effect)
|
||||
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var backgroundView: UIImageView = {
|
||||
let view = UIImageView()
|
||||
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - Public views
|
||||
|
||||
open fileprivate(set) lazy var headerView: HeaderView = { [unowned self] in
|
||||
let view = HeaderView()
|
||||
view.delegate = self
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
open fileprivate(set) lazy var footerView: FooterView = { [unowned self] in
|
||||
let view = FooterView()
|
||||
view.delegate = self
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
open fileprivate(set) lazy var overlayView: UIView = { [unowned self] in
|
||||
let view = UIView(frame: CGRect.zero)
|
||||
let gradient = CAGradientLayer()
|
||||
let colors = [UIColor(hex: "090909").withAlphaComponent(0), UIColor(hex: "040404")]
|
||||
|
||||
view.addGradientLayer(colors)
|
||||
view.alpha = 0
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
open fileprivate(set) var currentPage = 0 {
|
||||
didSet {
|
||||
page = min(images.count - 1, max(0, page))
|
||||
currentPage = min(numberOfPages - 1, max(0, currentPage))
|
||||
footerView.updatePage(currentPage + 1, numberOfPages)
|
||||
footerView.updateText(pageViews[currentPage].image.text)
|
||||
|
||||
let text = "\(page + 1)/\(images.count)"
|
||||
|
||||
pageLabel.attributedText = NSAttributedString(string: text,
|
||||
attributes: config.pageIndicator.textAttributes)
|
||||
pageLabel.sizeToFit()
|
||||
|
||||
if page == images.count - 1 {
|
||||
if currentPage == numberOfPages - 1 {
|
||||
seen = true
|
||||
}
|
||||
|
||||
pageDelegate?.lightboxControllerDidMoveToPage(self, page: page)
|
||||
reconfigurePagesForPreload()
|
||||
|
||||
pageDelegate?.lightboxController(self, didMoveToPage: currentPage)
|
||||
|
||||
if let image = pageViews[currentPage].imageView.image, dynamicBackground {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.125) {
|
||||
self.loadDynamicBackground(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public private(set) var seen = false
|
||||
|
||||
lazy var collectionView: UICollectionView = { [unowned self] in
|
||||
let collectionView = UICollectionView(frame: CGRectZero,
|
||||
collectionViewLayout: self.collectionViewLayout)
|
||||
|
||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
collectionView.backgroundColor = .blackColor()
|
||||
collectionView.dataSource = self
|
||||
collectionView.delegate = self
|
||||
collectionView.decelerationRate = UIScrollViewDecelerationRateFast
|
||||
|
||||
collectionView.registerClass(LightboxViewCell.self,
|
||||
forCellWithReuseIdentifier: LightboxViewCell.reuseIdentifier)
|
||||
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
lazy var collectionViewLayout: UICollectionViewLayout = { [unowned self] in
|
||||
let layout = CenterCellCollectionViewFlowLayout()
|
||||
|
||||
layout.scrollDirection = .Horizontal
|
||||
layout.minimumInteritemSpacing = self.config.spacing
|
||||
layout.minimumLineSpacing = self.config.spacing
|
||||
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
|
||||
|
||||
return layout
|
||||
}()
|
||||
|
||||
lazy var pageLabel: UILabel = { [unowned self] in
|
||||
let label = UILabel(frame: CGRectZero)
|
||||
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.hidden = !self.config.pageIndicator.enabled
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
lazy var closeButton: UIButton = { [unowned self] in
|
||||
let title = NSAttributedString(
|
||||
string: self.config.closeButton.text,
|
||||
attributes: self.config.closeButton.textAttributes)
|
||||
let button = UIButton(type: .System)
|
||||
|
||||
button.tintColor = self.config.closeButton.textAttributes[NSForegroundColorAttributeName] as? UIColor
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setAttributedTitle(title, forState: .Normal)
|
||||
button.addTarget(self, action: "closeButtonDidTouchUpInside:",
|
||||
forControlEvents: .TouchUpInside)
|
||||
|
||||
if let image = self.config.closeButton.image {
|
||||
button.setBackgroundImage(image, forState: .Normal)
|
||||
}
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var deleteButton: UIButton = { [unowned self] in
|
||||
let title = NSAttributedString(
|
||||
string: self.config.deleteButton.text,
|
||||
attributes: self.config.deleteButton.textAttributes)
|
||||
let button = UIButton(type: .System)
|
||||
|
||||
button.tintColor = self.config.deleteButton.textAttributes[NSForegroundColorAttributeName] as? UIColor
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setAttributedTitle(title, forState: .Normal)
|
||||
button.alpha = self.config.deleteButton.alpha
|
||||
button.addTarget(self, action: "deleteButtonDidPress:",
|
||||
forControlEvents: .TouchUpInside)
|
||||
|
||||
if let image = self.config.deleteButton.image {
|
||||
button.setBackgroundImage(image, forState: .Normal)
|
||||
}
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
// MARK: Initializers
|
||||
|
||||
public required init(images: [String], config: Config? = nil,
|
||||
pageDelegate: LightboxControllerPageDelegate? = nil,
|
||||
dismissalDelegate: LightboxControllerDismissalDelegate? = nil) {
|
||||
self.images = images
|
||||
self.pageDelegate = pageDelegate
|
||||
self.dismissalDelegate = dismissalDelegate
|
||||
|
||||
if let config = config {
|
||||
LightboxConfig.sharedInstance.config = config
|
||||
}
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
open var numberOfPages: Int {
|
||||
return pageViews.count
|
||||
}
|
||||
|
||||
public required init(imagesUI: [UIImage], config: Config? = nil,
|
||||
pageDelegate: LightboxControllerPageDelegate? = nil,
|
||||
dismissalDelegate: LightboxControllerDismissalDelegate? = nil) {
|
||||
self.images = imagesUI
|
||||
self.pageDelegate = pageDelegate
|
||||
self.dismissalDelegate = dismissalDelegate
|
||||
|
||||
if let config = config {
|
||||
LightboxConfig.sharedInstance.config = config
|
||||
open var dynamicBackground: Bool = false {
|
||||
didSet {
|
||||
if dynamicBackground == true {
|
||||
effectView.frame = view.frame
|
||||
backgroundView.frame = effectView.frame
|
||||
view.insertSubview(effectView, at: 0)
|
||||
view.insertSubview(backgroundView, at: 0)
|
||||
} else {
|
||||
effectView.removeFromSuperview()
|
||||
backgroundView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
open var spacing: CGFloat = 20 {
|
||||
didSet {
|
||||
configureLayout(view.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
open var images: [LightboxImage] {
|
||||
get {
|
||||
return pageViews.map { $0.image }
|
||||
}
|
||||
set(value) {
|
||||
initialImages = value
|
||||
configurePages(value)
|
||||
}
|
||||
}
|
||||
|
||||
open weak var pageDelegate: (any LightboxControllerPageDelegate)?
|
||||
open weak var dismissalDelegate: (any LightboxControllerDismissalDelegate)?
|
||||
open weak var imageTouchDelegate: (any LightboxControllerTouchDelegate)?
|
||||
open weak var imageTapDelegate: (any LightboxControllerTapDelegate)?
|
||||
open weak var imageDeleteDelegate: (any LightboxControllerDeleteDelegate)?
|
||||
open internal(set) var presented = false
|
||||
open fileprivate(set) var seen = false
|
||||
|
||||
lazy var transitionManager: LightboxTransition = LightboxTransition()
|
||||
var pageViews = [PageView]()
|
||||
var statusBarHidden = false
|
||||
|
||||
fileprivate var initialImages: [LightboxImage]
|
||||
fileprivate let initialPage: Int
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init(images: [LightboxImage] = [], startIndex index: Int = 0) {
|
||||
self.initialImages = images
|
||||
self.initialPage = index
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - View Lifecycle
|
||||
// MARK: - View lifecycle
|
||||
|
||||
public override func viewDidLoad() {
|
||||
open override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
collectionSize = CGSizeMake(view.frame.width, view.frame.height)
|
||||
for subview in [collectionView, pageLabel, closeButton, deleteButton] {
|
||||
self.view.addSubview(subview)
|
||||
}
|
||||
// 9 July 2020: @3lvis
|
||||
// Lightbox hasn't been optimized to be used in presentation styles other than fullscreen.
|
||||
modalPresentationStyle = .fullScreen
|
||||
|
||||
statusBarHidden = UIApplication.shared.isStatusBarHidden
|
||||
|
||||
view.backgroundColor = LightboxConfig.imageBackgroundColor
|
||||
transitionManager.lightboxController = self
|
||||
transitionManager.scrollView = scrollView
|
||||
transitioningDelegate = transitionManager
|
||||
transitionManager.delegate = self
|
||||
view.backgroundColor = UIColor.blackColor()
|
||||
|
||||
let orientationsSupported: [String] = NSBundle.mainBundle().objectForInfoDictionaryKey("UISupportedInterfaceOrientations") as! [String]
|
||||
[scrollView, overlayView, headerView, footerView].forEach { view.addSubview($0) }
|
||||
overlayView.addGestureRecognizer(overlayTapGestureRecognizer)
|
||||
|
||||
currentOrientation = UIDevice.currentDevice().orientation
|
||||
beforeFaceOrientation = UIDevice.currentDevice().orientation
|
||||
configurePages(initialImages)
|
||||
|
||||
if orientationsSupported.first == "UIInterfaceOrientationPortrait"
|
||||
&& orientationsSupported.count == 1 {
|
||||
NSNotificationCenter.defaultCenter().addObserver(
|
||||
self,
|
||||
selector: "deviceDidRotate",
|
||||
name: UIDeviceOrientationDidChangeNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
setupConstraints()
|
||||
page = 0
|
||||
goTo(initialPage, animated: false)
|
||||
}
|
||||
|
||||
public override func viewDidAppear(animated: Bool) {
|
||||
super.viewDidAppear(true)
|
||||
open override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
if config.hideStatusBar {
|
||||
UIApplication.sharedApplication().setStatusBarHidden(true,
|
||||
withAnimation: .Fade)
|
||||
scrollView.frame = view.bounds
|
||||
footerView.frame.size = CGSize(
|
||||
width: view.bounds.width,
|
||||
height: 100
|
||||
)
|
||||
|
||||
footerView.frame.origin = CGPoint(
|
||||
x: 0,
|
||||
y: view.bounds.height - footerView.frame.height
|
||||
)
|
||||
|
||||
headerView.frame = CGRect(
|
||||
x: 0,
|
||||
y: 16,
|
||||
width: view.bounds.width,
|
||||
height: 100
|
||||
)
|
||||
|
||||
if !presented {
|
||||
presented = true
|
||||
configureLayout(view.bounds.size)
|
||||
}
|
||||
}
|
||||
|
||||
open override var prefersStatusBarHidden: Bool {
|
||||
return LightboxConfig.hideStatusBar
|
||||
}
|
||||
|
||||
// MARK: - Rotation
|
||||
|
||||
override open func viewWillTransition(to size: CGSize, with coordinator: any UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
coordinator.animate(alongsideTransition: { _ in
|
||||
self.configureLayout(size)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configurePages(_ images: [LightboxImage]) {
|
||||
pageViews.forEach { $0.removeFromSuperview() }
|
||||
pageViews = []
|
||||
|
||||
let preloadIndicies = calculatePreloadIndicies()
|
||||
|
||||
for i in 0..<images.count {
|
||||
let pageView = PageView(image: preloadIndicies.contains(i) ? images[i] : LightboxImageStub())
|
||||
pageView.pageViewDelegate = self
|
||||
|
||||
scrollView.addSubview(pageView)
|
||||
pageViews.append(pageView)
|
||||
}
|
||||
|
||||
let orientationsSupported: [String] = NSBundle.mainBundle().objectForInfoDictionaryKey("UISupportedInterfaceOrientations") as! [String]
|
||||
configureLayout(view.bounds.size)
|
||||
}
|
||||
|
||||
if orientationsSupported.first == "UIInterfaceOrientationPortrait"
|
||||
&& orientationsSupported.count == 1 {
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft
|
||||
|| UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
|
||||
deviceDidRotate()
|
||||
func reconfigurePagesForPreload() {
|
||||
let preloadIndicies = calculatePreloadIndicies()
|
||||
|
||||
for i in 0..<initialImages.count {
|
||||
let pageView = pageViews[i]
|
||||
if preloadIndicies.contains(i) {
|
||||
if type(of: pageView.image) == LightboxImageStub.self {
|
||||
pageView.update(with: initialImages[i])
|
||||
}
|
||||
} else {
|
||||
if type(of: pageView.image) != LightboxImageStub.self {
|
||||
pageView.update(with: LightboxImageStub())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
NSNotificationCenter.defaultCenter().removeObserver(self)
|
||||
}
|
||||
|
||||
// MARK: - Handle rotation
|
||||
|
||||
func deviceDidRotate() {
|
||||
let orientation = UIDevice.currentDevice().orientation
|
||||
let orientations: [UIDeviceOrientation] = [.FaceUp, .FaceDown]
|
||||
let currentIsFace = orientations.filter({ $0 == self.currentOrientation }).first != nil
|
||||
let nextIsFace = orientations.filter({ $0 == orientation }).first != nil
|
||||
|
||||
if nextIsFace {
|
||||
beforeFaceOrientation = currentOrientation
|
||||
}
|
||||
currentOrientation = orientation
|
||||
|
||||
if (!currentIsFace && nextIsFace) ||
|
||||
(currentIsFace && !nextIsFace && orientation == beforeFaceOrientation) {
|
||||
return
|
||||
}
|
||||
|
||||
rotating = true
|
||||
var transform = CGAffineTransformIdentity
|
||||
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft {
|
||||
transform = moveCollectionView(true)
|
||||
moveViews(true)
|
||||
transitionManager.panGestureRecognizer.enabled = false
|
||||
} else if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
|
||||
transform = moveCollectionView(false)
|
||||
moveViews(false)
|
||||
transitionManager.panGestureRecognizer.enabled = false
|
||||
} else if UIDevice.currentDevice().orientation == UIDeviceOrientation.Portrait {
|
||||
for constraint in [collectionViewHeight!, collectionViewWidth!,
|
||||
closeButtonTop!, closeButtonRight!,
|
||||
pageLabelAlternative!, pageLabelBottom!] {
|
||||
view.removeConstraint(constraint)
|
||||
}
|
||||
|
||||
standardCollectionViewConstraints()
|
||||
standardCloseButtonConstraints()
|
||||
standardPageLabelConstraints()
|
||||
collectionView.alpha = 0
|
||||
|
||||
transitionManager.panGestureRecognizer.enabled = true
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation != UIDeviceOrientation.PortraitUpsideDown {
|
||||
UIView.animateWithDuration(0.1, animations: { [unowned self] in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.alpha = 0 }
|
||||
}, completion: { finished in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.transform = transform }
|
||||
let indexPath = NSIndexPath(forItem: self.page, inSection: 0)
|
||||
self.collectionView.scrollToItemAtIndexPath(indexPath,
|
||||
atScrollPosition: UICollectionViewScrollPosition(), animated: false)
|
||||
UIView.animateWithDuration(0.3, animations: { [unowned self] in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.alpha = 1 }
|
||||
self.rotating = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation != UIDeviceOrientation.PortraitUpsideDown {
|
||||
UIView.animateWithDuration(0.3, animations: { [unowned self] in
|
||||
self.collectionView.transform = transform
|
||||
self.closeButton.transform = transform
|
||||
self.pageLabel.transform = transform
|
||||
}, completion: { _ in
|
||||
let indexPath = NSIndexPath(forItem: self.page, inSection: 0)
|
||||
self.collectionView.scrollToItemAtIndexPath(indexPath,
|
||||
atScrollPosition: UICollectionViewScrollPosition(), animated: true)
|
||||
self.rotating = false
|
||||
})
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.PortraitUpsideDown {
|
||||
rotating = false
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Autolayout
|
||||
|
||||
func setupConstraints() {
|
||||
let attributes: [NSLayoutAttribute] = [.CenterX, .CenterY]
|
||||
|
||||
for attribute in attributes {
|
||||
view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: attribute,
|
||||
relatedBy: .Equal, toItem: self.view, attribute: attribute, multiplier: 1, constant: 0))
|
||||
}
|
||||
|
||||
standardCollectionViewConstraints()
|
||||
standardPageLabelConstraints()
|
||||
standardCloseButtonConstraints()
|
||||
standardDeleteButtonConstraints()
|
||||
|
||||
view.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Width,
|
||||
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,
|
||||
multiplier: 1, constant: config.closeButton.size.width))
|
||||
|
||||
view.addConstraint(NSLayoutConstraint(item: closeButton, attribute: .Height,
|
||||
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,
|
||||
multiplier: 1, constant: config.closeButton.size.height))
|
||||
|
||||
view.addConstraint(NSLayoutConstraint(item: deleteButton, attribute: .Width,
|
||||
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,
|
||||
multiplier: 1, constant: config.deleteButton.size.width))
|
||||
|
||||
view.addConstraint(NSLayoutConstraint(item: deleteButton, attribute: .Height,
|
||||
relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,
|
||||
multiplier: 1, constant: config.deleteButton.size.height))
|
||||
}
|
||||
|
||||
// MARK: - Orientation
|
||||
|
||||
public override func shouldAutorotate() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
|
||||
return UIInterfaceOrientationMask.All
|
||||
}
|
||||
|
||||
public override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
||||
rotating = true
|
||||
collectionSize = size
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
|
||||
coordinator.animateAlongsideTransition({ _ in
|
||||
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||
self.pageLabelBottom?.constant = self.pageLabelBottomConstant
|
||||
}, completion: { _ in
|
||||
let indexPath = NSIndexPath(forItem: self.page, inSection: 0)
|
||||
|
||||
self.view.layoutIfNeeded()
|
||||
self.collectionView.scrollToItemAtIndexPath(indexPath,
|
||||
atScrollPosition: .CenteredHorizontally,
|
||||
animated: false)
|
||||
self.rotating = false
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Pagination
|
||||
|
||||
public func goTo(page: Int, animated: Bool = true) {
|
||||
if page >= 0 && page < images.count {
|
||||
var offset = collectionView.contentOffset
|
||||
|
||||
offset.x = CGFloat(page) * collectionSize.width
|
||||
collectionView.setContentOffset(offset,
|
||||
animated: animated)
|
||||
open func goTo(_ page: Int, animated: Bool = true) {
|
||||
guard page >= 0 && page < numberOfPages else {
|
||||
return
|
||||
}
|
||||
|
||||
currentPage = page
|
||||
|
||||
var offset = scrollView.contentOffset
|
||||
offset.x = CGFloat(page) * (scrollView.frame.width + spacing)
|
||||
|
||||
let shouldAnimated = view.window != nil ? animated : false
|
||||
|
||||
scrollView.setContentOffset(offset, animated: shouldAnimated)
|
||||
}
|
||||
|
||||
public func next(animated: Bool = true) {
|
||||
goTo(page + 1, animated: animated)
|
||||
open func next(_ animated: Bool = true) {
|
||||
goTo(currentPage + 1, animated: animated)
|
||||
}
|
||||
|
||||
public func previous(animated: Bool = true) {
|
||||
goTo(page - 1, animated: animated)
|
||||
open func previous(_ animated: Bool = true) {
|
||||
goTo(currentPage - 1, animated: animated)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
func closeButtonDidTouchUpInside(sender: UIButton) {
|
||||
dismissalDelegate?.lightboxControllerDidDismiss(self)
|
||||
@objc func overlayViewDidTap(_ tapGestureRecognizer: UITapGestureRecognizer) {
|
||||
footerView.expand(false)
|
||||
}
|
||||
|
||||
func deleteButtonDidPress(button: UIButton) {
|
||||
button.enabled = false
|
||||
var indexPath = NSIndexPath()
|
||||
let index = page
|
||||
let array = images.mutableCopy() as! NSMutableArray
|
||||
// MARK: - Layout
|
||||
|
||||
if page < images.count - 1 {
|
||||
indexPath = NSIndexPath(forRow: page + 1, inSection: 0)
|
||||
} else if page == images.count - 1 && images.count != 1 {
|
||||
indexPath = NSIndexPath(forRow: page - 1, inSection: 0)
|
||||
open func configureLayout(_ size: CGSize) {
|
||||
scrollView.frame.size = size
|
||||
scrollView.contentSize = CGSize(
|
||||
width: size.width * CGFloat(numberOfPages) + spacing * CGFloat(numberOfPages - 1),
|
||||
height: size.height)
|
||||
scrollView.contentOffset = CGPoint(x: CGFloat(currentPage) * (size.width + spacing), y: 0)
|
||||
|
||||
for (index, pageView) in pageViews.enumerated() {
|
||||
var frame = scrollView.bounds
|
||||
frame.origin.x = (frame.width + spacing) * CGFloat(index)
|
||||
pageView.frame = frame
|
||||
pageView.configureLayout()
|
||||
if index != numberOfPages - 1 {
|
||||
pageView.frame.size.width += spacing
|
||||
}
|
||||
}
|
||||
|
||||
[headerView, footerView].forEach { ($0 as AnyObject).configureLayout() }
|
||||
|
||||
overlayView.frame = scrollView.frame
|
||||
overlayView.resizeGradientLayer()
|
||||
}
|
||||
|
||||
fileprivate func loadDynamicBackground(_ image: UIImage) {
|
||||
backgroundView.image = image
|
||||
backgroundView.layer.add(CATransition(), forKey: "fade")
|
||||
}
|
||||
|
||||
func toggleControls(pageView: PageView?, visible: Bool, duration: TimeInterval = 0.1, delay: TimeInterval = 0) {
|
||||
let alpha: CGFloat = visible ? 1.0 : 0.0
|
||||
|
||||
pageView?.playButton.isHidden = !visible
|
||||
|
||||
UIView.animate(withDuration: duration, delay: delay, options: [], animations: {
|
||||
self.headerView.alpha = alpha
|
||||
self.footerView.alpha = alpha
|
||||
pageView?.playButton.alpha = alpha
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Helper functions
|
||||
func calculatePreloadIndicies () -> [Int] {
|
||||
var preloadIndicies: [Int] = []
|
||||
let preload = LightboxConfig.preload
|
||||
if preload > 0 {
|
||||
let lb = max(0, currentPage - preload)
|
||||
let rb = min(initialImages.count, currentPage + preload)
|
||||
for i in lb..<rb {
|
||||
preloadIndicies.append(i)
|
||||
}
|
||||
} else {
|
||||
if array.count != 0 {
|
||||
array.removeObjectAtIndex(index)
|
||||
}
|
||||
images = array
|
||||
dismissalDelegate?.lightboxControllerDidDismiss(self)
|
||||
}
|
||||
|
||||
if images.count != 0 {
|
||||
collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Left, animated: true)
|
||||
|
||||
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(0.25 * Double(NSEC_PER_SEC)))
|
||||
dispatch_after(delayTime, dispatch_get_main_queue()) { [unowned self] in
|
||||
array.removeObjectAtIndex(index)
|
||||
self.collectionView.scrollToItemAtIndexPath(NSIndexPath(forRow: index, inSection: 0), atScrollPosition: .Left, animated: false)
|
||||
self.images = array
|
||||
self.page = index
|
||||
self.collectionView.reloadData()
|
||||
button.enabled = true
|
||||
}
|
||||
preloadIndicies = [Int](0..<initialImages.count)
|
||||
}
|
||||
return preloadIndicies
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegateFlowLayout
|
||||
|
||||
extension LightboxController: UICollectionViewDelegateFlowLayout {
|
||||
|
||||
public func collectionView(collectionView: UICollectionView,
|
||||
layout collectionViewLayout: UICollectionViewLayout,
|
||||
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
|
||||
return collectionSize
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UICollectionViewDelegate
|
||||
|
||||
extension LightboxController: UICollectionViewDelegate { }
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension LightboxController: UIScrollViewDelegate {
|
||||
|
||||
public func scrollViewDidScroll(scrollView: UIScrollView) {
|
||||
if !rotating {
|
||||
let pageWidth = collectionSize.width
|
||||
let currentPage = Int(floor((collectionView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
|
||||
if currentPage != page { page = currentPage }
|
||||
}
|
||||
}
|
||||
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
var speed: CGFloat = velocity.x < 0 ? -2 : 2
|
||||
|
||||
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
|
||||
if let cell = collectionView.visibleCells().first as? LightboxViewCell {
|
||||
cell.parentViewController = self
|
||||
cell.setupTransitionManager()
|
||||
if velocity.x == 0 {
|
||||
speed = 0
|
||||
}
|
||||
|
||||
let pageWidth = scrollView.bounds.width + spacing
|
||||
var x = scrollView.contentOffset.x + speed * 60.0
|
||||
|
||||
if speed > 0 {
|
||||
x = ceil(x / pageWidth) * pageWidth
|
||||
} else if speed < -0 {
|
||||
x = floor(x / pageWidth) * pageWidth
|
||||
} else {
|
||||
x = round(x / pageWidth) * pageWidth
|
||||
}
|
||||
|
||||
targetContentOffset.pointee.x = x
|
||||
currentPage = Int(x / pageWidth)
|
||||
}
|
||||
}
|
||||
|
||||
extension LightboxController: LightboxTransitionDelegate {
|
||||
// MARK: - PageViewDelegate
|
||||
|
||||
func transitionDidDismissController(controller: LightboxController) {
|
||||
dismissalDelegate?.lightboxControllerDidDismiss(controller)
|
||||
extension LightboxController: PageViewDelegate {
|
||||
|
||||
func remoteImageDidLoad(_ image: UIImage?, imageView: UIImageView) {
|
||||
guard let image = image, dynamicBackground else {
|
||||
return
|
||||
}
|
||||
|
||||
let imageViewFrame = imageView.convert(imageView.frame, to: view)
|
||||
guard view.frame.intersects(imageViewFrame) else {
|
||||
return
|
||||
}
|
||||
|
||||
loadDynamicBackground(image)
|
||||
}
|
||||
|
||||
func pageViewDidZoom(_ pageView: PageView) {
|
||||
let duration = pageView.hasZoomed ? 0.1 : 0.5
|
||||
toggleControls(pageView: pageView, visible: !pageView.hasZoomed, duration: duration, delay: 0.5)
|
||||
}
|
||||
|
||||
func pageView(_ pageView: PageView, didTouchPlayButton videoURL: URL) {
|
||||
LightboxConfig.handleVideo(self, videoURL)
|
||||
}
|
||||
|
||||
func pageViewDidTouch(_ pageView: PageView) {
|
||||
guard !pageView.hasZoomed else { return }
|
||||
|
||||
imageTouchDelegate?.lightboxController(self, didTouch: images[currentPage], at: currentPage)
|
||||
|
||||
let visible = (headerView.alpha == 1.0)
|
||||
toggleControls(pageView: pageView, visible: !visible)
|
||||
}
|
||||
|
||||
func pageViewDidTap(_ pageView: PageView) {
|
||||
imageTapDelegate?.lightboxController(self, didTap: images[currentPage], at: currentPage)
|
||||
}
|
||||
|
||||
func pageViewDidDoubleTap(_ pageView: PageView) {
|
||||
imageTapDelegate?.lightboxController(self, didDoubleTap: images[currentPage], at: currentPage)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Custom autolayout
|
||||
// MARK: - HeaderViewDelegate
|
||||
|
||||
extension LightboxController {
|
||||
extension LightboxController: HeaderViewDelegate {
|
||||
|
||||
private func moveCollectionView(left: Bool) -> CGAffineTransform {
|
||||
let value: CGFloat = left ? 1.57 : -1.57
|
||||
let transform = CGAffineTransformMakeRotation(value)
|
||||
let size = CGSizeMake(view.frame.height, view.frame.width)
|
||||
func headerView(_ headerView: HeaderView, didPressDeleteButton deleteButton: UIButton) {
|
||||
deleteButton.isEnabled = false
|
||||
|
||||
view.removeConstraint(collectionViewHeight!)
|
||||
view.removeConstraint(collectionViewWidth!)
|
||||
|
||||
collectionViewHeight = NSLayoutConstraint(item: collectionView, attribute: .Height,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Width,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
collectionViewWidth = NSLayoutConstraint(item: collectionView, attribute: .Width,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Height,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
view.addConstraint(collectionViewHeight!)
|
||||
view.addConstraint(collectionViewWidth!)
|
||||
|
||||
collectionSize = size
|
||||
collectionView.alpha = 0
|
||||
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
view.layoutIfNeeded()
|
||||
|
||||
return transform
|
||||
}
|
||||
|
||||
private func standardPageLabelConstraints() {
|
||||
pageLabelAlternative = NSLayoutConstraint(item: pageLabel, attribute: .CenterX,
|
||||
relatedBy: .Equal, toItem: view, attribute: .CenterX,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
pageLabelBottom = NSLayoutConstraint(item: pageLabel, attribute: .Bottom,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Bottom,
|
||||
multiplier: 1, constant: pageLabelBottomConstant)
|
||||
|
||||
view.addConstraint(pageLabelAlternative!)
|
||||
view.addConstraint(pageLabelBottom!)
|
||||
}
|
||||
|
||||
private func standardCollectionViewConstraints() {
|
||||
collectionViewWidth = NSLayoutConstraint(item: collectionView, attribute: .Width,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Width,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
collectionViewHeight = NSLayoutConstraint(item: collectionView, attribute: .Height,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Height,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
collectionSize = CGSizeMake(view.frame.width, view.frame.height)
|
||||
collectionView.reloadData()
|
||||
|
||||
view.addConstraint(collectionViewWidth!)
|
||||
view.addConstraint(collectionViewHeight!)
|
||||
}
|
||||
|
||||
private func standardCloseButtonConstraints() {
|
||||
closeButtonTop = NSLayoutConstraint(item: closeButton, attribute: .Top,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Top,
|
||||
multiplier: 1, constant: 16)
|
||||
|
||||
closeButtonRight = NSLayoutConstraint(item: closeButton, attribute: .Right,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Right,
|
||||
multiplier: 1, constant: -17)
|
||||
|
||||
view.addConstraint(closeButtonTop!)
|
||||
view.addConstraint(closeButtonRight!)
|
||||
}
|
||||
|
||||
private func standardDeleteButtonConstraints() {
|
||||
view.addConstraint(NSLayoutConstraint(item: deleteButton, attribute: .Top,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Top,
|
||||
multiplier: 1, constant: 16))
|
||||
|
||||
view.addConstraint(NSLayoutConstraint(item: deleteButton, attribute: .Left,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Left,
|
||||
multiplier: 1, constant: 17))
|
||||
}
|
||||
|
||||
private func moveViews(left: Bool) {
|
||||
for constraint in [closeButtonTop!, closeButtonRight!,
|
||||
pageLabelAlternative!, pageLabelBottom!] {
|
||||
view.removeConstraint(constraint)
|
||||
imageDeleteDelegate?.lightboxController(self, willDeleteAt: currentPage)
|
||||
|
||||
guard numberOfPages != 1 else {
|
||||
pageViews.removeAll()
|
||||
self.headerView(headerView, didPressCloseButton: headerView.closeButton)
|
||||
return
|
||||
}
|
||||
|
||||
closeButtonRight = left ?
|
||||
NSLayoutConstraint(item: closeButton, attribute: .Right,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Right,
|
||||
multiplier: 1, constant: 0) :
|
||||
NSLayoutConstraint(item: closeButton, attribute: .Left,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Left,
|
||||
multiplier: 1, constant: 0)
|
||||
let prevIndex = currentPage
|
||||
|
||||
closeButtonTop = left
|
||||
? NSLayoutConstraint(item: closeButton, attribute: .Bottom,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Bottom,
|
||||
multiplier: 1, constant: -35)
|
||||
: NSLayoutConstraint(item: closeButton, attribute: .Top,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Top,
|
||||
multiplier: 1, constant: 35)
|
||||
|
||||
pageLabelBottom = left
|
||||
? NSLayoutConstraint(item: pageLabel, attribute: .Left,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Left,
|
||||
multiplier: 1, constant: 20)
|
||||
: NSLayoutConstraint(item: pageLabel, attribute: .Top,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Top,
|
||||
multiplier: 1, constant: 20)
|
||||
|
||||
pageLabelAlternative = left
|
||||
? NSLayoutConstraint(item: pageLabel, attribute: .Bottom,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Bottom,
|
||||
multiplier: 1, constant: -20)
|
||||
: NSLayoutConstraint(item: pageLabel, attribute: .Right,
|
||||
relatedBy: .Equal, toItem: view, attribute: .Right,
|
||||
multiplier: 1, constant: -20)
|
||||
|
||||
for constraint in [closeButtonTop!, closeButtonRight!,
|
||||
pageLabelAlternative!, pageLabelBottom!] {
|
||||
view.addConstraint(constraint)
|
||||
if currentPage == numberOfPages - 1 {
|
||||
previous()
|
||||
} else {
|
||||
next()
|
||||
currentPage -= 1
|
||||
}
|
||||
|
||||
self.initialImages.remove(at: prevIndex)
|
||||
self.pageViews.remove(at: prevIndex).removeFromSuperview()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
|
||||
self.configureLayout(self.view.bounds.size)
|
||||
self.currentPage = Int(self.scrollView.contentOffset.x / self.view.bounds.width)
|
||||
deleteButton.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
func headerView(_ headerView: HeaderView, didPressCloseButton closeButton: UIButton) {
|
||||
closeButton.isEnabled = false
|
||||
presented = false
|
||||
dismissalDelegate?.lightboxControllerWillDismiss(self)
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - FooterViewDelegate
|
||||
|
||||
extension LightboxController: FooterViewDelegate {
|
||||
|
||||
public func footerView(_ footerView: FooterView, didExpand expanded: Bool) {
|
||||
UIView.animate(withDuration: 0.25, animations: {
|
||||
self.overlayView.alpha = expanded ? 1.0 : 0.0
|
||||
self.headerView.deleteButton.alpha = expanded ? 0.0 : 1.0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
extension LightboxController: UICollectionViewDataSource {
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return images.count
|
||||
}
|
||||
|
||||
public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
|
||||
let cellIdentifier = LightboxViewCell.reuseIdentifier
|
||||
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier,
|
||||
forIndexPath: indexPath) as! LightboxViewCell
|
||||
let image: AnyObject = images[indexPath.row]
|
||||
let config = LightboxConfig.sharedInstance.config
|
||||
|
||||
cell.parentViewController = self
|
||||
cell.loadingIndicator.alpha = 0
|
||||
cell.setupTransitionManager()
|
||||
|
||||
if let imageString = image as? String {
|
||||
if let imageURL = NSURL(string: imageString) where config.remoteImages {
|
||||
cell.loadingIndicator.alpha = 1
|
||||
config.loadImage(
|
||||
imageView: cell.lightboxView.imageView, URL: imageURL) { error in
|
||||
if error == nil {
|
||||
cell.loadingIndicator.alpha = 0
|
||||
cell.lightboxView.updateViewLayout()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cell.lightboxView.imageView.image = UIImage(named: imageString)
|
||||
cell.lightboxView.updateViewLayout()
|
||||
}
|
||||
} else if let image = image as? UIImage {
|
||||
cell.lightboxView.imageView.image = image
|
||||
cell.lightboxView.updateViewLayout()
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import UIKit
|
||||
|
||||
open class LightboxImage {
|
||||
|
||||
open fileprivate(set) var image: UIImage?
|
||||
open fileprivate(set) var imageURL: URL?
|
||||
open fileprivate(set) var videoURL: URL?
|
||||
open fileprivate(set) var imageClosure: (() -> UIImage)?
|
||||
open var text: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
internal init(text: String = "") {
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public init(image: UIImage, text: String = "", videoURL: URL? = nil) {
|
||||
self.image = image
|
||||
self.text = text
|
||||
self.videoURL = videoURL
|
||||
}
|
||||
|
||||
public init(imageURL: URL, text: String = "", videoURL: URL? = nil) {
|
||||
self.imageURL = imageURL
|
||||
self.text = text
|
||||
self.videoURL = videoURL
|
||||
}
|
||||
|
||||
public init(imageClosure: @escaping () -> UIImage, text: String = "", videoURL: URL? = nil) {
|
||||
self.imageClosure = imageClosure
|
||||
self.text = text
|
||||
self.videoURL = videoURL
|
||||
}
|
||||
|
||||
open func addImageTo(_ imageView: UIImageView, completion: ((UIImage?) -> Void)? = nil) {
|
||||
if let image = image {
|
||||
imageView.image = image
|
||||
completion?(image)
|
||||
} else if let imageURL = imageURL {
|
||||
guard let loadImage = LightboxConfig.loadImage else {
|
||||
print("Lightbox: To use `imageURL`, you must use `LightboxConfig.loadImage`.")
|
||||
imageView.image = nil
|
||||
completion?(nil)
|
||||
return
|
||||
}
|
||||
|
||||
loadImage(imageView, imageURL, completion)
|
||||
} else if let imageClosure = imageClosure {
|
||||
let img = imageClosure()
|
||||
imageView.image = img
|
||||
completion?(img)
|
||||
} else {
|
||||
imageView.image = nil
|
||||
completion?(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import UIKit
|
||||
|
||||
internal class LightboxImageStub: LightboxImage {
|
||||
|
||||
// MARK: - Initialization
|
||||
init () {
|
||||
super.init()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
protocol LightboxTransitionDelegate: class {
|
||||
|
||||
func transitionDidDismissController(controller: LightboxController)
|
||||
}
|
||||
|
||||
class LightboxTransition: UIPercentDrivenInteractiveTransition {
|
||||
|
||||
struct Timing {
|
||||
static let transition: NSTimeInterval = 0.4
|
||||
}
|
||||
|
||||
lazy var panGestureRecognizer: UIPanGestureRecognizer = { [unowned self] in
|
||||
let panGestureRecognizer = UIPanGestureRecognizer()
|
||||
panGestureRecognizer.addTarget(self, action: "handlePanGesture:")
|
||||
panGestureRecognizer.delegate = self
|
||||
|
||||
return panGestureRecognizer
|
||||
}()
|
||||
|
||||
var presentingViewController = false
|
||||
var interactive = false
|
||||
var animator: UIDynamicAnimator!
|
||||
var attachmentBehavior: UIAttachmentBehavior!
|
||||
var gravityBehaviour: UIGravityBehavior!
|
||||
var snapBehavior: UISnapBehavior!
|
||||
weak var sourceViewController: LightboxController!
|
||||
weak var delegate: LightboxTransitionDelegate?
|
||||
weak var lightboxController: LightboxController!
|
||||
|
||||
weak var sourceViewCell: LightboxViewCell? {
|
||||
didSet {
|
||||
sourceViewCell?.addGestureRecognizer(panGestureRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
func transition(controller: LightboxController, show: Bool) {
|
||||
lightboxController = controller
|
||||
|
||||
if UIDevice.currentDevice().orientation != UIDeviceOrientation.LandscapeLeft
|
||||
&& UIDevice.currentDevice().orientation != UIDeviceOrientation.LandscapeRight {
|
||||
if sourceViewCell != nil {
|
||||
self.sourceViewCell?.lightboxView.imageView.center = CGPointMake(
|
||||
UIScreen.mainScreen().bounds.width/2, UIScreen.mainScreen().bounds.height/2)
|
||||
}
|
||||
|
||||
controller.pageLabel.transform = show ? CGAffineTransformIdentity : CGAffineTransformMakeTranslation(0, 250)
|
||||
for button in [controller.closeButton, controller.deleteButton] {
|
||||
button.transform = show
|
||||
? CGAffineTransformIdentity
|
||||
: CGAffineTransformMakeTranslation(0, -250)
|
||||
}
|
||||
}
|
||||
|
||||
if presentingViewController {
|
||||
controller.collectionView.transform = show ? CGAffineTransformIdentity : CGAffineTransformMakeScale(0.5, 0.5)
|
||||
controller.view.alpha = show ? 1 : 0.01
|
||||
} else if !interactive {
|
||||
controller.view.alpha = show ? 1 : 0.1
|
||||
} else {
|
||||
controller.view.alpha = show ? 1 : 0.95
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Transitioning delegate
|
||||
|
||||
extension LightboxTransition : UIViewControllerAnimatedTransitioning {
|
||||
|
||||
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
|
||||
return Timing.transition
|
||||
}
|
||||
|
||||
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
|
||||
let containerView = transitionContext.containerView()
|
||||
|
||||
let screens : (from: UIViewController, to: UIViewController) = (
|
||||
transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!,
|
||||
transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!)
|
||||
|
||||
let lightboxViewController = !presentingViewController
|
||||
? screens.from as! LightboxController
|
||||
: screens.to as! LightboxController
|
||||
|
||||
let viewController = !presentingViewController
|
||||
? screens.to as UIViewController
|
||||
: screens.from as UIViewController
|
||||
|
||||
for controller in [viewController, lightboxViewController] {
|
||||
containerView?.addSubview(controller.view)
|
||||
}
|
||||
|
||||
if presentingViewController {
|
||||
transition(lightboxViewController, show: false)
|
||||
}
|
||||
|
||||
UIView.animateWithDuration(Timing.transition, animations: { [unowned self] in
|
||||
self.transition(lightboxViewController, show: self.presentingViewController)
|
||||
}, completion: { _ in
|
||||
if transitionContext.transitionWasCancelled() {
|
||||
UIView.animateWithDuration(Timing.transition/3, animations: { [unowned self] in
|
||||
self.sourceViewCell?.lightboxView.imageView.center = CGPointMake(
|
||||
UIScreen.mainScreen().bounds.width/2, UIScreen.mainScreen().bounds.height/2)
|
||||
self.transition(lightboxViewController, show: true)
|
||||
}, completion: { finished in
|
||||
transitionContext.completeTransition(false)
|
||||
UIApplication.sharedApplication().keyWindow?.addSubview(screens.from.view)
|
||||
})
|
||||
} else {
|
||||
if self.lightboxController.view.alpha < 0.97 && !self.presentingViewController {
|
||||
UIView.animateWithDuration(0.4, animations: {
|
||||
lightboxViewController.view.alpha = 0
|
||||
}, completion: { _ in
|
||||
transitionContext.completeTransition(true)
|
||||
})
|
||||
} else {
|
||||
transitionContext.completeTransition(true)
|
||||
UIApplication.sharedApplication().keyWindow?.addSubview(screens.to.view)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Transition delegate
|
||||
|
||||
extension LightboxTransition : UIViewControllerTransitioningDelegate {
|
||||
|
||||
func animationControllerForPresentedController(presented: UIViewController,
|
||||
presentingController presenting: UIViewController,
|
||||
sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
presentingViewController = true
|
||||
return self
|
||||
}
|
||||
|
||||
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
presentingViewController = false
|
||||
return self
|
||||
}
|
||||
|
||||
func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactive ? self : nil
|
||||
}
|
||||
|
||||
func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactive ? self : nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Interactive transition delegate
|
||||
|
||||
extension LightboxTransition {
|
||||
|
||||
func handlePanGesture(panGestureRecognizer: UIPanGestureRecognizer) {
|
||||
guard let imageView = sourceViewCell?.lightboxView.imageView else { return }
|
||||
let location = panGestureRecognizer.locationInView(sourceViewCell?.lightboxView)
|
||||
let boxLocation = panGestureRecognizer.locationInView(imageView)
|
||||
let translation = panGestureRecognizer.translationInView(sourceViewCell?.lightboxView)
|
||||
let percentage = fabs(translation.y / UIScreen.mainScreen().bounds.height)
|
||||
|
||||
if percentage > 0.35 {
|
||||
transition(lightboxController, show: false)
|
||||
}
|
||||
|
||||
if let controller = sourceViewCell?.parentViewController where controller.physics {
|
||||
if panGestureRecognizer.state == UIGestureRecognizerState.Began {
|
||||
interactive = true
|
||||
sourceViewController.dismissViewControllerAnimated(true, completion: nil)
|
||||
animator.removeBehavior(snapBehavior)
|
||||
let centerOffset = UIOffsetMake(boxLocation.x - CGRectGetMidX(imageView.bounds),
|
||||
boxLocation.y - CGRectGetMidY(imageView.bounds))
|
||||
attachmentBehavior = UIAttachmentBehavior(item: imageView,
|
||||
offsetFromCenter: centerOffset, attachedToAnchor: location)
|
||||
attachmentBehavior.frequency = 0
|
||||
animator.addBehavior(attachmentBehavior)
|
||||
} else if panGestureRecognizer.state == UIGestureRecognizerState.Changed {
|
||||
attachmentBehavior.anchorPoint = location
|
||||
updateInteractiveTransition(percentage)
|
||||
} else if panGestureRecognizer.state == UIGestureRecognizerState.Ended {
|
||||
interactive = false
|
||||
|
||||
if percentage > 0.35 {
|
||||
finishInteractiveTransition()
|
||||
delegate?.transitionDidDismissController(lightboxController)
|
||||
} else {
|
||||
cancelInteractiveTransition()
|
||||
if let cell = sourceViewCell {
|
||||
animator.removeBehavior(attachmentBehavior)
|
||||
snapBehavior = UISnapBehavior(item: imageView,
|
||||
snapToPoint: cell.lightboxView.center)
|
||||
animator.addBehavior(snapBehavior)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if panGestureRecognizer.state == .Began {
|
||||
interactive = true
|
||||
sourceViewController.dismissViewControllerAnimated(true, completion: nil)
|
||||
} else if panGestureRecognizer.state == .Changed {
|
||||
imageView.center = CGPointMake(imageView.center.x, UIScreen.mainScreen().bounds.height/2 + translation.y)
|
||||
updateInteractiveTransition(percentage)
|
||||
} else {
|
||||
interactive = false
|
||||
if percentage > 0.35 {
|
||||
finishInteractiveTransition()
|
||||
delegate?.transitionDidDismissController(lightboxController)
|
||||
lightboxController.collectionView.alpha = 0
|
||||
} else {
|
||||
cancelInteractiveTransition()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func finishInteractiveTransition() {
|
||||
super.finishInteractiveTransition()
|
||||
guard let cell = sourceViewCell else { return }
|
||||
|
||||
let point = (cell.lightboxView.imageView.center.y - UIScreen.mainScreen().bounds.height/2) * 10
|
||||
|
||||
UIView.animateWithDuration(Timing.transition, animations: { [unowned self] in
|
||||
self.sourceViewCell?.lightboxView.imageView.center = CGPointMake(
|
||||
UIScreen.mainScreen().bounds.width/2, point)
|
||||
}, completion: { _ in
|
||||
guard let cell = self.sourceViewCell else { return }
|
||||
if let controller = cell.parentViewController where controller.physics {
|
||||
self.animator.removeBehavior(self.attachmentBehavior)
|
||||
self.snapBehavior = UISnapBehavior(item: cell.lightboxView.imageView,
|
||||
snapToPoint: cell.lightboxView.center)
|
||||
self.animator.addBehavior(self.snapBehavior)
|
||||
}
|
||||
})
|
||||
|
||||
sourceViewController.collectionView.scrollRectToVisible(
|
||||
CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height),
|
||||
animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Gesture recognizer delegate methods
|
||||
|
||||
extension LightboxTransition : UIGestureRecognizerDelegate {
|
||||
|
||||
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
|
||||
let translation = panGestureRecognizer.translationInView(sourceViewCell?.superview!)
|
||||
if fabs(translation.x) < fabs(translation.y) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
public class LightboxView: UIView {
|
||||
|
||||
public var minimumZoomScale: CGFloat = 1
|
||||
public var maximumZoomScale: CGFloat = 3
|
||||
var lastZoomScale: CGFloat = -1
|
||||
|
||||
lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView(frame: CGRectZero)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.userInteractionEnabled = true
|
||||
|
||||
return imageView
|
||||
}()
|
||||
|
||||
lazy var scrollView: UIScrollView = { [unowned self] in
|
||||
let scrollView = UIScrollView(frame: CGRectZero)
|
||||
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||
scrollView.multipleTouchEnabled = true
|
||||
scrollView.minimumZoomScale = self.minimumZoomScale
|
||||
scrollView.maximumZoomScale = self.maximumZoomScale
|
||||
scrollView.delegate = self
|
||||
scrollView.showsVerticalScrollIndicator = false
|
||||
scrollView.showsHorizontalScrollIndicator = false
|
||||
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
var imageConstraintLeading: NSLayoutConstraint!
|
||||
var imageConstraintTrailing: NSLayoutConstraint!
|
||||
var imageConstraintTop: NSLayoutConstraint!
|
||||
var imageConstraintBottom: NSLayoutConstraint!
|
||||
|
||||
var constraintsAdded = false
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(frame: CGRect, image: UIImage? = nil) {
|
||||
super.init(frame: frame)
|
||||
|
||||
imageView.image = image
|
||||
|
||||
let config = LightboxConfig.sharedInstance.config
|
||||
backgroundColor = config.backgroundColor
|
||||
minimumZoomScale = config.zoom.minimumScale
|
||||
maximumZoomScale = config.zoom.maximumScale
|
||||
|
||||
scrollView.addSubview(imageView)
|
||||
addSubview(scrollView)
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - View lifecycle
|
||||
|
||||
public override func didMoveToSuperview() {
|
||||
setUpConstraints()
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
public func updateViewLayout() {
|
||||
if constraintsAdded {
|
||||
updateImageConstraints()
|
||||
updateZoom()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Autolayout
|
||||
|
||||
public func setUpConstraints() {
|
||||
if !constraintsAdded {
|
||||
let layoutAttributes: [NSLayoutAttribute] = [.Leading, .Trailing, .Top, .Bottom]
|
||||
for layoutAttribute in layoutAttributes {
|
||||
addConstraint(NSLayoutConstraint(item: self.scrollView, attribute: layoutAttribute,
|
||||
relatedBy: .Equal, toItem: self, attribute: layoutAttribute,
|
||||
multiplier: 1, constant: 0))
|
||||
}
|
||||
|
||||
imageConstraintLeading = NSLayoutConstraint(item: imageView, attribute: .Leading,
|
||||
relatedBy: .Equal, toItem: scrollView, attribute: .Leading,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
imageConstraintTrailing = NSLayoutConstraint(item: imageView, attribute: .Trailing,
|
||||
relatedBy: .Equal, toItem: scrollView, attribute: .Trailing,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
imageConstraintTop = NSLayoutConstraint(item: imageView, attribute: .Top,
|
||||
relatedBy: .Equal, toItem: scrollView, attribute: .Top,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
imageConstraintBottom = NSLayoutConstraint(item: imageView, attribute: .Bottom,
|
||||
relatedBy: .Equal, toItem: scrollView, attribute: .Bottom,
|
||||
multiplier: 1, constant: 0)
|
||||
|
||||
addConstraints([imageConstraintLeading, imageConstraintTrailing,
|
||||
imageConstraintTop, imageConstraintBottom])
|
||||
|
||||
layoutIfNeeded()
|
||||
|
||||
scrollView.contentSize = CGSize(width: frame.size.width, height: frame.size.height)
|
||||
|
||||
constraintsAdded = true
|
||||
}
|
||||
}
|
||||
|
||||
public func updateImageConstraints() {
|
||||
if let image = imageView.image {
|
||||
// Center image
|
||||
var hPadding = (bounds.size.width - scrollView.zoomScale * image.size.width) / 2
|
||||
if hPadding < 0 {
|
||||
hPadding = 0
|
||||
}
|
||||
|
||||
var vPadding = (bounds.size.height - scrollView.zoomScale * image.size.height) / 2
|
||||
if vPadding < 0 {
|
||||
vPadding = 0
|
||||
}
|
||||
|
||||
for constraint in [imageConstraintLeading, imageConstraintTrailing] { constraint.constant = hPadding }
|
||||
for constraint in [imageConstraintTop, imageConstraintBottom] { constraint.constant = vPadding }
|
||||
|
||||
layoutIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Zoom
|
||||
|
||||
public func updateZoom() {
|
||||
if let image = imageView.image {
|
||||
var minimumZoom = min(
|
||||
bounds.size.width / image.size.width,
|
||||
bounds.size.height / image.size.height)
|
||||
|
||||
if minimumZoom > 1 {
|
||||
minimumZoom = 1
|
||||
}
|
||||
|
||||
scrollView.minimumZoomScale = minimumZoom
|
||||
|
||||
if minimumZoom == lastZoomScale {
|
||||
minimumZoom += 0.000001
|
||||
}
|
||||
|
||||
scrollView.zoomScale = minimumZoom
|
||||
lastZoomScale = minimumZoom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension LightboxView: UIScrollViewDelegate {
|
||||
|
||||
public func scrollViewDidZoom(scrollView: UIScrollView) {
|
||||
updateImageConstraints()
|
||||
}
|
||||
|
||||
public func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
|
||||
return imageView
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
public class LightboxViewCell: UICollectionViewCell {
|
||||
|
||||
public static let reuseIdentifier: String = "LightboxViewCell"
|
||||
|
||||
var constraintsAdded = false
|
||||
weak var parentViewController: LightboxController?
|
||||
|
||||
public lazy var lightboxView: LightboxView = { [unowned self] in
|
||||
let lightboxView = LightboxView(frame: self.bounds)
|
||||
lightboxView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
self.contentView.addSubview(lightboxView)
|
||||
|
||||
return lightboxView
|
||||
}()
|
||||
|
||||
public lazy var loadingIndicator: UIActivityIndicatorView = {
|
||||
let loadingIndicator = UIActivityIndicatorView(activityIndicatorStyle: .White)
|
||||
loadingIndicator.startAnimating()
|
||||
loadingIndicator.alpha = 0
|
||||
loadingIndicator.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
return loadingIndicator
|
||||
}()
|
||||
|
||||
public override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
if loadingIndicator.superview == nil { self.contentView.addSubview(loadingIndicator) }
|
||||
|
||||
setupConstraints()
|
||||
lightboxView.updateViewLayout()
|
||||
}
|
||||
|
||||
public func setupTransitionManager() {
|
||||
guard let controller = parentViewController else { return }
|
||||
controller.transitionManager.sourceViewCell = self
|
||||
controller.transitionManager.animator = UIDynamicAnimator(referenceView: lightboxView)
|
||||
}
|
||||
|
||||
private func setupConstraints() {
|
||||
if !constraintsAdded {
|
||||
let layoutAttributes: [NSLayoutAttribute] = [.Leading, .Trailing, .Top, .Bottom]
|
||||
for layoutAttribute in layoutAttributes {
|
||||
addConstraint(NSLayoutConstraint(item: lightboxView, attribute: layoutAttribute,
|
||||
relatedBy: .Equal, toItem: contentView, attribute: layoutAttribute,
|
||||
multiplier: 1, constant: 0))
|
||||
}
|
||||
|
||||
addConstraint(NSLayoutConstraint(item: loadingIndicator, attribute: .CenterX,
|
||||
relatedBy: .Equal, toItem: self.contentView, attribute: .CenterX,
|
||||
multiplier: 1, constant: 0))
|
||||
|
||||
addConstraint(NSLayoutConstraint(item: loadingIndicator, attribute: .CenterY,
|
||||
relatedBy: .Equal, toItem: self.contentView, attribute: .CenterY,
|
||||
multiplier: 1, constant: 0))
|
||||
|
||||
constraintsAdded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import UIKit
|
||||
|
||||
public protocol FooterViewDelegate: AnyObject {
|
||||
@MainActor
|
||||
func footerView(_ footerView: FooterView, didExpand expanded: Bool)
|
||||
}
|
||||
|
||||
open class FooterView: UIView {
|
||||
|
||||
open fileprivate(set) lazy var infoLabel: InfoLabel = { [unowned self] in
|
||||
let label = InfoLabel(text: "")
|
||||
label.isHidden = !LightboxConfig.InfoLabel.enabled
|
||||
|
||||
label.textColor = LightboxConfig.InfoLabel.textColor
|
||||
label.isUserInteractionEnabled = true
|
||||
label.delegate = self
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
open fileprivate(set) lazy var pageLabel: UILabel = { [unowned self] in
|
||||
let label = UILabel(frame: CGRect.zero)
|
||||
label.isHidden = !LightboxConfig.PageIndicator.enabled
|
||||
label.numberOfLines = 1
|
||||
|
||||
return label
|
||||
}()
|
||||
|
||||
open fileprivate(set) lazy var separatorView: UIView = { [unowned self] in
|
||||
let view = UILabel(frame: CGRect.zero)
|
||||
view.isHidden = !LightboxConfig.PageIndicator.enabled
|
||||
view.backgroundColor = LightboxConfig.PageIndicator.separatorColor
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
let gradientColors = [UIColor(hex: "040404").withAlphaComponent(0.1), UIColor(hex: "040404")]
|
||||
open weak var delegate: (any FooterViewDelegate)?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
backgroundColor = UIColor.clear
|
||||
_ = addGradientLayer(gradientColors)
|
||||
|
||||
[pageLabel, infoLabel, separatorView].forEach { addSubview($0) }
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
func expand(_ expand: Bool) {
|
||||
expand ? infoLabel.expand() : infoLabel.collapse()
|
||||
}
|
||||
|
||||
func updatePage(_ page: Int, _ numberOfPages: Int) {
|
||||
let text = "\(page)/\(numberOfPages)"
|
||||
|
||||
pageLabel.attributedText = NSAttributedString(string: text,
|
||||
attributes: LightboxConfig.PageIndicator.textAttributes)
|
||||
pageLabel.sizeToFit()
|
||||
}
|
||||
|
||||
func updateText(_ text: String) {
|
||||
infoLabel.fullText = text
|
||||
|
||||
if text.isEmpty {
|
||||
_ = removeGradientLayer()
|
||||
} else if !infoLabel.expanded {
|
||||
_ = addGradientLayer(gradientColors)
|
||||
}
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
do {
|
||||
let bottomPadding: CGFloat
|
||||
if #available(iOS 11, *) {
|
||||
bottomPadding = safeAreaInsets.bottom
|
||||
} else {
|
||||
bottomPadding = 0
|
||||
}
|
||||
|
||||
pageLabel.frame.origin = CGPoint(
|
||||
x: (frame.width - pageLabel.frame.width) / 2,
|
||||
y: frame.height - pageLabel.frame.height - 2 - bottomPadding
|
||||
)
|
||||
}
|
||||
|
||||
separatorView.frame = CGRect(
|
||||
x: 0,
|
||||
y: pageLabel.frame.minY - 2.5,
|
||||
width: frame.width,
|
||||
height: 0.5
|
||||
)
|
||||
|
||||
infoLabel.frame.origin.y = separatorView.frame.minY - infoLabel.frame.height - 15
|
||||
|
||||
resizeGradientLayer()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension FooterView: LayoutConfigurable {
|
||||
|
||||
@objc public func configureLayout() {
|
||||
infoLabel.frame = CGRect(x: 17, y: 0, width: frame.width - 17 * 2, height: 35)
|
||||
infoLabel.configureLayout()
|
||||
}
|
||||
}
|
||||
|
||||
extension FooterView: InfoLabelDelegate {
|
||||
|
||||
public func infoLabel(_ infoLabel: InfoLabel, didExpand expanded: Bool) {
|
||||
_ = (expanded || infoLabel.fullText.isEmpty) ? removeGradientLayer() : addGradientLayer(gradientColors)
|
||||
delegate?.footerView(self, didExpand: expanded)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import UIKit
|
||||
|
||||
protocol HeaderViewDelegate: AnyObject {
|
||||
@MainActor
|
||||
func headerView(_ headerView: HeaderView, didPressDeleteButton deleteButton: UIButton)
|
||||
@MainActor
|
||||
func headerView(_ headerView: HeaderView, didPressCloseButton closeButton: UIButton)
|
||||
}
|
||||
|
||||
open class HeaderView: UIView {
|
||||
open fileprivate(set) lazy var closeButton: UIButton = { [unowned self] in
|
||||
let title = NSAttributedString(
|
||||
string: LightboxConfig.CloseButton.text,
|
||||
attributes: LightboxConfig.CloseButton.textAttributes)
|
||||
|
||||
let button = UIButton(type: .system)
|
||||
|
||||
button.setAttributedTitle(title, for: UIControl.State())
|
||||
|
||||
if let size = LightboxConfig.CloseButton.size {
|
||||
button.frame.size = size
|
||||
} else {
|
||||
button.sizeToFit()
|
||||
}
|
||||
|
||||
button.addTarget(self, action: #selector(closeButtonDidPress(_:)),
|
||||
for: .touchUpInside)
|
||||
|
||||
if let image = LightboxConfig.CloseButton.image {
|
||||
button.setBackgroundImage(image, for: UIControl.State())
|
||||
}
|
||||
|
||||
button.isHidden = !LightboxConfig.CloseButton.enabled
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
open fileprivate(set) lazy var deleteButton: UIButton = { [unowned self] in
|
||||
let title = NSAttributedString(
|
||||
string: LightboxConfig.DeleteButton.text,
|
||||
attributes: LightboxConfig.DeleteButton.textAttributes)
|
||||
|
||||
let button = UIButton(type: .system)
|
||||
|
||||
button.setAttributedTitle(title, for: .normal)
|
||||
|
||||
if let size = LightboxConfig.DeleteButton.size {
|
||||
button.frame.size = size
|
||||
} else {
|
||||
button.sizeToFit()
|
||||
}
|
||||
|
||||
button.addTarget(self, action: #selector(deleteButtonDidPress(_:)),
|
||||
for: .touchUpInside)
|
||||
|
||||
if let image = LightboxConfig.DeleteButton.image {
|
||||
button.setBackgroundImage(image, for: UIControl.State())
|
||||
}
|
||||
|
||||
button.isHidden = !LightboxConfig.DeleteButton.enabled
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
weak var delegate: (any HeaderViewDelegate)?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init() {
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
backgroundColor = UIColor.clear
|
||||
|
||||
[closeButton, deleteButton].forEach { addSubview($0) }
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func deleteButtonDidPress(_ button: UIButton) {
|
||||
delegate?.headerView(self, didPressDeleteButton: button)
|
||||
}
|
||||
|
||||
@objc func closeButtonDidPress(_ button: UIButton) {
|
||||
delegate?.headerView(self, didPressCloseButton: button)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension HeaderView: LayoutConfigurable {
|
||||
|
||||
@objc public func configureLayout() {
|
||||
let topPadding: CGFloat
|
||||
|
||||
if #available(iOS 11, *) {
|
||||
topPadding = safeAreaInsets.top
|
||||
} else {
|
||||
topPadding = 0
|
||||
}
|
||||
|
||||
closeButton.frame.origin = CGPoint(
|
||||
x: bounds.width - closeButton.frame.width - 17,
|
||||
y: topPadding
|
||||
)
|
||||
|
||||
deleteButton.frame.origin = CGPoint(
|
||||
x: 17,
|
||||
y: topPadding
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import UIKit
|
||||
|
||||
public protocol InfoLabelDelegate: AnyObject {
|
||||
@MainActor
|
||||
func infoLabel(_ infoLabel: InfoLabel, didExpand expanded: Bool)
|
||||
}
|
||||
|
||||
open class InfoLabel: UILabel {
|
||||
|
||||
lazy var tapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
|
||||
let gesture = UITapGestureRecognizer()
|
||||
gesture.addTarget(self, action: #selector(labelDidTap(_:)))
|
||||
|
||||
return gesture
|
||||
}()
|
||||
|
||||
open var numberOfVisibleLines = 2
|
||||
|
||||
var ellipsis: String {
|
||||
return "... \(LightboxConfig.InfoLabel.ellipsisText)"
|
||||
}
|
||||
|
||||
open weak var delegate: (any InfoLabelDelegate)?
|
||||
fileprivate var shortText = ""
|
||||
|
||||
var fullText: String {
|
||||
didSet {
|
||||
shortText = truncatedText
|
||||
updateText(fullText)
|
||||
configureLayout()
|
||||
}
|
||||
}
|
||||
|
||||
var expandable: Bool {
|
||||
return shortText != fullText
|
||||
}
|
||||
|
||||
fileprivate(set) var expanded = false {
|
||||
didSet {
|
||||
delegate?.infoLabel(self, didExpand: expanded)
|
||||
}
|
||||
}
|
||||
|
||||
var truncatedText: String {
|
||||
var truncatedText = fullText
|
||||
|
||||
guard numberOfLines(fullText) > numberOfVisibleLines else {
|
||||
return truncatedText
|
||||
}
|
||||
|
||||
// Perform quick "rough cut"
|
||||
while numberOfLines(truncatedText) > numberOfVisibleLines * 2 {
|
||||
truncatedText = String(truncatedText.prefix(truncatedText.count / 2))
|
||||
}
|
||||
|
||||
// Capture the endIndex of truncatedText before appending ellipsis
|
||||
var truncatedTextCursor = truncatedText.endIndex
|
||||
|
||||
truncatedText += ellipsis
|
||||
|
||||
// Remove characters ahead of ellipsis until the text is the right number of lines
|
||||
while numberOfLines(truncatedText) > numberOfVisibleLines {
|
||||
// To avoid "Cannot decrement before startIndex"
|
||||
guard truncatedTextCursor > truncatedText.startIndex else {
|
||||
break
|
||||
}
|
||||
|
||||
truncatedTextCursor = truncatedText.index(before: truncatedTextCursor)
|
||||
truncatedText.remove(at: truncatedTextCursor)
|
||||
}
|
||||
|
||||
return truncatedText
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
public init(text: String, expanded: Bool = false) {
|
||||
self.fullText = text
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
numberOfLines = 0
|
||||
updateText(text)
|
||||
self.expanded = expanded
|
||||
|
||||
addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
public required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc func labelDidTap(_ tapGestureRecognizer: UITapGestureRecognizer) {
|
||||
shortText = truncatedText
|
||||
expanded ? collapse() : expand()
|
||||
}
|
||||
|
||||
func expand() {
|
||||
frame.size.height = heightForString(fullText)
|
||||
updateText(fullText)
|
||||
|
||||
expanded = expandable
|
||||
}
|
||||
|
||||
func collapse() {
|
||||
frame.size.height = heightForString(shortText)
|
||||
updateText(shortText)
|
||||
|
||||
expanded = false
|
||||
}
|
||||
|
||||
fileprivate func updateText(_ string: String) {
|
||||
let textAttributes = LightboxConfig.InfoLabel.textAttributes
|
||||
let attributedString = NSMutableAttributedString(string: string, attributes: textAttributes)
|
||||
|
||||
if let range = string.range(of: ellipsis) {
|
||||
let ellipsisColor = LightboxConfig.InfoLabel.ellipsisColor
|
||||
let ellipsisRange = NSRange(range, in: string)
|
||||
attributedString.addAttribute(.foregroundColor, value: ellipsisColor, range: ellipsisRange)
|
||||
}
|
||||
|
||||
attributedText = attributedString
|
||||
}
|
||||
|
||||
// MARK: - Helper methods
|
||||
|
||||
fileprivate func heightForString(_ string: String) -> CGFloat {
|
||||
return string.boundingRect(
|
||||
with: CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude),
|
||||
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||
attributes: [NSAttributedString.Key.font: font!],
|
||||
context: nil).height
|
||||
}
|
||||
|
||||
fileprivate func numberOfLines(_ string: String) -> Int {
|
||||
let lineHeight = "A".size(withAttributes: [NSAttributedString.Key.font: font!]).height
|
||||
let totalHeight = heightForString(string)
|
||||
|
||||
return Int(totalHeight / lineHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension InfoLabel: LayoutConfigurable {
|
||||
|
||||
@objc public func configureLayout() {
|
||||
shortText = truncatedText
|
||||
expanded ? expand() : collapse()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import UIKit
|
||||
|
||||
class LoadingIndicator: UIView {
|
||||
|
||||
var indicator: UIActivityIndicatorView!
|
||||
|
||||
init() {
|
||||
super.init(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
|
||||
|
||||
backgroundColor = UIColor.darkGray
|
||||
layer.cornerRadius = bounds.size.width / 2
|
||||
clipsToBounds = true
|
||||
alpha = 0
|
||||
|
||||
indicator = UIActivityIndicatorView()
|
||||
indicator.style = .whiteLarge
|
||||
indicator.startAnimating()
|
||||
|
||||
addSubview(indicator)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
indicator.center = CGPoint(x: bounds.size.width/2, y: bounds.size.height/2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import UIKit
|
||||
|
||||
protocol PageViewDelegate: AnyObject {
|
||||
@MainActor
|
||||
func pageViewDidZoom(_ pageView: PageView)
|
||||
@MainActor
|
||||
func remoteImageDidLoad(_ image: UIImage?, imageView: UIImageView)
|
||||
@MainActor
|
||||
func pageView(_ pageView: PageView, didTouchPlayButton videoURL: URL)
|
||||
@MainActor
|
||||
func pageViewDidTouch(_ pageView: PageView)
|
||||
@MainActor
|
||||
func pageViewDidTap(_ pageView: PageView)
|
||||
@MainActor
|
||||
func pageViewDidDoubleTap(_ pageView: PageView)}
|
||||
|
||||
class PageView: UIScrollView {
|
||||
|
||||
lazy var imageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFit
|
||||
imageView.clipsToBounds = true
|
||||
imageView.isUserInteractionEnabled = true
|
||||
|
||||
return imageView
|
||||
}()
|
||||
|
||||
lazy var playButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.frame.size = CGSize(width: 60, height: 60)
|
||||
var buttonImage = AssetManager.image("lightbox_play")
|
||||
|
||||
// Note by Elvis Nuñez on Mon 22 Jun 08:06
|
||||
// When using SPM you might find that assets are note included. This is a workaround to provide default assets
|
||||
// under iOS 13 so using SPM can work without problems.
|
||||
if #available(iOS 13.0, *) {
|
||||
if buttonImage == nil {
|
||||
buttonImage = UIImage(systemName: "play.circle.fill")
|
||||
}
|
||||
}
|
||||
|
||||
button.setBackgroundImage(buttonImage, for: UIControl.State())
|
||||
button.addTarget(self, action: #selector(playButtonTouched(_:)), for: .touchUpInside)
|
||||
button.tintColor = .white
|
||||
|
||||
button.layer.shadowOffset = CGSize(width: 1, height: 1)
|
||||
button.layer.shadowColor = UIColor.gray.cgColor
|
||||
button.layer.masksToBounds = false
|
||||
button.layer.shadowOpacity = 0.8
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var loadingIndicator: UIView = LightboxConfig.makeLoadingIndicator()
|
||||
|
||||
var image: LightboxImage
|
||||
var contentFrame = CGRect.zero
|
||||
weak var pageViewDelegate: (any PageViewDelegate)?
|
||||
|
||||
var hasZoomed: Bool {
|
||||
return zoomScale != 1.0
|
||||
}
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init(image: LightboxImage) {
|
||||
self.image = image
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
configure()
|
||||
|
||||
fetchImage()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configure() {
|
||||
addSubview(imageView)
|
||||
|
||||
updatePlayButton()
|
||||
|
||||
addSubview(loadingIndicator)
|
||||
|
||||
delegate = self
|
||||
isMultipleTouchEnabled = true
|
||||
minimumZoomScale = LightboxConfig.Zoom.minimumScale
|
||||
maximumZoomScale = LightboxConfig.Zoom.maximumScale
|
||||
showsHorizontalScrollIndicator = false
|
||||
showsVerticalScrollIndicator = false
|
||||
|
||||
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(scrollViewDoubleTapped(_:)))
|
||||
doubleTapRecognizer.numberOfTapsRequired = 2
|
||||
doubleTapRecognizer.numberOfTouchesRequired = 1
|
||||
addGestureRecognizer(doubleTapRecognizer)
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(viewTapped(_:)))
|
||||
addGestureRecognizer(tapRecognizer)
|
||||
|
||||
tapRecognizer.require(toFail: doubleTapRecognizer)
|
||||
}
|
||||
|
||||
// MARK: - Update
|
||||
func update(with image: LightboxImage) {
|
||||
self.image = image
|
||||
updatePlayButton()
|
||||
fetchImage()
|
||||
}
|
||||
|
||||
func updatePlayButton () {
|
||||
if self.image.videoURL != nil && !subviews.contains(playButton) {
|
||||
addSubview(playButton)
|
||||
} else if self.image.videoURL == nil && subviews.contains(playButton) {
|
||||
playButton.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Fetch
|
||||
private func fetchImage () {
|
||||
loadingIndicator.alpha = 1
|
||||
self.image.addImageTo(imageView) { [weak self] image in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.isUserInteractionEnabled = true
|
||||
self.configureImageView()
|
||||
self.pageViewDelegate?.remoteImageDidLoad(image, imageView: self.imageView)
|
||||
|
||||
UIView.animate(withDuration: 0.4) {
|
||||
self.loadingIndicator.alpha = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Recognizers
|
||||
|
||||
@objc func scrollViewDoubleTapped(_ recognizer: UITapGestureRecognizer) {
|
||||
let pointInView = recognizer.location(in: imageView)
|
||||
let newZoomScale = zoomScale > minimumZoomScale
|
||||
? minimumZoomScale
|
||||
: maximumZoomScale
|
||||
|
||||
let width = contentFrame.size.width / newZoomScale
|
||||
let height = contentFrame.size.height / newZoomScale
|
||||
let x = pointInView.x - (width / 2.0)
|
||||
let y = pointInView.y - (height / 2.0)
|
||||
|
||||
let rectToZoomTo = CGRect(x: x, y: y, width: width, height: height)
|
||||
|
||||
zoom(to: rectToZoomTo, animated: true)
|
||||
pageViewDelegate?.pageViewDidDoubleTap(self)
|
||||
}
|
||||
|
||||
@objc func viewTapped(_ recognizer: UITapGestureRecognizer) {
|
||||
pageViewDelegate?.pageViewDidTouch(self)
|
||||
pageViewDelegate?.pageViewDidTap(self)
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
loadingIndicator.center = imageView.center
|
||||
playButton.center = imageView.center
|
||||
}
|
||||
|
||||
func configureImageView() {
|
||||
guard let image = imageView.image else {
|
||||
centerImageView()
|
||||
return
|
||||
}
|
||||
|
||||
let imageViewSize = imageView.frame.size
|
||||
let imageSize = image.size
|
||||
let realImageViewSize: CGSize
|
||||
|
||||
if imageSize.width / imageSize.height > imageViewSize.width / imageViewSize.height {
|
||||
realImageViewSize = CGSize(
|
||||
width: imageViewSize.width,
|
||||
height: imageViewSize.width / imageSize.width * imageSize.height)
|
||||
} else {
|
||||
realImageViewSize = CGSize(
|
||||
width: imageViewSize.height / imageSize.height * imageSize.width,
|
||||
height: imageViewSize.height)
|
||||
}
|
||||
|
||||
imageView.frame = CGRect(origin: CGPoint.zero, size: realImageViewSize)
|
||||
|
||||
centerImageView()
|
||||
}
|
||||
|
||||
func centerImageView() {
|
||||
let boundsSize = contentFrame.size
|
||||
var imageViewFrame = imageView.frame
|
||||
|
||||
if imageViewFrame.size.width < boundsSize.width {
|
||||
imageViewFrame.origin.x = (boundsSize.width - imageViewFrame.size.width) / 2.0
|
||||
} else {
|
||||
imageViewFrame.origin.x = 0.0
|
||||
}
|
||||
|
||||
if imageViewFrame.size.height < boundsSize.height {
|
||||
imageViewFrame.origin.y = (boundsSize.height - imageViewFrame.size.height) / 2.0
|
||||
} else {
|
||||
imageViewFrame.origin.y = 0.0
|
||||
}
|
||||
|
||||
imageView.frame = imageViewFrame
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
@objc func playButtonTouched(_ button: UIButton) {
|
||||
guard let videoURL = image.videoURL else { return }
|
||||
|
||||
pageViewDelegate?.pageView(self, didTouchPlayButton: videoURL as URL)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension PageView: LayoutConfigurable {
|
||||
|
||||
@objc func configureLayout() {
|
||||
contentFrame = frame
|
||||
contentSize = frame.size
|
||||
imageView.frame = frame
|
||||
zoomScale = minimumZoomScale
|
||||
|
||||
configureImageView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UIScrollViewDelegate
|
||||
|
||||
extension PageView: UIScrollViewDelegate {
|
||||
|
||||
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
|
||||
return imageView
|
||||
}
|
||||
|
||||
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
||||
centerImageView()
|
||||
pageViewDelegate?.pageViewDidZoom(self)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
machine:
|
||||
xcode:
|
||||
version: "9.0"
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- rm -rf Carthage
|
||||
- carthage update
|
||||
|
||||
test:
|
||||
override:
|
||||
- set -o pipefail && xcodebuild -project Lightbox.xcodeproj -scheme "Lightbox-iOS" -sdk iphonesimulator clean build
|
||||
@@ -0,0 +1,18 @@
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
lazy var controller: UINavigationController = UINavigationController(rootViewController: ViewController())
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow()
|
||||
window?.rootViewController = controller
|
||||
window?.makeKeyAndVisible()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo3.jpg"
|
||||
"filename" : "photo1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 1.7 MiB |
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo1.jpg"
|
||||
"filename" : "photo2.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 1.5 MiB |
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo2.jpg"
|
||||
"filename" : "photo3.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 987 KiB |
@@ -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>
|
||||
@@ -3,21 +3,19 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>no.hyper.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
@@ -31,6 +29,15 @@
|
||||
<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>
|
||||
@@ -0,0 +1,67 @@
|
||||
import UIKit
|
||||
import Lightbox
|
||||
import SDWebImage
|
||||
|
||||
class ViewController: UIViewController {
|
||||
|
||||
lazy var showButton: UIButton = { [unowned self] in
|
||||
let button = UIButton()
|
||||
button.addTarget(self, action: #selector(showLightbox), for: .touchUpInside)
|
||||
button.setTitle("Show me the lightbox", for: UIControl.State())
|
||||
button.setTitleColor(UIColor(red:0.47, green:0.6, blue:0.13, alpha:1), for: UIControl.State())
|
||||
button.titleLabel?.font = UIFont(name: "AvenirNextCondensed-DemiBold", size: 30)
|
||||
button.frame = UIScreen.main.bounds
|
||||
button.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin]
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin]
|
||||
view.backgroundColor = UIColor.white
|
||||
view.addSubview(showButton)
|
||||
title = "Lightbox"
|
||||
LightboxConfig.preload = 2
|
||||
LightboxConfig.loadImage = { imageView, url, completion in
|
||||
imageView.sd_setImage(with: url) { image, _, _ , _ in
|
||||
completion?(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Action methods
|
||||
|
||||
@objc func showLightbox() {
|
||||
let images = [
|
||||
LightboxImage(imageURL: URL(string: "https://media.giphy.com/media/Ku65904QQe4yez448B/giphy.gif")!),
|
||||
LightboxImage(imageURL: URL(string: "https://media.giphy.com/media/lQDLwWUMPaAHvh8pAG/giphy.gif")!),
|
||||
LightboxImage(imageURL: URL(string: "https://media.giphy.com/media/ontKwPWJxARsuKaKqJ/giphy.gif")!),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo1")!,
|
||||
text: "Photography is the science, art, application and practice of creating durable images by recording light or other electromagnetic radiation, either electronically by means of an image sensor, or chemically by means of a light-sensitive material such as photographic film"
|
||||
),
|
||||
|
||||
LightboxImage(imageURL: URL(string: "https://via.placeholder.com/300.png/09f/fff")!),
|
||||
|
||||
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo2")!,
|
||||
text: "Emoji 😍 (/ɪˈmoʊdʒi/; singular emoji, plural emoji or emojis;[4] from the Japanese 絵文字えもじ, pronounced [emodʑi]) are ideograms and smileys used in electronic messages and web pages. Emoji are used much like emoticons and exist in various genres, including facial expressions, common objects, places and types of weather 🌅☔️💦, and animals 🐶🐱",
|
||||
videoURL: URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo3")!,
|
||||
text: "A lightbox is a translucent surface illuminated from behind, used for situations where a shape laid upon the surface needs to be seen with high contrast."
|
||||
),
|
||||
LightboxImage(imageURL: URL(string: "https://c.tenor.com/kccsHXtdDn0AAAAC/alcohol-wine.gif")!)
|
||||
]
|
||||
|
||||
let controller = LightboxController(images: images)
|
||||
controller.dynamicBackground = true
|
||||
|
||||
present(controller, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||