Compare commits
288 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 @@
|
||||
4.0
|
||||
@@ -0,0 +1,44 @@
|
||||
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
|
||||
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/Hue" ~> 3.0
|
||||
github "hyperoslo/Imaginary" ~> 3.0
|
||||
@@ -0,0 +1,4 @@
|
||||
github "hyperoslo/Cache" "4.0.2"
|
||||
github "hyperoslo/Hue" "3.0.0"
|
||||
github "hyperoslo/Imaginary" "3.0.0"
|
||||
github "onmyway133/SwiftHash" "2.0.1"
|
||||
@@ -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,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>
|
||||
|
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
|
||||
@@ -0,0 +1,44 @@
|
||||
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
|
||||
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle)
|
||||
@@ -0,0 +1,400 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
29B4A42D1C43A4320060ED52 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B4A42C1C43A4320060ED52 /* AppDelegate.swift */; };
|
||||
29B4A42F1C43A4320060ED52 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29B4A42E1C43A4320060ED52 /* ViewController.swift */; };
|
||||
29B4A4341C43A4320060ED52 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 29B4A4331C43A4320060ED52 /* Assets.xcassets */; };
|
||||
29B4A4371C43A4320060ED52 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 29B4A4351C43A4320060ED52 /* LaunchScreen.storyboard */; };
|
||||
B89E57B7A21C44D762123511 /* Pods_DemoLightbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE4E898FD95F4D90F624B067 /* Pods_DemoLightbox.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
064155DBC618EBACF5C257C6 /* Pods-DemoLightbox.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoLightbox.release.xcconfig"; path = "Pods/Target Support Files/Pods-DemoLightbox/Pods-DemoLightbox.release.xcconfig"; sourceTree = "<group>"; };
|
||||
29B4A4291C43A4320060ED52 /* DemoLightbox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoLightbox.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
29B4A42C1C43A4320060ED52 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
29B4A42E1C43A4320060ED52 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
29B4A4331C43A4320060ED52 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
29B4A4361C43A4320060ED52 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
29B4A4381C43A4320060ED52 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6096248776155BE47C0A196A /* Pods-DemoLightbox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoLightbox.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DemoLightbox/Pods-DemoLightbox.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
DAE713340DA5D2F2EF13EA8D /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
EE4E898FD95F4D90F624B067 /* Pods_DemoLightbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DemoLightbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
29B4A4261C43A4320060ED52 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B89E57B7A21C44D762123511 /* Pods_DemoLightbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0C8887567644E86396B8D885 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6096248776155BE47C0A196A /* Pods-DemoLightbox.debug.xcconfig */,
|
||||
064155DBC618EBACF5C257C6 /* Pods-DemoLightbox.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B4A4201C43A4320060ED52 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B4A42B1C43A4320060ED52 /* DemoLightbox */,
|
||||
29B4A42A1C43A4320060ED52 /* Products */,
|
||||
B1B2B3DADEEC7FD14D4A9FF8 /* Frameworks */,
|
||||
0C8887567644E86396B8D885 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B4A42A1C43A4320060ED52 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B4A4291C43A4320060ED52 /* DemoLightbox.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
29B4A42B1C43A4320060ED52 /* DemoLightbox */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B4A42C1C43A4320060ED52 /* AppDelegate.swift */,
|
||||
29B4A42E1C43A4320060ED52 /* ViewController.swift */,
|
||||
29B4A4331C43A4320060ED52 /* Assets.xcassets */,
|
||||
29B4A4351C43A4320060ED52 /* LaunchScreen.storyboard */,
|
||||
29B4A4381C43A4320060ED52 /* Info.plist */,
|
||||
);
|
||||
path = DemoLightbox;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B2B3DADEEC7FD14D4A9FF8 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAE713340DA5D2F2EF13EA8D /* Pods.framework */,
|
||||
EE4E898FD95F4D90F624B067 /* Pods_DemoLightbox.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
29B4A4281C43A4320060ED52 /* DemoLightbox */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 29B4A43B1C43A4320060ED52 /* Build configuration list for PBXNativeTarget "DemoLightbox" */;
|
||||
buildPhases = (
|
||||
4DE126F6B5D0C8D6C0E7D848 /* [CP] Check Pods Manifest.lock */,
|
||||
29B4A4251C43A4320060ED52 /* Sources */,
|
||||
29B4A4261C43A4320060ED52 /* Frameworks */,
|
||||
29B4A4271C43A4320060ED52 /* Resources */,
|
||||
F162948C45248BBD677112BF /* [CP] Embed Pods Frameworks */,
|
||||
D1AB0C213924D367899FA1A2 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = DemoLightbox;
|
||||
productName = DemoLightbox;
|
||||
productReference = 29B4A4291C43A4320060ED52 /* DemoLightbox.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
29B4A4211C43A4320060ED52 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0720;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = "Hyper Interaktiv AS";
|
||||
TargetAttributes = {
|
||||
29B4A4281C43A4320060ED52 = {
|
||||
CreatedOnToolsVersion = 7.2;
|
||||
DevelopmentTeam = LG4DBY4QF9;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 29B4A4241C43A4320060ED52 /* Build configuration list for PBXProject "DemoLightbox" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 29B4A4201C43A4320060ED52;
|
||||
productRefGroup = 29B4A42A1C43A4320060ED52 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
29B4A4281C43A4320060ED52 /* DemoLightbox */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
29B4A4271C43A4320060ED52 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
29B4A4371C43A4320060ED52 /* LaunchScreen.storyboard in Resources */,
|
||||
29B4A4341C43A4320060ED52 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
4DE126F6B5D0C8D6C0E7D848 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-DemoLightbox-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D1AB0C213924D367899FA1A2 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DemoLightbox/Pods-DemoLightbox-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
F162948C45248BBD677112BF /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-DemoLightbox/Pods-DemoLightbox-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/Cache/Cache.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Hue/Hue.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Imaginary/Imaginary.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/Lightbox/Lightbox.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SwiftHash/SwiftHash.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Cache.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Hue.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Imaginary.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lightbox.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftHash.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DemoLightbox/Pods-DemoLightbox-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
29B4A4251C43A4320060ED52 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
29B4A42F1C43A4320060ED52 /* ViewController.swift in Sources */,
|
||||
29B4A42D1C43A4320060ED52 /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
29B4A4351C43A4320060ED52 /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
29B4A4361C43A4320060ED52 /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
29B4A4391C43A4320060ED52 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
29B4A43A1C43A4320060ED52 /* 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_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = 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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_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 = 9.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
29B4A43C1C43A4320060ED52 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6096248776155BE47C0A196A /* Pods-DemoLightbox.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = LG4DBY4QF9;
|
||||
INFOPLIST_FILE = DemoLightbox/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.DemoLightbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
29B4A43D1C43A4320060ED52 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 064155DBC618EBACF5C257C6 /* Pods-DemoLightbox.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = LG4DBY4QF9;
|
||||
INFOPLIST_FILE = DemoLightbox/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.DemoLightbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
29B4A4241C43A4320060ED52 /* Build configuration list for PBXProject "DemoLightbox" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
29B4A4391C43A4320060ED52 /* Debug */,
|
||||
29B4A43A1C43A4320060ED52 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
29B4A43B1C43A4320060ED52 /* Build configuration list for PBXNativeTarget "DemoLightbox" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
29B4A43C1C43A4320060ED52 /* Debug */,
|
||||
29B4A43D1C43A4320060ED52 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 29B4A4211C43A4320060ED52 /* Project object */;
|
||||
}
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:LightboxDemo.xcodeproj">
|
||||
location = "self:DemoLightbox.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -2,7 +2,7 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:LightboxDemo.xcodeproj">
|
||||
location = "group:DemoLightbox.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
@@ -0,0 +1,18 @@
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
lazy var controller: UIViewController = ViewController()
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
|
||||
window = UIWindow()
|
||||
window?.rootViewController = controller
|
||||
window?.makeKeyAndVisible()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo1.jpg"
|
||||
"filename" : "photo1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 1.7 MiB |
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo2.jpg"
|
||||
"filename" : "photo2.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 1.5 MiB |
@@ -6,8 +6,8 @@
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "photo3.jpg"
|
||||
"filename" : "photo3.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 987 KiB |
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -7,7 +7,7 @@
|
||||
<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>
|
||||
@@ -31,6 +31,16 @@
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!--Include to allow all connections (DANGER)-->
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,52 @@
|
||||
import UIKit
|
||||
import Lightbox
|
||||
|
||||
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: UIControlState())
|
||||
button.setTitleColor(UIColor(red:0.47, green:0.6, blue:0.13, alpha:1), for: UIControlState())
|
||||
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)
|
||||
}
|
||||
|
||||
// MARK: - Action methods
|
||||
|
||||
@objc func showLightbox() {
|
||||
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: "Some very long lorem ipsum text. Some very long lorem ipsum text. Some very long lorem ipsum text. Some very long lorem ipsum text"
|
||||
),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo2")!,
|
||||
text: "🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🏃 🌲 🏃♀️ 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲\n\nSuspendisse massa massa, maximus et finibus ac, auctor volutpat diam.\n\nPellentesque consequat magna condimentum mauris bibendum, nec ornare nisl hendrerit. Phasellus nec ultrices sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nSuspendisse sit amet facilisis ante, ac suscipit sem. Integer feugiat sit amet erat sit amet mattis. Donec tristique, nunc ut varius elementum, nisi elit viverra ipsum, vitae aliquam justo libero in arcu. Quisque tempor et justo at malesuada. Curabitur justo dolor, ornare convallis sollicitudin sed, consectetur eu turpis. \n\nNulla et dui condimentum, laoreet lacus eu, ultrices nisl. Vivamus in ante volutpat, gravida nunc scelerisque, sagittis tellus. Nullam justo purus, sagittis a tincidunt a, maximus nec sem.",
|
||||
videoURL: URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
),
|
||||
LightboxImage(
|
||||
image: UIImage(named: "photo3")!,
|
||||
text: "Some very long lorem ipsum text."
|
||||
)
|
||||
]
|
||||
|
||||
let controller = LightboxController(images: images)
|
||||
controller.dynamicBackground = true
|
||||
|
||||
present(controller, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
platform :ios, '9.0'
|
||||
|
||||
target 'DemoLightbox' do
|
||||
use_frameworks!
|
||||
pod 'Lightbox', path: '../../'
|
||||
end
|
||||
@@ -0,0 +1,28 @@
|
||||
PODS:
|
||||
- Cache (4.0.2):
|
||||
- SwiftHash (~> 2.0.0)
|
||||
- Hue (3.0.0)
|
||||
- Imaginary (3.0.0):
|
||||
- Cache (~> 4.0)
|
||||
- Lightbox (2.0.0):
|
||||
- Hue (~> 3.0)
|
||||
- Imaginary (~> 3.0)
|
||||
- SwiftHash (2.0.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Lightbox (from `../../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Lightbox:
|
||||
:path: ../../
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Cache: 363b6899cee63c82ccbd291e64a6c202abc17a88
|
||||
Hue: b8fe1e43eef13631331eebecb2198b68e2622f95
|
||||
Imaginary: 2765d293d425cbed3b07fa11642554cbaebe913d
|
||||
Lightbox: f7f1cc942d81e84d85095531208f5fe1bfd9c639
|
||||
SwiftHash: d2e09b13495447178cdfb8e46e54a5c46f15f5a9
|
||||
|
||||
PODFILE CHECKSUM: 408ae3477507a1d4b7ff06ffb3f162eda443424f
|
||||
|
||||
COCOAPODS: 1.3.1
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -1,15 +1,20 @@
|
||||
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.0.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, '9.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.dependency 'Hue', '~> 3.0'
|
||||
s.dependency 'Imaginary', '~> 3.0'
|
||||
|
||||
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0' }
|
||||
end
|
||||
|
||||
@@ -7,25 +7,41 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D523B0BC1C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D523B0B51C43AA8A001AD1EC /* CenterCellCollectionViewFlowLayout.swift */; };
|
||||
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D22006731DFB4D9700E92898 /* Lightbox.bundle */; };
|
||||
D2A58F5E1F7943A30064F14E /* Imaginary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2A58F5D1F7943A30064F14E /* Imaginary.framework */; };
|
||||
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 */; };
|
||||
D54DFCC21C5AAAF100ADEA0E /* Hue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D54DFCC01C5AAAF100ADEA0E /* Hue.framework */; };
|
||||
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 PBXFileReference section */
|
||||
D22006731DFB4D9700E92898 /* Lightbox.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Lightbox.bundle; sourceTree = "<group>"; };
|
||||
D2A58F5D1F7943A30064F14E /* Imaginary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Imaginary.framework; path = Carthage/Build/iOS/Imaginary.framework; 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>"; };
|
||||
D54DFCC01C5AAAF100ADEA0E /* Hue.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Hue.framework; path = Carthage/Build/iOS/Hue.framework; 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 */
|
||||
@@ -33,18 +49,39 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D2A58F5E1F7943A30064F14E /* Imaginary.framework in Frameworks */,
|
||||
D54DFCC21C5AAAF100ADEA0E /* Hue.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
D22006721DFB4D9700E92898 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D22006731DFB4D9700E92898 /* Lightbox.bundle */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D2A58F5C1F7943A30064F14E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D2A58F5D1F7943A30064F14E /* Imaginary.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D523B09F1C43AA2A001AD1EC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D22006721DFB4D9700E92898 /* Resources */,
|
||||
D54DFCC01C5AAAF100ADEA0E /* Hue.framework */,
|
||||
D523B0B41C43AA8A001AD1EC /* Source */,
|
||||
D523B0AB1C43AA2A001AD1EC /* Lightbox */,
|
||||
D523B0AA1C43AA2A001AD1EC /* Products */,
|
||||
D2A58F5C1F7943A30064F14E /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -67,17 +104,38 @@
|
||||
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 */,
|
||||
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 */,
|
||||
);
|
||||
path = Library;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -99,6 +157,8 @@
|
||||
D523B0A51C43AA2A001AD1EC /* Frameworks */,
|
||||
D523B0A61C43AA2A001AD1EC /* Headers */,
|
||||
D523B0A71C43AA2A001AD1EC /* Resources */,
|
||||
D54DFCC41C5AAAF600ADEA0E /* Copy frameworks with Carthage */,
|
||||
5CF8A88D1F50B4EA00C28475 /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -115,11 +175,12 @@
|
||||
D523B0A01C43AA2A001AD1EC /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0720;
|
||||
LastUpgradeCheck = 0900;
|
||||
ORGANIZATIONNAME = "Hyper Interaktiv AS";
|
||||
TargetAttributes = {
|
||||
D523B0A81C43AA2A001AD1EC = {
|
||||
CreatedOnToolsVersion = 7.2;
|
||||
LastSwiftMigration = 0900;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -145,23 +206,63 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
5CF8A88D1F50B4EA00C28475 /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
|
||||
};
|
||||
D54DFCC41C5AAAF600ADEA0E /* Copy frameworks with Carthage */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/Carthage/Build/iOS/Hue.framework",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/Imaginary.framework",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/Cache.framework",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/SwiftHash.framework",
|
||||
);
|
||||
name = "Copy frameworks with Carthage";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/usr/local/bin/carthage copy-frameworks";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
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 */,
|
||||
D2D71BBC1D54DA77006AB907 /* AssetManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -176,13 +277,21 @@
|
||||
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_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_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -205,11 +314,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 = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -224,13 +334,21 @@
|
||||
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_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_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -247,9 +365,11 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 4.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -260,13 +380,18 @@
|
||||
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;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
@@ -277,13 +402,18 @@
|
||||
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;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0900"
|
||||
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>
|
||||
@@ -26,6 +26,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
@@ -36,6 +37,7 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
@@ -46,7 +48,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
@@ -64,7 +66,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
||||
@@ -1,14 +1,163 @@
|
||||
# Lightbox
|
||||
|
||||
[](https://travis-ci.org/hyperoslo/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||

|
||||
|
||||
[Demo](https://appetize.io/app/wfgwc2uvg82m9pzbt17p4rrgh4?device=iphone5s&scale=75&orientation=portrait&osVersion=9.3)
|
||||
|
||||
<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.
|
||||
- [x] Interactive transition animations.
|
||||
- [x] Powerful configuration.
|
||||
- [x] Demo project.
|
||||
|
||||
## 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 `sendAsynchronousRequest` method of
|
||||
`NSURLConnection`. But it's easy to change this behavior using **Lightbox**
|
||||
configuration.
|
||||
|
||||
```swift
|
||||
LightboxConfig.loadImage = {
|
||||
imageView, URL, completion in
|
||||
// Custom image loading
|
||||
}
|
||||
```
|
||||
|
||||
### Video
|
||||
|
||||
**Lightbox** has video support out of the box. Configure 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 = "ShowMore"
|
||||
```
|
||||
|
||||
## Installation
|
||||
@@ -20,10 +169,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,4 @@
|
||||
protocol LayoutConfigurable: class {
|
||||
|
||||
func configureLayout()
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
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 }
|
||||
|
||||
controller.headerView.transform = show
|
||||
? CGAffineTransform.identity
|
||||
: CGAffineTransform(translationX: 0, y: -200)
|
||||
|
||||
controller.footerView.transform = show
|
||||
? CGAffineTransform.identity
|
||||
: CGAffineTransform(translationX: 0, y: 250)
|
||||
|
||||
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: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||
return 0.25
|
||||
}
|
||||
|
||||
func animateTransition(using transitionContext: 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) -> UIViewControllerAnimatedTransitioning? {
|
||||
dismissing = true
|
||||
return self
|
||||
}
|
||||
|
||||
func animationController(forPresented presented: UIViewController,
|
||||
presenting: UIViewController,
|
||||
source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||
dismissing = false
|
||||
return self
|
||||
}
|
||||
|
||||
func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
|
||||
return interactive ? self : nil
|
||||
}
|
||||
|
||||
func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> 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 fabs(translation.x) < fabs(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,90 @@
|
||||
import UIKit
|
||||
import Hue
|
||||
import AVKit
|
||||
import AVFoundation
|
||||
|
||||
class LightboxConfig {
|
||||
public class LightboxConfig {
|
||||
/// Whether to show status bar while Lightbox is presented
|
||||
public static var hideStatusBar = true
|
||||
|
||||
var config = Config()
|
||||
/// Provide a closure to handle selected video
|
||||
public static var handleVideo: (_ from: UIViewController, _ videoURL: URL) -> Void = { from, videoURL in
|
||||
let videoController = AVPlayerViewController()
|
||||
videoController.player = AVPlayer(url: videoURL)
|
||||
|
||||
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)
|
||||
|
||||
NSURLConnection.sendAsynchronousRequest(imageRequest,
|
||||
queue: NSOperationQueue.mainQueue(),
|
||||
completionHandler: { response, data, error in
|
||||
if let data = data, image = UIImage(data: data) {
|
||||
imageView.image = image
|
||||
}
|
||||
|
||||
completion?(error: error)
|
||||
})
|
||||
from.present(videoController, animated: true) {
|
||||
videoController.player?.play()
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicator is used to show while image is being fetched
|
||||
public static var makeLoadingIndicator: () -> UIView = {
|
||||
return LoadingIndicator()
|
||||
}
|
||||
|
||||
public init() { }
|
||||
|
||||
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: [NSAttributedStringKey: 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: [NSAttributedStringKey: 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: [NSAttributedStringKey: 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: [NSAttributedStringKey: 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,426 @@
|
||||
import UIKit
|
||||
import Hue
|
||||
|
||||
public protocol LightboxControllerPageDelegate: class {
|
||||
|
||||
func lightboxControllerDidMoveToPage(controller: LightboxController, page: Int)
|
||||
func lightboxController(_ controller: LightboxController, didMoveToPage page: Int)
|
||||
}
|
||||
|
||||
public protocol LightboxControllerDismissalDelegate: class {
|
||||
|
||||
func lightboxControllerDidDismiss(controller: LightboxController)
|
||||
func lightboxControllerWillDismiss(_ controller: LightboxController)
|
||||
}
|
||||
|
||||
public class LightboxController: UIViewController {
|
||||
public protocol LightboxControllerTouchDelegate: class {
|
||||
|
||||
public weak var pageDelegate: LightboxControllerPageDelegate?
|
||||
public weak var dismissalDelegate: LightboxControllerDismissalDelegate?
|
||||
func lightboxController(_ controller: LightboxController, didTouch image: LightboxImage, at index: Int)
|
||||
}
|
||||
|
||||
lazy var transitionManager: LightboxTransition = { [unowned self] in
|
||||
let manager = LightboxTransition()
|
||||
manager.sourceViewController = self
|
||||
open class LightboxController: UIViewController {
|
||||
|
||||
return manager
|
||||
// MARK: - Internal views
|
||||
|
||||
lazy var scrollView: UIScrollView = { [unowned self] in
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.frame = self.screenBounds
|
||||
scrollView.isPagingEnabled = false
|
||||
scrollView.delegate = self
|
||||
scrollView.isUserInteractionEnabled = true
|
||||
scrollView.showsHorizontalScrollIndicator = false
|
||||
scrollView.decelerationRate = UIScrollViewDecelerationRateFast
|
||||
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
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
|
||||
lazy var overlayTapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
|
||||
let gesture = UITapGestureRecognizer()
|
||||
gesture.addTarget(self, action: #selector(overlayViewDidTap(_:)))
|
||||
|
||||
lazy var config: Config = {
|
||||
return LightboxConfig.sharedInstance.config
|
||||
return gesture
|
||||
}()
|
||||
|
||||
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
|
||||
}()
|
||||
|
||||
var pageLabelBottomConstant: CGFloat {
|
||||
return collectionSize.width < collectionSize.height ? -20 : -2
|
||||
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").alpha(0), UIColor(hex: "040404")]
|
||||
|
||||
view.addGradientLayer(colors)
|
||||
view.alpha = 0
|
||||
|
||||
return view
|
||||
}()
|
||||
|
||||
var screenBounds: CGRect {
|
||||
return UIApplication.shared.delegate?.window??.bounds ?? .zero
|
||||
}
|
||||
|
||||
var currentOrientation: UIDeviceOrientation?
|
||||
var beforeFaceOrientation: UIDeviceOrientation?
|
||||
// MARK: - Properties
|
||||
|
||||
var rotating = false
|
||||
|
||||
public private(set) var page = 0 {
|
||||
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)
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
open var images: [LightboxImage] {
|
||||
get {
|
||||
return pageViews.map { $0.image }
|
||||
}
|
||||
set(value) {
|
||||
configurePages(value)
|
||||
}
|
||||
}
|
||||
|
||||
open weak var pageDelegate: LightboxControllerPageDelegate?
|
||||
open weak var dismissalDelegate: LightboxControllerDismissalDelegate?
|
||||
open weak var imageTouchDelegate: LightboxControllerTouchDelegate?
|
||||
open internal(set) var presented = false
|
||||
open fileprivate(set) var seen = false
|
||||
|
||||
lazy var transitionManager: LightboxTransition = LightboxTransition()
|
||||
var pageViews = [PageView]()
|
||||
var statusBarHidden = false
|
||||
|
||||
fileprivate let 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)
|
||||
}
|
||||
statusBarHidden = UIApplication.shared.isStatusBarHidden
|
||||
|
||||
view.backgroundColor = UIColor.black
|
||||
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)
|
||||
currentPage = initialPage
|
||||
|
||||
if orientationsSupported.first == "UIInterfaceOrientationPortrait"
|
||||
&& orientationsSupported.count == 1 {
|
||||
NSNotificationCenter.defaultCenter().addObserver(
|
||||
self,
|
||||
selector: "deviceDidRotate",
|
||||
name: UIDeviceOrientationDidChangeNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
setupConstraints()
|
||||
page = 0
|
||||
goTo(currentPage, animated: false)
|
||||
}
|
||||
|
||||
public override func viewDidAppear(animated: Bool) {
|
||||
super.viewDidAppear(true)
|
||||
|
||||
if config.hideStatusBar {
|
||||
UIApplication.sharedApplication().setStatusBarHidden(true,
|
||||
withAnimation: .Fade)
|
||||
}
|
||||
|
||||
let orientationsSupported: [String] = NSBundle.mainBundle().objectForInfoDictionaryKey("UISupportedInterfaceOrientations") as! [String]
|
||||
|
||||
if orientationsSupported.first == "UIInterfaceOrientationPortrait"
|
||||
&& orientationsSupported.count == 1 {
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft
|
||||
|| UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
|
||||
deviceDidRotate()
|
||||
}
|
||||
open override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
if !presented {
|
||||
presented = true
|
||||
configureLayout()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
NSNotificationCenter.defaultCenter().removeObserver(self)
|
||||
open override var prefersStatusBarHidden: Bool {
|
||||
return LightboxConfig.hideStatusBar
|
||||
}
|
||||
|
||||
// MARK: - Handle rotation
|
||||
// MARK: - 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
|
||||
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
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
|
||||
}
|
||||
coordinator.animate(alongsideTransition: { _ in
|
||||
self.configureLayout(size)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Autolayout
|
||||
// MARK: - Configuration
|
||||
|
||||
func setupConstraints() {
|
||||
let attributes: [NSLayoutAttribute] = [.CenterX, .CenterY]
|
||||
func configurePages(_ images: [LightboxImage]) {
|
||||
pageViews.forEach { $0.removeFromSuperview() }
|
||||
pageViews = []
|
||||
|
||||
for attribute in attributes {
|
||||
view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: attribute,
|
||||
relatedBy: .Equal, toItem: self.view, attribute: attribute, multiplier: 1, constant: 0))
|
||||
for image in images {
|
||||
let pageView = PageView(image: image)
|
||||
pageView.pageViewDelegate = self
|
||||
|
||||
scrollView.addSubview(pageView)
|
||||
pageViews.append(pageView)
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
configureLayout()
|
||||
}
|
||||
|
||||
// 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)
|
||||
} else {
|
||||
if array.count != 0 {
|
||||
array.removeObjectAtIndex(index)
|
||||
}
|
||||
images = array
|
||||
dismissalDelegate?.lightboxControllerDidDismiss(self)
|
||||
}
|
||||
open func configureLayout(_ size: CGSize = UIApplication.shared.delegate?.window??.bounds.size ?? .zero) {
|
||||
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)
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
let bounds = scrollView.bounds
|
||||
let headerViewHeight = headerView.closeButton.frame.height > headerView.deleteButton.frame.height
|
||||
? headerView.closeButton.frame.height
|
||||
: headerView.deleteButton.frame.height
|
||||
|
||||
headerView.frame = CGRect(x: 0, y: 16, width: bounds.width, height: headerViewHeight)
|
||||
footerView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: 70)
|
||||
|
||||
[headerView, footerView].forEach { ($0 as AnyObject).configureLayout() }
|
||||
|
||||
footerView.frame.origin.y = bounds.height - footerView.frame.height
|
||||
|
||||
overlayView.frame = scrollView.frame
|
||||
overlayView.resizeGradientLayer()
|
||||
}
|
||||
|
||||
fileprivate func loadDynamicBackground(_ image: UIImage) {
|
||||
backgroundView.image = image
|
||||
backgroundView.layer.add(CATransition(), forKey: kCATransitionFade)
|
||||
}
|
||||
|
||||
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: - 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 / screenBounds.width)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
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.pageViews.remove(at: prevIndex).removeFromSuperview()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
|
||||
self.configureLayout()
|
||||
self.currentPage = Int(self.scrollView.contentOffset.x / self.screenBounds.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) {
|
||||
footerView.frame.origin.y = screenBounds.height - footerView.frame.height
|
||||
|
||||
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,40 @@
|
||||
import UIKit
|
||||
import Imaginary
|
||||
|
||||
open class LightboxImage {
|
||||
|
||||
open fileprivate(set) var image: UIImage?
|
||||
open fileprivate(set) var imageURL: URL?
|
||||
open fileprivate(set) var videoURL: URL?
|
||||
open var text: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
open func addImageTo(_ imageView: UIImageView, completion: ((_ image: UIImage?) -> Void)? = nil) {
|
||||
if let image = image {
|
||||
imageView.image = image
|
||||
completion?(image)
|
||||
} else if let imageURL = imageURL {
|
||||
imageView.setImage(url: imageURL, placeholder: nil, completion: { result in
|
||||
switch result {
|
||||
case .value(let image):
|
||||
completion?(image)
|
||||
case .error:
|
||||
completion?(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,114 @@
|
||||
import UIKit
|
||||
|
||||
public protocol FooterViewDelegate: class {
|
||||
|
||||
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").alpha(0.1), UIColor(hex: "040404")]
|
||||
open weak var delegate: 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)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
fileprivate func resetFrames() {
|
||||
frame.size.height = infoLabel.frame.height + 40 + 0.5
|
||||
|
||||
pageLabel.frame.origin = CGPoint(
|
||||
x: (frame.width - pageLabel.frame.width) / 2,
|
||||
y: frame.height - pageLabel.frame.height - 2)
|
||||
|
||||
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) {
|
||||
resetFrames()
|
||||
_ = (expanded || infoLabel.fullText.isEmpty) ? removeGradientLayer() : addGradientLayer(gradientColors)
|
||||
delegate?.footerView(self, didExpand: expanded)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import UIKit
|
||||
|
||||
protocol HeaderViewDelegate: class {
|
||||
func headerView(_ headerView: HeaderView, didPressDeleteButton deleteButton: UIButton)
|
||||
func headerView(_ headerView: HeaderView, didPressCloseButton closeButton: UIButton)
|
||||
}
|
||||
|
||||
open class HeaderView: UIView {
|
||||
|
||||
var centerTextStyle: NSMutableParagraphStyle = {
|
||||
var style = NSMutableParagraphStyle()
|
||||
style.alignment = .center
|
||||
return style
|
||||
}()
|
||||
|
||||
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: UIControlState())
|
||||
|
||||
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: UIControlState())
|
||||
}
|
||||
|
||||
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: UIControlState())
|
||||
}
|
||||
|
||||
button.isHidden = !LightboxConfig.DeleteButton.enabled
|
||||
|
||||
return button
|
||||
}()
|
||||
|
||||
weak var delegate: 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() {
|
||||
closeButton.frame.origin = CGPoint(
|
||||
x: bounds.width - closeButton.frame.width - 17, y: 0)
|
||||
|
||||
deleteButton.frame.origin = CGPoint(x: 17, y: 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import UIKit
|
||||
|
||||
public protocol InfoLabelDelegate: class {
|
||||
|
||||
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: 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)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var truncatedText: String {
|
||||
var truncatedText = fullText
|
||||
|
||||
guard numberOfLines(fullText) > numberOfVisibleLines else {
|
||||
return truncatedText
|
||||
}
|
||||
|
||||
while numberOfLines(truncatedText) > numberOfVisibleLines * 2 {
|
||||
truncatedText = String(truncatedText.characters.prefix(truncatedText.characters.count / 2))
|
||||
}
|
||||
|
||||
truncatedText += ellipsis
|
||||
|
||||
let start = truncatedText.characters.index(truncatedText.endIndex, offsetBy: -(ellipsis.characters.count + 1))
|
||||
let end = truncatedText.characters.index(truncatedText.endIndex, offsetBy: -ellipsis.characters.count)
|
||||
var range = start..<end
|
||||
|
||||
while numberOfLines(truncatedText) > numberOfVisibleLines {
|
||||
truncatedText.removeSubrange(range)
|
||||
range = truncatedText.index(range.lowerBound, offsetBy: -1)..<truncatedText.index(range.upperBound, offsetBy: -1)
|
||||
}
|
||||
|
||||
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 attributedString = NSMutableAttributedString(string: string,
|
||||
attributes: LightboxConfig.InfoLabel.textAttributes)
|
||||
|
||||
if string.range(of: ellipsis) != nil {
|
||||
let range = (string as NSString).range(of: ellipsis)
|
||||
attributedString.addAttribute(NSAttributedStringKey.foregroundColor,
|
||||
value: LightboxConfig.InfoLabel.ellipsisColor, range: range)
|
||||
}
|
||||
|
||||
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: [NSAttributedStringKey.font: font],
|
||||
context: nil).height
|
||||
}
|
||||
|
||||
fileprivate func numberOfLines(_ string: String) -> Int {
|
||||
let lineHeight = "A".size(withAttributes: [NSAttributedStringKey.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.activityIndicatorViewStyle = .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,213 @@
|
||||
import UIKit
|
||||
|
||||
protocol PageViewDelegate: class {
|
||||
|
||||
func pageViewDidZoom(_ pageView: PageView)
|
||||
func remoteImageDidLoad(_ image: UIImage?, imageView: UIImageView)
|
||||
func pageView(_ pageView: PageView, didTouchPlayButton videoURL: URL)
|
||||
func pageViewDidTouch(_ 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)
|
||||
button.setBackgroundImage(AssetManager.image("lightbox_play"), for: UIControlState())
|
||||
button.addTarget(self, action: #selector(playButtonTouched(_:)), for: .touchUpInside)
|
||||
|
||||
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: PageViewDelegate?
|
||||
|
||||
var hasZoomed: Bool {
|
||||
return zoomScale != 1.0
|
||||
}
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
init(image: LightboxImage) {
|
||||
self.image = image
|
||||
super.init(frame: CGRect.zero)
|
||||
|
||||
configure()
|
||||
|
||||
loadingIndicator.alpha = 1
|
||||
self.image.addImageTo(imageView) { [weak self] image in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.isUserInteractionEnabled = true
|
||||
strongSelf.configureImageView()
|
||||
strongSelf.pageViewDelegate?.remoteImageDidLoad(image, imageView: strongSelf.imageView)
|
||||
|
||||
UIView.animate(withDuration: 0.4) {
|
||||
strongSelf.loadingIndicator.alpha = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configure() {
|
||||
addSubview(imageView)
|
||||
|
||||
if image.videoURL != nil {
|
||||
addSubview(playButton)
|
||||
}
|
||||
|
||||
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: - 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)
|
||||
}
|
||||
|
||||
@objc func viewTapped(_ recognizer: UITapGestureRecognizer) {
|
||||
pageViewDelegate?.pageViewDidTouch(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,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
carthage bootstrap
|
||||
cp Cartfile.resolved Carthage
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! cmp -s Cartfile.resolved Carthage/Cartfile.resolved; then
|
||||
bin/bootstrap
|
||||
fi
|
||||
@@ -0,0 +1,13 @@
|
||||
machine:
|
||||
xcode:
|
||||
version: "9.0"
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- bin/bootstrap-if-needed
|
||||
cache_directories:
|
||||
- "Carthage"
|
||||
|
||||
test:
|
||||
override:
|
||||
- set -o pipefail && xcodebuild -project Lightbox.xcodeproj -scheme "Lightbox-iOS" -sdk iphonesimulator clean build
|
||||