Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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
|
||||
|
||||
@@ -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 +1,13 @@
|
||||
osx_image: xcode8
|
||||
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
|
||||
before_install:
|
||||
- brew update
|
||||
- if brew outdated | grep -qx carthage; then brew upgrade carthage; fi
|
||||
- travis_wait 35 carthage bootstrap --platform iOS
|
||||
|
||||
# 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
|
||||
script:
|
||||
- xcodebuild clean build -project Lightbox.xcodeproj -scheme "Lightbox-iOS" -sdk iphonesimulator
|
||||
|
||||
after_success: slather
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
github "hyperoslo/Hue" "f82ffc7dd55d093d3acc6ce5b3d36ca259091fdd"
|
||||
@@ -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,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,374 @@
|
||||
// !$*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 */; };
|
||||
7193005DD460E198432D8F06 /* Pods_DemoLightbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0977FB7C36F459186E043753 /* Pods_DemoLightbox.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0977FB7C36F459186E043753 /* Pods_DemoLightbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DemoLightbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
5B9CE7F93A06A0459BF452D8 /* 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>"; };
|
||||
C37FDBF69CDC2A95E0E105A7 /* 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; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
29B4A4261C43A4320060ED52 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7193005DD460E198432D8F06 /* Pods_DemoLightbox.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
29B4A4201C43A4320060ED52 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
29B4A42B1C43A4320060ED52 /* DemoLightbox */,
|
||||
29B4A42A1C43A4320060ED52 /* Products */,
|
||||
B1B2B3DADEEC7FD14D4A9FF8 /* Frameworks */,
|
||||
80F119CA8153119A6934480D /* 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>";
|
||||
};
|
||||
80F119CA8153119A6934480D /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C37FDBF69CDC2A95E0E105A7 /* Pods-DemoLightbox.debug.xcconfig */,
|
||||
5B9CE7F93A06A0459BF452D8 /* Pods-DemoLightbox.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1B2B3DADEEC7FD14D4A9FF8 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAE713340DA5D2F2EF13EA8D /* Pods.framework */,
|
||||
0977FB7C36F459186E043753 /* 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 = (
|
||||
2CF277B8821F7CE6E2730C9C /* [CP] Check Pods Manifest.lock */,
|
||||
29B4A4251C43A4320060ED52 /* Sources */,
|
||||
29B4A4261C43A4320060ED52 /* Frameworks */,
|
||||
29B4A4271C43A4320060ED52 /* Resources */,
|
||||
831C9CD301B03A92F6320AEE /* [CP] Embed Pods Frameworks */,
|
||||
3BCDC27EE322C46C109D231B /* [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 = 0800;
|
||||
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 */
|
||||
2CF277B8821F7CE6E2730C9C /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] 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 # 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";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3BCDC27EE322C46C109D231B /* [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;
|
||||
};
|
||||
831C9CD301B03A92F6320AEE /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
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_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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
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 = 3.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_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_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
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 = 3.0;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
29B4A43C1C43A4320060ED52 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = C37FDBF69CDC2A95E0E105A7 /* 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)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
29B4A43D1C43A4320060ED52 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 5B9CE7F93A06A0459BF452D8 /* 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)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
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
|
||||
|
||||
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: "",
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ use_frameworks!
|
||||
inhibit_all_warnings!
|
||||
|
||||
pod 'Lightbox', path: '../../'
|
||||
target 'DemoLightbox'
|
||||
@@ -0,0 +1,19 @@
|
||||
PODS:
|
||||
- Hue (2.0.1)
|
||||
- Lightbox (1.0.0):
|
||||
- Hue
|
||||
|
||||
DEPENDENCIES:
|
||||
- Lightbox (from `../../`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Lightbox:
|
||||
:path: "../../"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Hue: 354caec055fdc9d38b5ef33ca2e7224721843baf
|
||||
Lightbox: eb4196a846a883c4c2a52209b29b283d1bc706e1
|
||||
|
||||
PODFILE CHECKSUM: cd88b68c201e5c39cef62070056649eaee91c71b
|
||||
|
||||
COCOAPODS: 1.1.1
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -1,7 +1,7 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "Lightbox"
|
||||
s.summary = "A short description of Lightbox."
|
||||
s.version = "0.1.0"
|
||||
s.version = "1.0.0"
|
||||
s.homepage = "https://github.com/hyperoslo/Lightbox"
|
||||
s.license = 'MIT'
|
||||
s.author = { "Hyper Interaktiv AS" => "ios@hyper.no" }
|
||||
@@ -10,6 +10,10 @@ Pod::Spec.new do |s|
|
||||
s.platform = :ios, '8.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'
|
||||
|
||||
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3.0' }
|
||||
end
|
||||
|
||||
@@ -7,25 +7,39 @@
|
||||
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 */; };
|
||||
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>"; };
|
||||
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,15 +47,26 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
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>";
|
||||
};
|
||||
D523B09F1C43AA2A001AD1EC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D22006721DFB4D9700E92898 /* Resources */,
|
||||
D54DFCC01C5AAAF100ADEA0E /* Hue.framework */,
|
||||
D523B0B41C43AA8A001AD1EC /* Source */,
|
||||
D523B0AB1C43AA2A001AD1EC /* Lightbox */,
|
||||
D523B0AA1C43AA2A001AD1EC /* Products */,
|
||||
@@ -67,17 +92,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 +145,7 @@
|
||||
D523B0A51C43AA2A001AD1EC /* Frameworks */,
|
||||
D523B0A61C43AA2A001AD1EC /* Headers */,
|
||||
D523B0A71C43AA2A001AD1EC /* Resources */,
|
||||
D54DFCC41C5AAAF600ADEA0E /* Copy frameworks with Carthage */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -115,11 +162,12 @@
|
||||
D523B0A01C43AA2A001AD1EC /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0720;
|
||||
LastUpgradeCheck = 0800;
|
||||
ORGANIZATIONNAME = "Hyper Interaktiv AS";
|
||||
TargetAttributes = {
|
||||
D523B0A81C43AA2A001AD1EC = {
|
||||
CreatedOnToolsVersion = 7.2;
|
||||
LastSwiftMigration = 0800;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -145,23 +193,47 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
D54DFCC41C5AAAF600ADEA0E /* Copy frameworks with Carthage */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/Carthage/Build/iOS/Hue.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;
|
||||
};
|
||||
@@ -181,8 +253,10 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -210,6 +284,7 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@@ -229,8 +304,10 @@
|
||||
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_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
@@ -250,6 +327,8 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_VERSION = 3.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -260,10 +339,15 @@
|
||||
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;
|
||||
@@ -271,16 +355,22 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D523B0B31C43AA2A001AD1EC /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Carthage/Build/iOS",
|
||||
);
|
||||
INFOPLIST_FILE = Lightbox/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
@@ -288,6 +378,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = no.hyper.Lightbox;
|
||||
PRODUCT_NAME = Lightbox;
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0720"
|
||||
LastUpgradeVersion = "0800"
|
||||
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>
|
||||
@@ -46,7 +46,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
@@ -64,7 +64,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
|
||||
BuildableName = "Lightbox-iOS.framework"
|
||||
BuildableName = "Lightbox.framework"
|
||||
BlueprintName = "Lightbox-iOS"
|
||||
ReferencedContainer = "container:Lightbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
||||
@@ -1,14 +1,162 @@
|
||||
# Lightbox
|
||||
|
||||
[](https://travis-ci.org/hyperoslo/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](https://travis-ci.org/hyperoslo/Lightbox)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||

|
||||
|
||||
<img src="https://raw.githubusercontent.com/hyperoslo/Lightbox/master/Images/Icon.png" alt="Lightbox Icon" align="right" />
|
||||
|
||||
**Lightbox** is a convenient and easy to use image viewer for your iOS app,
|
||||
packed with all the features you expect:
|
||||
|
||||
- [x] Paginated image slideshow.
|
||||
- [x] Video support.
|
||||
- [x] Double-tap to zoom.
|
||||
- [x] Image caption.
|
||||
- [x] Dynamic background.
|
||||
- [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 +168,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,9 @@
|
||||
import UIKit
|
||||
|
||||
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,179 @@
|
||||
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
|
||||
|
||||
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.viewController(forKey: UITransitionContextViewControllerKey.from)?.view,
|
||||
let toView = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)?.view
|
||||
else { return }
|
||||
|
||||
let firstView = dismissing ? toView : fromView
|
||||
let secondView = dismissing ? fromView : toView
|
||||
|
||||
if !dismissing { transition(false) }
|
||||
|
||||
container.addSubview(firstView)
|
||||
container.addSubview(secondView)
|
||||
|
||||
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 {
|
||||
|
||||
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,105 @@
|
||||
import UIKit
|
||||
import Hue
|
||||
import AVKit
|
||||
import AVFoundation
|
||||
|
||||
class LightboxConfig {
|
||||
open class LightboxConfig {
|
||||
|
||||
var config = Config()
|
||||
public typealias LoadImageCompletion = (_ error: NSError?, _ image: UIImage?) -> Void
|
||||
|
||||
static let sharedInstance = LightboxConfig()
|
||||
}
|
||||
open static var hideStatusBar = true
|
||||
|
||||
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 = {
|
||||
open static var loadImage: (_ imageView: UIImageView, _ URL: URL, _ completion: LoadImageCompletion?) -> Void = {
|
||||
imageView, URL, completion in
|
||||
let imageRequest: NSURLRequest = NSURLRequest(URL: URL)
|
||||
|
||||
let imageRequest: URLRequest = URLRequest(url: URL)
|
||||
|
||||
NSURLConnection.sendAsynchronousRequest(imageRequest,
|
||||
queue: NSOperationQueue.mainQueue(),
|
||||
queue: OperationQueue.main,
|
||||
completionHandler: { response, data, error in
|
||||
if let data = data, image = UIImage(data: data) {
|
||||
if let data = data, let image = UIImage(data: data) {
|
||||
imageView.image = image
|
||||
}
|
||||
|
||||
completion?(error: error)
|
||||
completion?(error as NSError?, imageView.image)
|
||||
})
|
||||
}
|
||||
|
||||
public init() { }
|
||||
|
||||
open static var handleVideo: (_ from: UIViewController, _ videoURL: URL) -> Void = { from, videoURL in
|
||||
let videoController = AVPlayerViewController()
|
||||
videoController.player = AVPlayer(url: videoURL)
|
||||
|
||||
from.present(videoController, animated: true) {
|
||||
videoController.player?.play()
|
||||
}
|
||||
}
|
||||
|
||||
public struct PageIndicator {
|
||||
public var enabled = true
|
||||
public var textAttributes = [
|
||||
NSFontAttributeName: UIFont.systemFontOfSize(18),
|
||||
NSForegroundColorAttributeName: UIColor.lightGrayColor(),
|
||||
public static var enabled = true
|
||||
public static var separatorColor = UIColor(hex: "3D4757")
|
||||
|
||||
public static var textAttributes = [
|
||||
NSFontAttributeName: UIFont.systemFont(ofSize: 12),
|
||||
NSForegroundColorAttributeName: UIColor(hex: "899AB8"),
|
||||
NSParagraphStyleAttributeName: {
|
||||
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(),
|
||||
public static var enabled = true
|
||||
public static var size = CGSize(width: 60, height: 25)
|
||||
public static var text = NSLocalizedString("Close", comment: "")
|
||||
public static var image: UIImage?
|
||||
|
||||
public static var textAttributes = [
|
||||
NSFontAttributeName: UIFont.boldSystemFont(ofSize: 16),
|
||||
NSForegroundColorAttributeName: UIColor.white,
|
||||
NSParagraphStyleAttributeName: {
|
||||
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),
|
||||
public static var enabled = false
|
||||
public static var size = CGSize(width: 70, height: 25)
|
||||
public static var text = NSLocalizedString("Delete", comment: "")
|
||||
public static var image: UIImage?
|
||||
|
||||
public static var textAttributes = [
|
||||
NSFontAttributeName: UIFont.boldSystemFont(ofSize: 16),
|
||||
NSForegroundColorAttributeName: UIColor(hex: "FA2F5B"),
|
||||
NSParagraphStyleAttributeName: {
|
||||
var style = NSMutableParagraphStyle()
|
||||
style.alignment = .Center
|
||||
style.alignment = .center
|
||||
return style
|
||||
}()
|
||||
]
|
||||
public var size = CGSize(width: 70, height: 25)
|
||||
public var text = NSLocalizedString("Delete", comment: "")
|
||||
public var image: UIImage?
|
||||
}
|
||||
|
||||
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 = [
|
||||
NSFontAttributeName: UIFont.systemFont(ofSize: 12),
|
||||
NSForegroundColorAttributeName: UIColor(hex: "DBDBDB")
|
||||
]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
public struct LoadingIndicator {
|
||||
public static var configure: ((UIActivityIndicatorView) -> Void)? = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,599 +1,417 @@
|
||||
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 {
|
||||
open class LightboxController: UIViewController {
|
||||
|
||||
public weak var pageDelegate: LightboxControllerPageDelegate?
|
||||
public weak var dismissalDelegate: LightboxControllerDismissalDelegate?
|
||||
// MARK: - Internal views
|
||||
|
||||
lazy var transitionManager: LightboxTransition = { [unowned self] in
|
||||
let manager = LightboxTransition()
|
||||
manager.sourceViewController = self
|
||||
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 manager
|
||||
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 UIScreen.main.bounds
|
||||
}
|
||||
|
||||
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 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)
|
||||
open override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if config.hideStatusBar {
|
||||
UIApplication.sharedApplication().setStatusBarHidden(true,
|
||||
withAnimation: .Fade)
|
||||
if LightboxConfig.hideStatusBar {
|
||||
UIApplication.shared.setStatusBarHidden(true, with: .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()
|
||||
}
|
||||
if !presented {
|
||||
presented = true
|
||||
configureLayout()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
NSNotificationCenter.defaultCenter().removeObserver(self)
|
||||
}
|
||||
open override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
// MARK: - Handle rotation
|
||||
|
||||
func deviceDidRotate() {
|
||||
let orientation = UIDevice.currentDevice().orientation
|
||||
let orientations: [UIDeviceOrientation] = [.FaceUp, .FaceDown]
|
||||
let currentIsFace = orientations.filter({ $0 == self.currentOrientation }).first != nil
|
||||
let nextIsFace = orientations.filter({ $0 == orientation }).first != nil
|
||||
|
||||
if nextIsFace {
|
||||
beforeFaceOrientation = currentOrientation
|
||||
}
|
||||
currentOrientation = orientation
|
||||
|
||||
if (!currentIsFace && nextIsFace) ||
|
||||
(currentIsFace && !nextIsFace && orientation == beforeFaceOrientation) {
|
||||
return
|
||||
}
|
||||
|
||||
rotating = true
|
||||
var transform = CGAffineTransformIdentity
|
||||
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft {
|
||||
transform = moveCollectionView(true)
|
||||
moveViews(true)
|
||||
transitionManager.panGestureRecognizer.enabled = false
|
||||
} else if UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight {
|
||||
transform = moveCollectionView(false)
|
||||
moveViews(false)
|
||||
transitionManager.panGestureRecognizer.enabled = false
|
||||
} else if UIDevice.currentDevice().orientation == UIDeviceOrientation.Portrait {
|
||||
for constraint in [collectionViewHeight!, collectionViewWidth!,
|
||||
closeButtonTop!, closeButtonRight!,
|
||||
pageLabelAlternative!, pageLabelBottom!] {
|
||||
view.removeConstraint(constraint)
|
||||
}
|
||||
|
||||
standardCollectionViewConstraints()
|
||||
standardCloseButtonConstraints()
|
||||
standardPageLabelConstraints()
|
||||
collectionView.alpha = 0
|
||||
|
||||
transitionManager.panGestureRecognizer.enabled = true
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation != UIDeviceOrientation.PortraitUpsideDown {
|
||||
UIView.animateWithDuration(0.1, animations: { [unowned self] in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.alpha = 0 }
|
||||
}, completion: { finished in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.transform = transform }
|
||||
let indexPath = NSIndexPath(forItem: self.page, inSection: 0)
|
||||
self.collectionView.scrollToItemAtIndexPath(indexPath,
|
||||
atScrollPosition: UICollectionViewScrollPosition(), animated: false)
|
||||
UIView.animateWithDuration(0.3, animations: { [unowned self] in
|
||||
for subview in [self.collectionView, self.closeButton, self.pageLabel] { subview.alpha = 1 }
|
||||
self.rotating = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation != UIDeviceOrientation.PortraitUpsideDown {
|
||||
UIView.animateWithDuration(0.3, animations: { [unowned self] in
|
||||
self.collectionView.transform = transform
|
||||
self.closeButton.transform = transform
|
||||
self.pageLabel.transform = transform
|
||||
}, completion: { _ in
|
||||
let indexPath = NSIndexPath(forItem: self.page, inSection: 0)
|
||||
self.collectionView.scrollToItemAtIndexPath(indexPath,
|
||||
atScrollPosition: UICollectionViewScrollPosition(), animated: true)
|
||||
self.rotating = false
|
||||
})
|
||||
}
|
||||
|
||||
if UIDevice.currentDevice().orientation == UIDeviceOrientation.PortraitUpsideDown {
|
||||
rotating = false
|
||||
if LightboxConfig.hideStatusBar {
|
||||
UIApplication.shared.setStatusBarHidden(statusBarHidden, with: .fade)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Autolayout
|
||||
// MARK: - Rotation
|
||||
|
||||
func setupConstraints() {
|
||||
let attributes: [NSLayoutAttribute] = [.CenterX, .CenterY]
|
||||
override open func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
for attribute in attributes {
|
||||
view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: attribute,
|
||||
relatedBy: .Equal, toItem: self.view, attribute: attribute, multiplier: 1, constant: 0))
|
||||
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
|
||||
self.configureLayout(size)
|
||||
}, completion: nil)
|
||||
}
|
||||
|
||||
// MARK: - Configuration
|
||||
|
||||
func configurePages(_ images: [LightboxImage]) {
|
||||
pageViews.forEach { $0.removeFromSuperview() }
|
||||
pageViews = []
|
||||
|
||||
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)
|
||||
|
||||
scrollView.setContentOffset(offset, animated: animated)
|
||||
}
|
||||
|
||||
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)
|
||||
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 = UIScreen.main.bounds.size) {
|
||||
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?) {
|
||||
guard let image = image , dynamicBackground 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 }
|
||||
|
||||
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,34 @@
|
||||
import UIKit
|
||||
|
||||
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 {
|
||||
LightboxConfig.loadImage(imageView, imageURL) { error, image in
|
||||
completion?(image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
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 ? removeGradientLayer() : addGradientLayer(gradientColors)
|
||||
delegate?.footerView(self, didExpand: expanded)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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.frame.size = LightboxConfig.CloseButton.size
|
||||
button.setAttributedTitle(title, for: UIControlState())
|
||||
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.frame.size = LightboxConfig.DeleteButton.size
|
||||
button.setAttributedTitle(title, for: .normal)
|
||||
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
|
||||
|
||||
func deleteButtonDidPress(_ button: UIButton) {
|
||||
delegate?.headerView(self, didPressDeleteButton: button)
|
||||
}
|
||||
|
||||
func closeButtonDidPress(_ button: UIButton) {
|
||||
delegate?.headerView(self, didPressCloseButton: button)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension HeaderView: LayoutConfigurable {
|
||||
|
||||
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,142 @@
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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(NSForegroundColorAttributeName,
|
||||
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: [NSFontAttributeName : font],
|
||||
context: nil).height
|
||||
}
|
||||
|
||||
fileprivate func numberOfLines(_ string: String) -> Int {
|
||||
let lineHeight = "A".size(attributes: [NSFontAttributeName: font]).height
|
||||
let totalHeight = heightForString(string)
|
||||
|
||||
return Int(totalHeight / lineHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension InfoLabel: LayoutConfigurable {
|
||||
|
||||
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,216 @@
|
||||
import UIKit
|
||||
|
||||
protocol PageViewDelegate: class {
|
||||
|
||||
func pageViewDidZoom(_ pageView: PageView)
|
||||
func remoteImageDidLoad(_ image: UIImage?)
|
||||
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 activityIndicator: LoadingIndicator = LoadingIndicator()
|
||||
|
||||
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()
|
||||
|
||||
activityIndicator.alpha = 1
|
||||
self.image.addImageTo(imageView) { image in
|
||||
self.isUserInteractionEnabled = true
|
||||
self.configureImageView()
|
||||
self.pageViewDelegate?.remoteImageDidLoad(image)
|
||||
|
||||
UIView.animate(withDuration: 0.4) {
|
||||
self.activityIndicator.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(activityIndicator)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func viewTapped(_ recognizer: UITapGestureRecognizer) {
|
||||
pageViewDelegate?.pageViewDidTouch(self)
|
||||
}
|
||||
|
||||
// MARK: - Layout
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
activityIndicator.center = imageView.center
|
||||
playButton.center = imageView.center
|
||||
}
|
||||
|
||||
func configureImageView() {
|
||||
guard let image = imageView.image else { 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
|
||||
|
||||
func playButtonTouched(_ button: UIButton) {
|
||||
guard let videoURL = image.videoURL else { return }
|
||||
|
||||
pageViewDelegate?.pageView(self, didTouchPlayButton: videoURL as URL)
|
||||
}
|
||||
|
||||
// MARK: - Controls
|
||||
|
||||
func makeActivityIndicator() -> UIActivityIndicatorView {
|
||||
let view = UIActivityIndicatorView(activityIndicatorStyle: .white)
|
||||
LightboxConfig.LoadingIndicator.configure?(view)
|
||||
view.startAnimating()
|
||||
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LayoutConfigurable
|
||||
|
||||
extension PageView: LayoutConfigurable {
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||