Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d9e3ef7ab | |||
| 888067cae0 | |||
| 0180ca76c7 | |||
| 951b95b0fb | |||
| 1803722b6a | |||
| 4f5ee72f97 | |||
| 9e17ebb58f | |||
| 28acb5a8d6 | |||
| 5c6fa430e0 | |||
| 5108f3651f | |||
| 05ec840ffd | |||
| 5189d880c4 | |||
| 308051a898 | |||
| 56481d5f8a | |||
| 3882befd7b | |||
| cf88115eb6 | |||
| 663bafce52 | |||
| 6911104a98 | |||
| 369252ee7c | |||
| 3322107688 | |||
| 7731adc79c | |||
| 5c48b2482a | |||
| 83416d6870 | |||
| 4f9540b4df | |||
| 304c3e61f6 | |||
| 026153f502 | |||
| 6e53ee01c7 |
+2
-3
@@ -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."
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 290 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 296 KiB |
+1
-1
@@ -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.0"
|
||||
s.homepage = "https://github.com/hyperoslo/Lightbox"
|
||||
s.license = 'MIT'
|
||||
s.author = { "Hyper Interaktiv AS" => "ios@hyper.no" }
|
||||
|
||||
@@ -235,7 +235,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 = (
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
# Lightbox
|
||||
|
||||
[](https://travis-ci.org/hyperoslo/Lightbox)
|
||||
[](https://circleci.com/gh/hyperoslo/Lightbox)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||
[](http://cocoadocs.org/docsets/Lightbox)
|
||||

|
||||
|
||||
[Demo](https://appetize.io/app/wfgwc2uvg82m9pzbt17p4rrgh4?device=iphone5s&scale=75&orientation=portrait&osVersion=9.3)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/hyperoslo/Lightbox/master/Images/Icon.png" alt="Lightbox Icon" align="right" />
|
||||
|
||||
**Lightbox** is a convenient and easy to use image viewer for your iOS app,
|
||||
@@ -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 [Imagary](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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,21 @@ 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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -194,6 +188,25 @@ open class LightboxController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +401,7 @@ extension LightboxController: HeaderViewDelegate {
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
|
||||
self.configureLayout()
|
||||
self.currentPage = Int(self.scrollView.contentOffset.x / self.screenBounds.width)
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,19 +48,20 @@ open class InfoLabel: UILabel {
|
||||
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)
|
||||
truncatedTextCursor = truncatedText.index(before: truncatedTextCursor)
|
||||
truncatedText.remove(at: truncatedTextCursor)
|
||||
}
|
||||
|
||||
return truncatedText
|
||||
@@ -105,13 +106,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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
carthage bootstrap
|
||||
cp Cartfile.resolved Carthage
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! cmp -s Cartfile.resolved Carthage/Cartfile.resolved; then
|
||||
bin/bootstrap
|
||||
fi
|
||||
+2
-3
@@ -4,9 +4,8 @@ machine:
|
||||
|
||||
dependencies:
|
||||
override:
|
||||
- bin/bootstrap-if-needed
|
||||
cache_directories:
|
||||
- "Carthage"
|
||||
- rm -rf Carthage
|
||||
- carthage update
|
||||
|
||||
test:
|
||||
override:
|
||||
|
||||
Reference in New Issue
Block a user