diff --git a/ImageSlideshow/Classes/Core/ImageSlideshow.swift b/ImageSlideshow/Classes/Core/ImageSlideshow.swift index 9d4fbd37..acdd34fc 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshow.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshow.swift @@ -28,7 +28,7 @@ public protocol ImageSlideshowDelegate: class { @objc optional func imageSlideshowDidEndDecelerating(_ imageSlideshow: ImageSlideshow) } -/** +/** Used to represent position of the Page Control - hidden: Page Control is hidden - insideScrollView: Page Control is inside image slideshow @@ -145,6 +145,10 @@ open class ImageSlideshow: UIView { } } + /// Optional fallbackImage for each InputSource + open var fallbackImage: ImageSource? + + /// Current scroll view page. This may differ from `currentPage` as circular slider has two more dummy pages at indexes 0 and n-1 to provide fluent scrolling between first and last item. open fileprivate(set) var scrollViewPage: Int = 0 @@ -207,6 +211,9 @@ open class ImageSlideshow: UIView { } } + // Fallback content mode for fallbackImage + open var fallbackScaleMode: UIView.ContentMode = .scaleAspectFit + fileprivate var slideshowTimer: Timer? fileprivate var scrollViewImages = [InputSource]() fileprivate var isAnimating: Bool = false @@ -283,7 +290,8 @@ open class ImageSlideshow: UIView { pageIndicatorView.isHidden = images.count < 2 var edgeInsets: UIEdgeInsets = UIEdgeInsets.zero - if #available(iOS 11.0, *) { + if #available(iOS 11.0, *), + pageIndicator?.respectSafeAreaInsets ?? true { edgeInsets = safeAreaInsets } @@ -320,7 +328,7 @@ open class ImageSlideshow: UIView { var i = 0 for image in scrollViewImages { - let item = ImageSlideshowItem(image: image, zoomEnabled: zoomEnabled, activityIndicator: activityIndicator?.create(), maximumScale: maximumScale) + let item = ImageSlideshowItem(image: image, zoomEnabled: zoomEnabled, activityIndicator: activityIndicator?.create(), maximumScale: maximumScale, fallbackImage: fallbackImage, fallbackScaleMode: fallbackScaleMode) item.imageView.contentMode = contentScaleMode slideshowItems.append(item) scrollView.addSubview(item) diff --git a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift index 7b2136a8..1a03e37a 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift @@ -20,6 +20,12 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { /// Input Source for the item public let image: InputSource + /// Fallback Input Source for the item + public let fallbackImage: InputSource? + + /// Scale mode for fallback input source + public let fallbackScaleMode: UIView.ContentMode + /// Guesture recognizer to detect double tap to zoom open var gestureRecognizer: UITapGestureRecognizer? @@ -52,12 +58,16 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { Initializes a new ImageSlideshowItem - parameter image: Input Source to load the image - parameter zoomEnabled: holds if it should be possible to zoom-in the image + - parameter fallbackImage: A fallback image to display if image is unavailable + - parameter fallbackScaleMode: The fallback image scale mode */ - init(image: InputSource, zoomEnabled: Bool, activityIndicator: ActivityIndicatorView? = nil, maximumScale: CGFloat = 2.0) { + init(image: InputSource, zoomEnabled: Bool, activityIndicator: ActivityIndicatorView? = nil, maximumScale: CGFloat = 2.0, fallbackImage: InputSource? = nil, fallbackScaleMode: UIViewContentMode = .scaleAspectFit) { self.zoomEnabled = zoomEnabled self.image = image self.activityIndicator = activityIndicator self.maximumScale = maximumScale + self.fallbackImage = fallbackImage + self.fallbackScaleMode = fallbackScaleMode super.init(frame: CGRect.null) @@ -135,7 +145,8 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { /// Request to load Image Source to Image View public func loadImage() { - if self.imageView.image == nil && !isLoading { + if (self.imageView.image == nil || self.loadFailed) && !isLoading { + self.imageView.image = nil isLoading = true imageReleased = false activityIndicator?.show() @@ -150,6 +161,17 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { self?.loadFailed = image == nil self?.isLoading = false + if (self?.loadFailed ?? false) && self?.fallbackImage != nil { + if let imageView = self?.imageView { + self?.fallbackImage?.load(to: imageView) { [weak self] fallbackImage in + imageView.image = (self?.imageReleased ?? false) ? nil : fallbackImage + if let scaleMode = self?.fallbackScaleMode { + imageView.contentMode = scaleMode + } + } + } + } + self?.setNeedsLayout() } } diff --git a/ImageSlideshow/Classes/Core/PageIndicator.swift b/ImageSlideshow/Classes/Core/PageIndicator.swift index 2dd99b5e..801618f0 100644 --- a/ImageSlideshow/Classes/Core/PageIndicator.swift +++ b/ImageSlideshow/Classes/Core/PageIndicator.swift @@ -17,6 +17,9 @@ public protocol PageIndicatorView: class { /// Total number of pages of the page indicator var numberOfPages: Int { get set} + + /// Bool indicating if the page indicator view should respect safe area insets + var respectSafeAreaInsets: Bool { get } } extension UIPageControl: PageIndicatorView { @@ -33,6 +36,10 @@ extension UIPageControl: PageIndicatorView { } } + public var respectSafeAreaInsets: Bool { + return true + } + open override func sizeToFit() { var frame = self.frame frame.size = size(forNumberOfPages: numberOfPages) @@ -59,6 +66,10 @@ public class LabelPageIndicator: UILabel, PageIndicatorView { } } + public var respectSafeAreaInsets: Bool { + return true + } + public override init(frame: CGRect) { super.init(frame: frame) initialize()