38 Commits

Author SHA1 Message Date
Khoa Pham 90fe59ffc6 Bump to 2.1.1 2017-11-21 14:24:34 +01:00
Khoa Pham 422b23b4c2 Update README.md 2017-11-21 10:41:26 +01:00
Khoa Pham 08a4c284f4 Merge pull request #156 from andreyrd/fix/no-window-bounds
Use view.bounds instead of UIApplicationDelegate.window.bounds
2017-11-20 21:59:07 +01:00
Khoa Pham b775f12fe5 Merge pull request #157 from hyperoslo/fix/truncating
Add test
2017-11-20 15:47:17 +01:00
Khoa Pham 9f75ddffdf Use just 1 test 2017-11-20 14:53:21 +01:00
Khoa Pham 225fac033a Remove fileprivate in order to test 2017-11-20 14:49:26 +01:00
Khoa Pham fe3229278d Add test 2017-11-20 14:48:57 +01:00
Khoa Pham de0d273258 Add quotes 2017-11-20 14:26:01 +01:00
Khoa Pham d1231f19ff Fix swiftlint warnings 2017-11-20 14:25:33 +01:00
Khoa Pham 20f927f338 Need to greater than startIndex 2017-11-20 14:22:40 +01:00
Andrey Radchishin 8144d96129 Use view.bounds instead of UIApplicationDelegate.window.bounds 2017-11-17 10:11:20 -08:00
Khoa Pham 0d9e3ef7ab Bump to 2.1.0 2017-11-17 16:21:51 +01:00
Khoa Pham 888067cae0 Merge pull request #155 from hyperoslo/fix/image_download
Allow custom download
2017-11-17 16:21:18 +01:00
Khoa Pham 0180ca76c7 Update README 2017-11-17 15:16:20 +01:00
Khoa Pham 951b95b0fb Update README 2017-11-17 15:08:28 +01:00
Khoa Pham 1803722b6a Expose LightboxConfig.loadImage. Use Imaginary by default 2017-11-17 15:04:31 +01:00
Khoa Pham 4f5ee72f97 Merge pull request #154 from hyperoslo/fix/layout
Update layout for iPhone X
2017-11-17 14:45:57 +01:00
Khoa Pham 9e17ebb58f Update README with 2 screenshots 2017-11-17 14:37:59 +01:00
Khoa Pham 28acb5a8d6 Layout HeaderView 2017-11-17 14:30:47 +01:00
Khoa Pham 5c6fa430e0 Remove HeaderView animation when dismissasl 2017-11-17 14:27:27 +01:00
Khoa Pham 5108f3651f Remove centerTextStyle 2017-11-17 14:26:59 +01:00
Khoa Pham 05ec840ffd Fix indentation 2017-11-17 14:20:20 +01:00
Khoa Pham 5189d880c4 Should not animate FooterView upon dismissal animation 2017-11-17 14:17:02 +01:00
Khoa Pham 308051a898 Remove unused isUserInteractionEnabled 2017-11-17 14:04:00 +01:00
Khoa Pham 56481d5f8a Update description text 2017-11-17 13:55:48 +01:00
Khoa Pham 3882befd7b Layout FooterView in viewDidLayoutSubviews 2017-11-17 13:52:50 +01:00
Khoa Pham cf88115eb6 Use view bounds instead of UIScreen 2017-11-17 13:31:56 +01:00
Khoa Pham 663bafce52 Use bottomPadding for pageLabel 2017-11-17 13:19:45 +01:00
Khoa Pham 6911104a98 Use topPadding for HeaderView buttons 2017-11-17 12:51:26 +01:00
Vadym Markov 369252ee7c Merge pull request #152 from andreyrd/string-characters-deprecated
Use String directly instead of String.characters
2017-11-13 20:08:00 +01:00
Andrey Radchishin 3322107688 Use String directly instead of String.characters 2017-11-13 09:02:46 -08:00
Khoa Pham 7731adc79c Update README.md 2017-11-06 12:15:10 +01:00
Khoa Pham 5c48b2482a Bump to 2.0.1 2017-11-02 13:03:21 +01:00
Khoa Pham 83416d6870 Merge pull request #147 from hyperoslo/fix/carthage
Update Carthage
2017-11-02 13:02:44 +01:00
Khoa Pham 4f9540b4df Update Cartfile 2017-11-02 12:57:58 +01:00
Khoa Pham 304c3e61f6 Just run carthage update 2017-11-02 12:51:28 +01:00
Khoa Pham 026153f502 Merge pull request #143 from bchrobot/fix-index-error
Refactored truncatedText getter to prevent occasional EXC_BREAKPOINT crash
2017-10-15 18:20:15 +02:00
Benjamin Chrobot 6e53ee01c7 Refactored truncatedText getter to prevent occassional 'EXC_BREAKPOINT: cannot decrement invalid index' crash. 2017-10-13 13:49:51 -04:00
20 changed files with 317 additions and 116 deletions
+2 -3
View File
@@ -1,4 +1,3 @@
github "hyperoslo/Cache" "4.0.2"
github "hyperoslo/Cache" "4.1.2"
github "hyperoslo/Hue" "3.0.0"
github "hyperoslo/Imaginary" "3.0.0"
github "onmyway133/SwiftHash" "2.0.1"
github "hyperoslo/Imaginary" "3.0.2"
@@ -13,11 +13,11 @@ class ViewController: UIViewController {
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)
@@ -30,16 +30,16 @@ class ViewController: UIViewController {
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"
text: "Photography is the science, art, application and practice of creating durable images by recording light or other electromagnetic radiation, either electronically by means of an image sensor, or chemically by means of a light-sensitive material such as photographic film"
),
LightboxImage(
image: UIImage(named: "photo2")!,
text: "🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🏃 🌲 🏃‍♀️ 🌲 🌲 🌲 🌲 🌲 🌲 🌲 🌲\n\nSuspendisse massa massa, maximus et finibus ac, auctor volutpat diam.\n\nPellentesque consequat magna condimentum mauris bibendum, nec ornare nisl hendrerit. Phasellus nec ultrices sem. Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n\nSuspendisse sit amet facilisis ante, ac suscipit sem. Integer feugiat sit amet erat sit amet mattis. Donec tristique, nunc ut varius elementum, nisi elit viverra ipsum, vitae aliquam justo libero in arcu. Quisque tempor et justo at malesuada. Curabitur justo dolor, ornare convallis sollicitudin sed, consectetur eu turpis. \n\nNulla et dui condimentum, laoreet lacus eu, ultrices nisl. Vivamus in ante volutpat, gravida nunc scelerisque, sagittis tellus. Nullam justo purus, sagittis a tincidunt a, maximus nec sem.",
text: "Emoji 😍 (/ɪˈmoʊdʒi/; singular emoji, plural emoji or emojis;[4] from the Japanese 絵文字えもじ, pronounced [emodʑi]) are ideograms and smileys used in electronic messages and web pages. Emoji are used much like emoticons and exist in various genres, including facial expressions, common objects, places and types of weather 🌅☔️💦, and animals 🐶🐱",
videoURL: URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
),
LightboxImage(
image: UIImage(named: "photo3")!,
text: "Some very long lorem ipsum text."
text: "A lightbox is a translucent surface illuminated from behind, used for situations where a shape laid upon the surface needs to be seen with high contrast."
)
]
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
+13
View File
@@ -0,0 +1,13 @@
import XCTest
@testable import Lightbox
class InfoLabelTests: XCTestCase {
func testTruncating() {
let label = InfoLabel(text: "", expanded: false)
label.frame.size = CGSize(width: 10, height: 10)
let text = Array(repeating: "A", count: 4).joined(separator: "")
label.fullText = text
_ = label.truncatedText
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "Lightbox"
s.summary = "A a convenient and easy to use image viewer for your iOS app, packed with all the features you expect"
s.version = "2.0.0"
s.version = "2.1.1"
s.homepage = "https://github.com/hyperoslo/Lightbox"
s.license = 'MIT'
s.author = { "Hyper Interaktiv AS" => "ios@hyper.no" }
+133 -1
View File
@@ -8,6 +8,8 @@
/* Begin PBXBuildFile section */
D22006741DFB4D9700E92898 /* Lightbox.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D22006731DFB4D9700E92898 /* Lightbox.bundle */; };
D229B5E61FC3123F00F04123 /* Lightbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D523B0A91C43AA2A001AD1EC /* Lightbox.framework */; };
D229B5ED1FC3125D00F04123 /* InfoLabelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D229B5EC1FC3125D00F04123 /* InfoLabelTests.swift */; };
D2A58F5E1F7943A30064F14E /* Imaginary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2A58F5D1F7943A30064F14E /* Imaginary.framework */; };
D2D71BBC1D54DA77006AB907 /* AssetManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D71BBB1D54DA77006AB907 /* AssetManager.swift */; };
D5026B3C1C5BF3FD003BC1A3 /* LightboxImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5026B3B1C5BF3FD003BC1A3 /* LightboxImage.swift */; };
@@ -24,8 +26,21 @@
D58A18CB1C5ABF8F000024BB /* FooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58A18CA1C5ABF8F000024BB /* FooterView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
D229B5E71FC3123F00F04123 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D523B0A01C43AA2A001AD1EC /* Project object */;
proxyType = 1;
remoteGlobalIDString = D523B0A81C43AA2A001AD1EC;
remoteInfo = "Lightbox-iOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
D22006731DFB4D9700E92898 /* Lightbox.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Lightbox.bundle; sourceTree = "<group>"; };
D229B5E11FC3123F00F04123 /* Lightbox-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Lightbox-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
D229B5E51FC3123F00F04123 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D229B5EC1FC3125D00F04123 /* InfoLabelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoLabelTests.swift; sourceTree = "<group>"; };
D2A58F5D1F7943A30064F14E /* Imaginary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Imaginary.framework; path = Carthage/Build/iOS/Imaginary.framework; sourceTree = "<group>"; };
D2D71BBB1D54DA77006AB907 /* AssetManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AssetManager.swift; sourceTree = "<group>"; };
D5026B3B1C5BF3FD003BC1A3 /* LightboxImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightboxImage.swift; sourceTree = "<group>"; };
@@ -45,6 +60,14 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
D229B5DE1FC3123F00F04123 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D229B5E61FC3123F00F04123 /* Lightbox.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D523B0A51C43AA2A001AD1EC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -65,6 +88,15 @@
path = Resources;
sourceTree = "<group>";
};
D229B5E21FC3123F00F04123 /* Lightbox-iOS-Tests */ = {
isa = PBXGroup;
children = (
D229B5E51FC3123F00F04123 /* Info.plist */,
D229B5EC1FC3125D00F04123 /* InfoLabelTests.swift */,
);
path = "Lightbox-iOS-Tests";
sourceTree = "<group>";
};
D2A58F5C1F7943A30064F14E /* Frameworks */ = {
isa = PBXGroup;
children = (
@@ -80,6 +112,7 @@
D54DFCC01C5AAAF100ADEA0E /* Hue.framework */,
D523B0B41C43AA8A001AD1EC /* Source */,
D523B0AB1C43AA2A001AD1EC /* Lightbox */,
D229B5E21FC3123F00F04123 /* Lightbox-iOS-Tests */,
D523B0AA1C43AA2A001AD1EC /* Products */,
D2A58F5C1F7943A30064F14E /* Frameworks */,
);
@@ -89,6 +122,7 @@
isa = PBXGroup;
children = (
D523B0A91C43AA2A001AD1EC /* Lightbox.framework */,
D229B5E11FC3123F00F04123 /* Lightbox-iOS-Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -149,6 +183,24 @@
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
D229B5E01FC3123F00F04123 /* Lightbox-iOS-Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = D229B5E91FC3123F00F04123 /* Build configuration list for PBXNativeTarget "Lightbox-iOS-Tests" */;
buildPhases = (
D229B5DD1FC3123F00F04123 /* Sources */,
D229B5DE1FC3123F00F04123 /* Frameworks */,
D229B5DF1FC3123F00F04123 /* Resources */,
);
buildRules = (
);
dependencies = (
D229B5E81FC3123F00F04123 /* PBXTargetDependency */,
);
name = "Lightbox-iOS-Tests";
productName = "Lightbox-iOS-Tests";
productReference = D229B5E11FC3123F00F04123 /* Lightbox-iOS-Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = D523B0B11C43AA2A001AD1EC /* Build configuration list for PBXNativeTarget "Lightbox-iOS" */;
@@ -175,9 +227,14 @@
D523B0A01C43AA2A001AD1EC /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0910;
LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "Hyper Interaktiv AS";
TargetAttributes = {
D229B5E01FC3123F00F04123 = {
CreatedOnToolsVersion = 9.1;
ProvisioningStyle = Automatic;
};
D523B0A81C43AA2A001AD1EC = {
CreatedOnToolsVersion = 7.2;
LastSwiftMigration = 0900;
@@ -197,11 +254,19 @@
projectRoot = "";
targets = (
D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */,
D229B5E01FC3123F00F04123 /* Lightbox-iOS-Tests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
D229B5DF1FC3123F00F04123 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
D523B0A71C43AA2A001AD1EC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -235,7 +300,6 @@
"$(SRCROOT)/Carthage/Build/iOS/Hue.framework",
"$(SRCROOT)/Carthage/Build/iOS/Imaginary.framework",
"$(SRCROOT)/Carthage/Build/iOS/Cache.framework",
"$(SRCROOT)/Carthage/Build/iOS/SwiftHash.framework",
);
name = "Copy frameworks with Carthage";
outputPaths = (
@@ -247,6 +311,14 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
D229B5DD1FC3123F00F04123 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D229B5ED1FC3125D00F04123 /* InfoLabelTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
D523B0A41C43AA2A001AD1EC /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -268,7 +340,58 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
D229B5E81FC3123F00F04123 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = D523B0A81C43AA2A001AD1EC /* Lightbox-iOS */;
targetProxy = D229B5E71FC3123F00F04123 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
D229B5EA1FC3123F00F04123 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Lightbox-iOS-Tests/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.fantageek.Lightbox-iOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
D229B5EB1FC3123F00F04123 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = "Lightbox-iOS-Tests/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.fantageek.Lightbox-iOS-Tests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
D523B0AF1C43AA2A001AD1EC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -424,6 +547,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
D229B5E91FC3123F00F04123 /* Build configuration list for PBXNativeTarget "Lightbox-iOS-Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D229B5EA1FC3123F00F04123 /* Debug */,
D229B5EB1FC3123F00F04123 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D523B0A31C43AA2A001AD1EC /* Build configuration list for PBXProject "Lightbox" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -29,7 +29,26 @@
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D229B5E01FC3123F00F04123"
BuildableName = "Lightbox-iOS-Tests.xctest"
BlueprintName = "Lightbox-iOS-Tests"
ReferencedContainer = "container:Lightbox.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D523B0A81C43AA2A001AD1EC"
BuildableName = "Lightbox.framework"
BlueprintName = "Lightbox-iOS"
ReferencedContainer = "container:Lightbox.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
+12 -10
View File
@@ -1,13 +1,11 @@
# Lightbox
[![CI Status](http://img.shields.io/travis/hyperoslo/Lightbox.svg?style=flat)](https://travis-ci.org/hyperoslo/Lightbox)
[![CI Status](https://circleci.com/gh/hyperoslo/Lightbox.png)](https://circleci.com/gh/hyperoslo/Lightbox)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![License](https://img.shields.io/cocoapods/l/Lightbox.svg?style=flat)](http://cocoadocs.org/docsets/Lightbox)
[![Platform](https://img.shields.io/cocoapods/p/Lightbox.svg?style=flat)](http://cocoadocs.org/docsets/Lightbox)
![Swift](https://img.shields.io/badge/%20in-swift%204.0-orange.svg)
[Demo](https://appetize.io/app/wfgwc2uvg82m9pzbt17p4rrgh4?device=iphone5s&scale=75&orientation=portrait&osVersion=9.3)
<img src="https://raw.githubusercontent.com/hyperoslo/Lightbox/master/Images/Icon.png" alt="Lightbox Icon" align="right" />
**Lightbox** is a convenient and easy to use image viewer for your iOS app,
@@ -17,10 +15,16 @@ packed with all the features you expect:
- [x] Video support.
- [x] Double-tap to zoom.
- [x] Image caption.
- [x] Dynamic background.
- [x] Dynamic background based on [Hue](https://github.com/hyperoslo/Hue)
- [x] Remote image loading and caching based on [Imaginary](https://github.com/hyperoslo/Imaginary)
- [x] Interactive transition animations.
- [x] Powerful configuration.
- [x] Demo project.
- [x] [Live Demo](https://appetize.io/app/wfgwc2uvg82m9pzbt17p4rrgh4?device=iphone5s&scale=75&orientation=portrait&osVersion=9.3)
<div align="center">
<img src="Images/demo.png" height="500">
<img src="Images/demo2.png" height="500">
</div>
## Table of Contents
@@ -106,9 +110,7 @@ extension ViewController: LightboxControllerDismissalDelegate: class {
### Image loading
By default images are loaded using `sendAsynchronousRequest` method of
`NSURLConnection`. But it's easy to change this behavior using **Lightbox**
configuration.
By default images are loaded using [Imaginary](https://github.com/hyperoslo/Imaginary) for reliable loading and caching. But it's easy to change this behavior using **LightboxConfig**
```swift
LightboxConfig.loadImage = {
@@ -119,7 +121,7 @@ LightboxConfig.loadImage = {
### Video
**Lightbox** has video support out of the box. Configure video by using `videoURL`:
**Lightbox** can show and plays video using default `AVPlayerViewController`. Showning video by using `videoURL`:
```swift
LightboxImage(
@@ -157,7 +159,7 @@ LightboxConfig.DeleteButton.image = UIImage(named: ImageList.Lightbox.deleteButt
LightboxConfig.DeleteButton.textAttributes = TextAttributes.Lightbox.deleteButton
LightboxConfig.DeleteButton.text = "Delete"
LightboxConfig.InfoLabel.ellipsisText = "ShowMore"
LightboxConfig.InfoLabel.ellipsisText = "Show more"
```
## Installation
-8
View File
@@ -28,14 +28,6 @@ class LightboxTransition: UIPercentDrivenInteractiveTransition {
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 {
+15
View File
@@ -2,6 +2,7 @@ import UIKit
import Hue
import AVKit
import AVFoundation
import Imaginary
public class LightboxConfig {
/// Whether to show status bar while Lightbox is presented
@@ -17,6 +18,20 @@ public class LightboxConfig {
}
}
/// How to load image onto UIImageView
public static var loadImage: (UIImageView, URL, ((UIImage?) -> Void)?) -> Void = { (imageView, imageURL, completion) in
// Use Imaginary by default
imageView.setImage(url: imageURL, placeholder: nil, completion: { result in
switch result {
case .value(let image):
completion?(image)
case .error:
completion?(nil)
}
})
}
/// Indicator is used to show while image is being fetched
public static var makeLoadingIndicator: () -> UIView = {
return LoadingIndicator()
+30 -29
View File
@@ -22,15 +22,13 @@ open class LightboxController: UIViewController {
lazy var scrollView: UIScrollView = { [unowned self] in
let scrollView = UIScrollView()
scrollView.frame = self.screenBounds
scrollView.isPagingEnabled = false
scrollView.delegate = self
scrollView.isUserInteractionEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.decelerationRate = UIScrollViewDecelerationRateFast
return scrollView
}()
}()
lazy var overlayTapGestureRecognizer: UITapGestureRecognizer = { [unowned self] in
let gesture = UITapGestureRecognizer()
@@ -61,14 +59,14 @@ open class LightboxController: UIViewController {
view.delegate = self
return view
}()
}()
open fileprivate(set) lazy var footerView: FooterView = { [unowned self] in
let view = FooterView()
view.delegate = self
return view
}()
}()
open fileprivate(set) lazy var overlayView: UIView = { [unowned self] in
let view = UIView(frame: CGRect.zero)
@@ -79,11 +77,7 @@ open class LightboxController: UIViewController {
view.alpha = 0
return view
}()
var screenBounds: CGRect {
return UIApplication.shared.delegate?.window??.bounds ?? .zero
}
}()
// MARK: - Properties
@@ -127,7 +121,7 @@ open class LightboxController: UIViewController {
open var spacing: CGFloat = 20 {
didSet {
configureLayout()
configureLayout(view.bounds.size)
}
}
@@ -190,10 +184,29 @@ open class LightboxController: UIViewController {
super.viewDidAppear(animated)
if !presented {
presented = true
configureLayout()
configureLayout(view.bounds.size)
}
}
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.frame = view.bounds
footerView.frame = CGRect(
x: 0,
y: view.bounds.height - footerView.frame.height,
width: view.bounds.width,
height: 100
)
headerView.frame = CGRect(
x: 0,
y: 16,
width: view.bounds.width,
height: 100
)
}
open override var prefersStatusBarHidden: Bool {
return LightboxConfig.hideStatusBar
}
@@ -222,7 +235,7 @@ open class LightboxController: UIViewController {
pageViews.append(pageView)
}
configureLayout()
configureLayout(view.bounds.size)
}
// MARK: - Pagination
@@ -258,7 +271,7 @@ open class LightboxController: UIViewController {
// MARK: - Layout
open func configureLayout(_ size: CGSize = UIApplication.shared.delegate?.window??.bounds.size ?? .zero) {
open func configureLayout(_ size: CGSize) {
scrollView.frame.size = size
scrollView.contentSize = CGSize(
width: size.width * CGFloat(numberOfPages) + spacing * CGFloat(numberOfPages - 1),
@@ -275,18 +288,8 @@ open class LightboxController: UIViewController {
}
}
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()
}
@@ -332,7 +335,7 @@ extension LightboxController: UIScrollViewDelegate {
}
targetContentOffset.pointee.x = x
currentPage = Int(x / screenBounds.width)
currentPage = Int(x / view.bounds.width)
}
}
@@ -397,8 +400,8 @@ extension LightboxController: HeaderViewDelegate {
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)
self.configureLayout(self.view.bounds.size)
self.currentPage = Int(self.scrollView.contentOffset.x / self.view.bounds.width)
deleteButton.isEnabled = true
}
}
@@ -416,8 +419,6 @@ extension LightboxController: HeaderViewDelegate {
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
+2 -9
View File
@@ -22,19 +22,12 @@ open class LightboxImage {
self.videoURL = videoURL
}
open func addImageTo(_ imageView: UIImageView, completion: ((_ image: UIImage?) -> Void)? = nil) {
open func addImageTo(_ imageView: UIImageView, completion: ((UIImage?) -> Void)? = nil) {
if let image = image {
imageView.image = image
completion?(image)
} else if let imageURL = imageURL {
imageView.setImage(url: imageURL, placeholder: nil, completion: { result in
switch result {
case .value(let image):
completion?(image)
case .error:
completion?(nil)
}
})
LightboxConfig.loadImage(imageView, imageURL, completion)
}
}
}
+23 -12
View File
@@ -16,7 +16,7 @@ open class FooterView: UIView {
label.delegate = self
return label
}()
}()
open fileprivate(set) lazy var pageLabel: UILabel = { [unowned self] in
let label = UILabel(frame: CGRect.zero)
@@ -24,7 +24,7 @@ open class FooterView: UIView {
label.numberOfLines = 1
return label
}()
}()
open fileprivate(set) lazy var separatorView: UIView = { [unowned self] in
let view = UILabel(frame: CGRect.zero)
@@ -32,7 +32,7 @@ open class FooterView: UIView {
view.backgroundColor = LightboxConfig.PageIndicator.separatorColor
return view
}()
}()
let gradientColors = [UIColor(hex: "040404").alpha(0.1), UIColor(hex: "040404")]
open weak var delegate: FooterViewDelegate?
@@ -76,17 +76,29 @@ open class FooterView: UIView {
}
}
// MARK: - Layout
open override func layoutSubviews() {
super.layoutSubviews()
fileprivate func resetFrames() {
frame.size.height = infoLabel.frame.height + 40 + 0.5
do {
let bottomPadding: CGFloat
if #available(iOS 11, *) {
bottomPadding = safeAreaInsets.bottom
} else {
bottomPadding = 0
}
pageLabel.frame.origin = CGPoint(
x: (frame.width - pageLabel.frame.width) / 2,
y: frame.height - pageLabel.frame.height - 2)
pageLabel.frame.origin = CGPoint(
x: (frame.width - pageLabel.frame.width) / 2,
y: frame.height - pageLabel.frame.height - 2 - bottomPadding
)
}
separatorView.frame = CGRect(x: 0, y: pageLabel.frame.minY - 2.5,
width: frame.width, height: 0.5)
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
@@ -107,7 +119,6 @@ extension FooterView: LayoutConfigurable {
extension FooterView: InfoLabelDelegate {
public func infoLabel(_ infoLabel: InfoLabel, didExpand expanded: Bool) {
resetFrames()
_ = (expanded || infoLabel.fullText.isEmpty) ? removeGradientLayer() : addGradientLayer(gradientColors)
delegate?.footerView(self, didExpand: expanded)
}
+18 -12
View File
@@ -6,13 +6,6 @@ protocol HeaderViewDelegate: class {
}
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,
@@ -38,7 +31,7 @@ open class HeaderView: UIView {
button.isHidden = !LightboxConfig.CloseButton.enabled
return button
}()
}()
open fileprivate(set) lazy var deleteButton: UIButton = { [unowned self] in
let title = NSAttributedString(
@@ -65,7 +58,7 @@ open class HeaderView: UIView {
button.isHidden = !LightboxConfig.DeleteButton.enabled
return button
}()
}()
weak var delegate: HeaderViewDelegate?
@@ -99,9 +92,22 @@ open class HeaderView: UIView {
extension HeaderView: LayoutConfigurable {
@objc public func configureLayout() {
closeButton.frame.origin = CGPoint(
x: bounds.width - closeButton.frame.width - 17, y: 0)
let topPadding: CGFloat
deleteButton.frame.origin = CGPoint(x: 17, y: 0)
if #available(iOS 11, *) {
topPadding = safeAreaInsets.top
} else {
topPadding = 0
}
closeButton.frame.origin = CGPoint(
x: bounds.width - closeButton.frame.width - 17,
y: topPadding
)
deleteButton.frame.origin = CGPoint(
x: 17,
y: topPadding
)
}
}
+20 -14
View File
@@ -41,26 +41,32 @@ open class InfoLabel: UILabel {
}
}
fileprivate var truncatedText: String {
var truncatedText: String {
var truncatedText = fullText
guard numberOfLines(fullText) > numberOfVisibleLines else {
return truncatedText
}
// Perform quick "rough cut"
while numberOfLines(truncatedText) > numberOfVisibleLines * 2 {
truncatedText = String(truncatedText.characters.prefix(truncatedText.characters.count / 2))
truncatedText = String(truncatedText.prefix(truncatedText.count / 2))
}
// Capture the endIndex of truncatedText before appending ellipsis
var truncatedTextCursor = truncatedText.endIndex
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
// Remove characters ahead of ellipsis until the text is the right number of lines
while numberOfLines(truncatedText) > numberOfVisibleLines {
truncatedText.removeSubrange(range)
range = truncatedText.index(range.lowerBound, offsetBy: -1)..<truncatedText.index(range.upperBound, offsetBy: -1)
// To avoid "Cannot decrement before startIndex"
guard truncatedTextCursor > truncatedText.startIndex else {
break
}
truncatedTextCursor = truncatedText.index(before: truncatedTextCursor)
truncatedText.remove(at: truncatedTextCursor)
}
return truncatedText
@@ -105,13 +111,13 @@ open class InfoLabel: UILabel {
}
fileprivate func updateText(_ string: String) {
let attributedString = NSMutableAttributedString(string: string,
attributes: LightboxConfig.InfoLabel.textAttributes)
let textAttributes = LightboxConfig.InfoLabel.textAttributes
let attributedString = NSMutableAttributedString(string: string, attributes: textAttributes)
if string.range(of: ellipsis) != nil {
let range = (string as NSString).range(of: ellipsis)
attributedString.addAttribute(NSAttributedStringKey.foregroundColor,
value: LightboxConfig.InfoLabel.ellipsisColor, range: range)
if let range = string.range(of: ellipsis) {
let ellipsisColor = LightboxConfig.InfoLabel.ellipsisColor
let ellipsisRange = NSRange(range, in: string)
attributedString.addAttribute(.foregroundColor, value: ellipsisColor, range: ellipsisRange)
}
attributedText = attributedString
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
carthage bootstrap
cp Cartfile.resolved Carthage
-5
View File
@@ -1,5 +0,0 @@
#!/bin/sh
if ! cmp -s Cartfile.resolved Carthage/Cartfile.resolved; then
bin/bootstrap
fi
+2 -3
View File
@@ -4,9 +4,8 @@ machine:
dependencies:
override:
- bin/bootstrap-if-needed
cache_directories:
- "Carthage"
- rm -rf Carthage
- carthage update
test:
override: