diff --git a/.gitignore b/.gitignore index 684eb885..669d819a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ Carthage # `pod install` in .travis.yml Pods/ + +# Swift Package Manager +.build/ diff --git a/.swift-version b/.swift-version index bf77d549..ef425ca9 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.2 diff --git a/Example/ImageSlideshow.xcodeproj/project.pbxproj b/Example/ImageSlideshow.xcodeproj/project.pbxproj index b809b4a6..888725d1 100644 --- a/Example/ImageSlideshow.xcodeproj/project.pbxproj +++ b/Example/ImageSlideshow.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */; }; 48A9DE9233FBEA3CF63A8E54 /* Pods_ImageSlideshow_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; @@ -46,6 +47,7 @@ /* Begin PBXFileReference section */ 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageSlideshow_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example.release.xcconfig"; sourceTree = ""; }; 1C4985EDB005E63A830176CE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Bundle+Module.swift"; path = "../../ImageSlideshow/Classes/Core/Bundle+Module.swift"; sourceTree = ""; }; 5FD91AF3667236846934E8B9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageSlideshow_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -207,20 +209,21 @@ D0E8A9E31D97EB6D007EC517 /* ImageSlideshow_framework */ = { isa = PBXGroup; children = ( - F539204B210F03600057EFB3 /* SwiftSupport.swift */, + D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */, + 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */, D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */, - D0B974AF202738F6006217CF /* PageIndicator.swift */, - F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */, + D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */, D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */, - D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */, D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */, + D0E8A9E51D97EB6D007EC517 /* Info.plist */, D0E8A9ED1D97EB94007EC517 /* InputSource.swift */, + D0B974AF202738F6006217CF /* PageIndicator.swift */, + F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */, + D00C7A2420B4C05C00E5725B /* Resources */, + F539204B210F03600057EFB3 /* SwiftSupport.swift */, D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */, D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */, D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */, - D00C7A2420B4C05C00E5725B /* Resources */, - D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */, - D0E8A9E51D97EB6D007EC517 /* Info.plist */, ); path = ImageSlideshow_framework; sourceTree = ""; @@ -453,6 +456,7 @@ D0E8A9F61D97EB94007EC517 /* UIImageView+Tools.swift in Sources */, D0E8A9F71D97EB94007EC517 /* ZoomAnimatedTransitioning.swift in Sources */, D0083DCE1EB739E700126B21 /* ActivityIndicator.swift in Sources */, + 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */, F802999120CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */, D0E8A9F51D97EB94007EC517 /* UIImage+AspectFit.swift in Sources */, D0E8A9F11D97EB94007EC517 /* FullScreenSlideshowViewController.swift in Sources */, diff --git a/Example/ImageSlideshow/Base.lproj/Main.storyboard b/Example/ImageSlideshow/Base.lproj/Main.storyboard index 59a04678..1c0e19b7 100644 --- a/Example/ImageSlideshow/Base.lproj/Main.storyboard +++ b/Example/ImageSlideshow/Base.lproj/Main.storyboard @@ -1,11 +1,10 @@ - - - - + + - + + @@ -25,7 +24,7 @@ - + @@ -35,17 +34,16 @@ - - + + + + + - - - + + + + + - + + + + + + @@ -89,7 +98,12 @@ - + + + + + + diff --git a/Example/ImageSlideshow/TableViewController.swift b/Example/ImageSlideshow/TableViewController.swift index f226834b..76869655 100644 --- a/Example/ImageSlideshow/TableViewController.swift +++ b/Example/ImageSlideshow/TableViewController.swift @@ -20,7 +20,7 @@ struct Model { class TableViewController: UITableViewController { - let models = [Model(image: UIImage(named:"img1")!, title: "First image"), Model(image: UIImage(named: "img2")!, title: "Second image"), Model(image: UIImage(named: "img3")!, title: "Third image"), Model(image: UIImage(named: "img4")!, title: "Fourth image")] + let models = [Model(image: UIImage(named: "img1")!, title: "First image"), Model(image: UIImage(named: "img2")!, title: "Second image"), Model(image: UIImage(named: "img3")!, title: "Third image"), Model(image: UIImage(named: "img4")!, title: "Fourth image")] var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? = nil @@ -52,6 +52,7 @@ class TableViewController: UITableViewController { if let cell = tableView.cellForRow(at: indexPath), let imageView = cell.imageView { slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(imageView: imageView, slideshowController: fullScreenController) + fullScreenController.modalPresentationStyle = .custom fullScreenController.transitioningDelegate = slideshowTransitioningDelegate } diff --git a/Example/ImageSlideshow/ViewController.swift b/Example/ImageSlideshow/ViewController.swift index c175f36b..f4cb7aef 100644 --- a/Example/ImageSlideshow/ViewController.swift +++ b/Example/ImageSlideshow/ViewController.swift @@ -30,10 +30,7 @@ class ViewController: UIViewController { slideshow.pageIndicatorPosition = .init(horizontal: .center, vertical: .under) slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill - let pageControl = UIPageControl() - pageControl.currentPageIndicatorTintColor = UIColor.lightGray - pageControl.pageIndicatorTintColor = UIColor.black - slideshow.pageIndicator = pageControl + slideshow.pageIndicator = UIPageControl.withSlideshowColors() // optional way to show activity indicator during image load (skipping the line will show no activity indicator) slideshow.activityIndicator = DefaultActivityIndicator() @@ -54,7 +51,7 @@ class ViewController: UIViewController { } extension ViewController: ImageSlideshowDelegate { - func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int) { + func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int) { print("current page:", page) } } diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 9ac26818..373b4057 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -14,30 +14,28 @@ PODS: - AFNetworking/Serialization (3.2.1) - AFNetworking/UIKit (3.2.1): - AFNetworking/NSURLSession - - Alamofire (4.9.1) - - AlamofireImage (3.6.0): - - Alamofire (~> 4.9) - - ImageSlideshow (1.8.1): - - ImageSlideshow/Core (= 1.8.1) - - ImageSlideshow/AFURL (1.8.1): + - Alamofire (5.9.1) + - AlamofireImage (4.3.0): + - Alamofire (~> 5.8) + - ImageSlideshow (1.9.2): + - ImageSlideshow/Core (= 1.9.2) + - ImageSlideshow/AFURL (1.9.2): - AFNetworking (~> 3.0) - ImageSlideshow/Core - - ImageSlideshow/Alamofire (1.8.1): - - AlamofireImage (~> 3.0) + - ImageSlideshow/Alamofire (1.9.2): + - AlamofireImage (~> 4.0) - ImageSlideshow/Core - - ImageSlideshow/Core (1.8.1) - - ImageSlideshow/Kingfisher (1.8.1): + - ImageSlideshow/Core (1.9.2) + - ImageSlideshow/Kingfisher (1.9.2): - ImageSlideshow/Core - Kingfisher (> 3.0) - - ImageSlideshow/SDWebImage (1.8.1): + - ImageSlideshow/SDWebImage (1.9.2): - ImageSlideshow/Core - SDWebImage (>= 3.7) - - Kingfisher (5.13.0): - - Kingfisher/Core (= 5.13.0) - - Kingfisher/Core (5.13.0) - - SDWebImage (5.5.1): - - SDWebImage/Core (= 5.5.1) - - SDWebImage/Core (5.5.1) + - Kingfisher (6.3.1) + - SDWebImage (5.19.6): + - SDWebImage/Core (= 5.19.6) + - SDWebImage/Core (5.19.6) DEPENDENCIES: - ImageSlideshow (from `../`) @@ -59,13 +57,13 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 - Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 - AlamofireImage: be9963c6582d68b39e89191f64c82a7d7bf40fdd - ImageSlideshow: f088eda262a51187cc50ca898f5f880fd52b9b00 - Kingfisher: 0d334cada987fcddbe9b5cec516406e1ee9d4748 - SDWebImage: 1245d058b7b8f59adef7a6da6bbafd4f1ab67041 + AFNetworking: cb604b1c2bded0871f5f61f5d53653739e841d6b + Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c + AlamofireImage: 843953fa97bee5f561cf05d83abd759e590b068d + ImageSlideshow: be305b54eaf2b2cfc7a7141c7c734605d44dc07d + Kingfisher: 016c8b653a35add51dd34a3aba36b580041acc74 + SDWebImage: a79252b60f4678812d94316c91da69ec83089c9f PODFILE CHECKSUM: 46015634277c8c8bfd78d1b363e41ea2dfd179d9 -COCOAPODS: 1.8.4 +COCOAPODS: 1.15.2 diff --git a/ImageSlideshow.podspec b/ImageSlideshow.podspec index 439d2bfe..db075e96 100644 --- a/ImageSlideshow.podspec +++ b/ImageSlideshow.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "ImageSlideshow" - s.version = "1.8.3" + s.version = "1.9.2" s.summary = "Image slideshow written in Swift with circular scrolling, timer and full screen viewer" # This description is used to generate tags and improve search results. @@ -21,13 +21,13 @@ Image slideshow is a Swift library providing customizable image slideshow with c DESC s.homepage = "https://github.com/zvonicek/ImageSlideshow" - s.screenshots = "http://cl.ly/image/2v193I0G0h0Z/ImageSlideshow2.gif" + s.screenshots = "https://dzwonsemrish7.cloudfront.net/items/2R06283n040V3P3p0i42/ezgif.com-optimize.gif" s.license = 'MIT' s.author = { "Petr Zvonicek" => "zvonicek@gmail.com" } s.source = { :git => "https://github.com/zvonicek/ImageSlideshow.git", :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/zvonicek' - s.swift_versions = ['4.0', '4.1', '4.2', '5', '5.1'] + s.swift_versions = ['4.0', '4.1', '4.2', '5', '5.1', '5.2'] s.platform = :ios, '8.0' s.requires_arc = true @@ -42,9 +42,16 @@ Image slideshow is a Swift library providing customizable image slideshow with c subspec.source_files = 'ImageSlideshow/Classes/InputSources/AFURLSource.swift' end - s.subspec 'Alamofire' do |subspec| + s.subspec 'Alamofire3' do |subspec| subspec.dependency 'ImageSlideshow/Core' subspec.dependency 'AlamofireImage', '~> 3.0' + subspec.source_files = 'ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift' + end + + s.subspec 'Alamofire' do |subspec| + subspec.dependency 'ImageSlideshow/Core' + subspec.dependency 'AlamofireImage', '~> 4.0' + subspec.platform = :ios, '10.0' subspec.source_files = 'ImageSlideshow/Classes/InputSources/AlamofireSource.swift' end diff --git a/ImageSlideshow/Classes/Core/ActivityIndicator.swift b/ImageSlideshow/Classes/Core/ActivityIndicator.swift index a6f4460b..a0af2009 100644 --- a/ImageSlideshow/Classes/Core/ActivityIndicator.swift +++ b/ImageSlideshow/Classes/Core/ActivityIndicator.swift @@ -44,7 +44,7 @@ extension UIActivityIndicatorView: ActivityIndicatorView { open class DefaultActivityIndicator: ActivityIndicatorFactory { /// activity indicator style open var style: UIActivityIndicatorViewStyle - + /// activity indicator color open var color: UIColor? diff --git a/ImageSlideshow/Classes/Core/Bundle+Module.swift b/ImageSlideshow/Classes/Core/Bundle+Module.swift new file mode 100644 index 00000000..4ad0203f --- /dev/null +++ b/ImageSlideshow/Classes/Core/Bundle+Module.swift @@ -0,0 +1,16 @@ +// +// Bundle+Module.swift +// ImageSlideshow +// +// Created by woxtu on 20/11/21. +// + +import Foundation + +#if !SWIFT_PACKAGE +extension Bundle { + static var module: Bundle = { + return Bundle(for: ImageSlideshow.self) + }() +} +#endif diff --git a/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift b/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift index d259b2d4..6aac7426 100644 --- a/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift +++ b/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift @@ -50,10 +50,10 @@ open class FullScreenSlideshowViewController: UIViewController { fileprivate var isInit = true convenience init() { - self.init(nibName:nil, bundle:nil) + self.init(nibName: nil, bundle: nil) + self.modalPresentationStyle = .custom if #available(iOS 13.0, *) { - self.modalPresentationStyle = .fullScreen // Use KVC to set the value to preserve backwards compatiblity with Xcode < 11 self.setValue(true, forKey: "modalInPresentation") } @@ -72,7 +72,7 @@ open class FullScreenSlideshowViewController: UIViewController { view.addSubview(slideshow) // close button configuration - closeButton.setImage(UIImage(named: "ic_cross_white", in: Bundle(for: type(of: self)), compatibleWith: nil), for: UIControlState()) + closeButton.setImage(UIImage(named: "ic_cross_white", in: .module, compatibleWith: nil), for: UIControlState()) closeButton.addTarget(self, action: #selector(FullScreenSlideshowViewController.close), for: UIControlEvents.touchUpInside) view.addSubview(closeButton) } @@ -92,8 +92,11 @@ open class FullScreenSlideshowViewController: UIViewController { override open func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - + slideshow.slideshowItems.forEach { $0.cancelPendingLoad() } + + // Prevents broken dismiss transition when image is zoomed in + slideshow.currentSlideshowItem?.zoomOut() } open override func viewDidLayoutSubviews() { @@ -104,14 +107,14 @@ open class FullScreenSlideshowViewController: UIViewController { } else { safeAreaInsets = UIEdgeInsets.zero } - + closeButton.frame = closeButtonFrame ?? CGRect(x: max(10, safeAreaInsets.left), y: max(10, safeAreaInsets.top), width: 40, height: 40) } slideshow.frame = view.frame } - @objc func close() { + func close() { // if pageSelected closure set, send call it with current page if let pageSelected = pageSelected { pageSelected(slideshow.currentPage) diff --git a/ImageSlideshow/Classes/Core/ImageSlideshow.swift b/ImageSlideshow/Classes/Core/ImageSlideshow.swift index 898a5bf2..936a002e 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshow.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshow.swift @@ -74,11 +74,14 @@ open class ImageSlideshow: UIView { } } - open var pageIndicator: PageIndicatorView? = UIPageControl() { + open var pageIndicator: PageIndicatorView? { didSet { oldValue?.view.removeFromSuperview() if let pageIndicator = pageIndicator { addSubview(pageIndicator.view) + if let pageIndicator = pageIndicator as? UIControl { + pageIndicator.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged) + } } setNeedsLayout() } @@ -114,6 +117,7 @@ open class ImageSlideshow: UIView { open fileprivate(set) var currentPage: Int = 0 { didSet { if oldValue != currentPage { + pageIndicator?.page = currentPage currentPageChanged?(currentPage) delegate?.imageSlideshow?(self, didChangeCurrentPageTo: currentPage) } @@ -124,13 +128,13 @@ open class ImageSlideshow: UIView { open weak var delegate: ImageSlideshowDelegate? /// Called on each currentPage change - open var currentPageChanged: ((_ page: Int) -> ())? + open var currentPageChanged: ((_ page: Int) -> Void)? /// Called on scrollViewWillBeginDragging - open var willBeginDragging: (() -> ())? + open var willBeginDragging: (() -> Void)? /// Called on scrollViewDidEndDecelerating - open var didEndDecelerating: (() -> ())? + open var didEndDecelerating: (() -> Void)? /// Currenlty displayed slideshow item open var currentSlideshowItem: ImageSlideshowItem? { @@ -174,7 +178,7 @@ open class ImageSlideshow: UIView { reloadScrollView() } } - + /// Maximum zoom scale open var maximumScale: CGFloat = 2.0 { didSet { @@ -208,9 +212,9 @@ open class ImageSlideshow: UIView { fileprivate var isAnimating: Bool = false /// Transitioning delegate to manage the transition to full screen controller - open fileprivate(set) var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? - - var primaryVisiblePage: Int { + open fileprivate(set) var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? // swiftlint:disable:this weak_delegate + + private var primaryVisiblePage: Int { return scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x + scrollView.frame.size.width / 2) / Int(scrollView.frame.size.width) : 0 } @@ -233,6 +237,9 @@ open class ImageSlideshow: UIView { fileprivate func initialize() { autoresizesSubviews = true clipsToBounds = true + if #available(iOS 13.0, *) { + backgroundColor = .systemBackground + } // scroll view configuration scrollView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height - 50.0) @@ -251,12 +258,8 @@ open class ImageSlideshow: UIView { } addSubview(scrollView) - if let pageIndicator = pageIndicator { - addSubview(pageIndicator.view) - } - - if let pageIndicator = pageIndicator as? UIControl { - pageIndicator.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged) + if pageIndicator == nil { + pageIndicator = UIPageControl() } setTimerIfNeeded() @@ -426,7 +429,7 @@ open class ImageSlideshow: UIView { } } - @objc func slideshowTick(_ timer: Timer) { + func slideshowTick(_ timer: Timer) { let page = scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x / scrollView.frame.size.width) : 0 var nextPage = page + 1 @@ -451,7 +454,7 @@ open class ImageSlideshow: UIView { scrollViewPage = page currentPage = currentPageForScrollViewPage(page) } - + fileprivate func currentPageForScrollViewPage(_ page: Int) -> Int { if circular { if page == 0 { @@ -537,7 +540,7 @@ open class ImageSlideshow: UIView { - returns: FullScreenSlideshowViewController instance */ @discardableResult - open func presentFullScreenController(from controller: UIViewController) -> FullScreenSlideshowViewController { + open func presentFullScreenController(from controller: UIViewController, completion: (() -> Void)? = nil) -> FullScreenSlideshowViewController { let fullscreen = FullScreenSlideshowViewController() fullscreen.pageSelected = {[weak self] (page: Int) in self?.setCurrentPage(page, animated: false) @@ -547,7 +550,8 @@ open class ImageSlideshow: UIView { fullscreen.inputs = images slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(slideshowView: self, slideshowController: fullscreen) fullscreen.transitioningDelegate = slideshowTransitioningDelegate - controller.present(fullscreen, animated: true, completion: nil) + fullscreen.modalPresentationStyle = .custom + controller.present(fullscreen, animated: true, completion: completion) return fullscreen } @@ -584,7 +588,11 @@ extension ImageSlideshow: UIScrollViewDelegate { } } - pageIndicator?.page = currentPageForScrollViewPage(primaryVisiblePage) + // Updates the page indicator as the user scrolls (#204). Not called when not dragging to prevent flickers + // when interacting with PageControl directly (#376). + if scrollView.isDragging { + pageIndicator?.page = currentPageForScrollViewPage(primaryVisiblePage) + } } public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { diff --git a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift index 62f056a1..8c1df7c2 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift @@ -28,7 +28,7 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { /// If set to true image is initially zoomed in open var zoomInInitially = false - + /// Maximum zoom scale open var maximumScale: CGFloat = 2.0 @@ -63,6 +63,11 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { imageViewWrapper.addSubview(imageView) imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + imageView.isAccessibilityElement = true + imageView.accessibilityTraits = .image + if #available(iOS 11.0, *) { + imageView.accessibilityIgnoresInvertColors = true + } imageViewWrapper.clipsToBounds = true imageViewWrapper.isUserInteractionEnabled = true @@ -138,29 +143,29 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { // set image to nil if there was a release request during the image load if let imageRelease = self?.imageReleased, imageRelease { self?.imageView.image = nil - }else{ + } else { self?.imageView.image = image } self?.activityIndicator?.hide() self?.loadFailed = image == nil self?.isLoading = false - + self?.setNeedsLayout() } } } - + func releaseImage() { imageReleased = true cancelPendingLoad() self.imageView.image = nil } - + public func cancelPendingLoad() { image.cancelLoad?(on: imageView) } - @objc func retryLoadImage() { + func retryLoadImage() { self.loadImage() } @@ -174,11 +179,26 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { self.setZoomScale(minimumZoomScale, animated: false) } - @objc func tapZoom() { + func tapZoom() { if isZoomed() { self.setZoomScale(minimumZoomScale, animated: true) } else { - self.setZoomScale(maximumZoomScale, animated: true) + // Zoom to aspect fill the image + var scale = maximumZoomScale + if let image = self.imageView.image + { + let selfAspect = self.bounds.size.width / self.bounds.size.height + let imageAspect = image.size.width / image.size.height + if selfAspect > imageAspect + { + scale = self.bounds.size.width / self.imageView.bounds.size.width + } + else + { + scale = self.bounds.size.height / self.imageView.bounds.size.height + } + } + self.setZoomScale(min(scale, maximumZoomScale), animated: true) } } diff --git a/ImageSlideshow/Classes/Core/InputSource.swift b/ImageSlideshow/Classes/Core/InputSource.swift index 6b8ef33c..3689af2a 100644 --- a/ImageSlideshow/Classes/Core/InputSource.swift +++ b/ImageSlideshow/Classes/Core/InputSource.swift @@ -17,7 +17,7 @@ import UIKit - parameter image: Image that was set to the image view. */ func load(to imageView: UIImageView, with callback: @escaping (_ image: UIImage?) -> Void) - + /** Cancel image load on the image view - parameter imageView: Image view that is loading the image @@ -58,14 +58,14 @@ open class ImageSource: NSObject, InputSource { @objcMembers open class BundleImageSource: NSObject, InputSource { var imageString: String - + /// Initializes a new Image Source with an image name from the main bundle /// - parameter imageString: name of the file in the application's main bundle public init(imageString: String) { self.imageString = imageString super.init() } - + public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { let image = UIImage(named: imageString) imageView.image = image @@ -77,14 +77,14 @@ open class BundleImageSource: NSObject, InputSource { @objcMembers open class FileImageSource: NSObject, InputSource { var path: String - + /// Initializes a new Image Source with an image name from the main bundle /// - parameter imageString: name of the file in the application's main bundle public init(path: String) { self.path = path super.init() } - + public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { let image = UIImage(contentsOfFile: path) imageView.image = image diff --git a/ImageSlideshow/Classes/Core/PageIndicator.swift b/ImageSlideshow/Classes/Core/PageIndicator.swift index 2dd99b5e..d9863e91 100644 --- a/ImageSlideshow/Classes/Core/PageIndicator.swift +++ b/ImageSlideshow/Classes/Core/PageIndicator.swift @@ -39,6 +39,28 @@ extension UIPageControl: PageIndicatorView { frame.size.height = 30 self.frame = frame } + + public static func withSlideshowColors() -> UIPageControl { + let pageControl = UIPageControl() + + if #available(iOS 13.0, *) { + pageControl.currentPageIndicatorTintColor = UIColor { traits in + traits.userInterfaceStyle == .dark ? .white : .lightGray + } + } else { + pageControl.currentPageIndicatorTintColor = .lightGray + } + + if #available(iOS 13.0, *) { + pageControl.pageIndicatorTintColor = UIColor { traits in + traits.userInterfaceStyle == .dark ? .systemGray : .black + } + } else { + pageControl.pageIndicatorTintColor = .black + } + + return pageControl + } } /// Page indicator that shows page in numeric style, eg. "5/21" diff --git a/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift b/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift index d6b62b40..9b29afae 100644 --- a/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift +++ b/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift @@ -61,7 +61,7 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti UIApplication.shared.keyWindow?.addGestureRecognizer(gestureRecognizer) } - @objc func handleSwipe(_ gesture: UIPanGestureRecognizer) { + func handleSwipe(_ gesture: UIPanGestureRecognizer) { guard let referenceSlideshowController = referenceSlideshowController else { return } @@ -123,6 +123,17 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { return interactionController } + + public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { + return PresentationController(presentedViewController: presented, presenting: presenting) + } +} + +private class PresentationController: UIPresentationController { + // Needed for interactive dismiss to keep the presenter View Controller visible + override var shouldRemovePresentersView: Bool { + return false + } } extension ZoomAnimatedTransitioningDelegate: UIGestureRecognizerDelegate { @@ -199,7 +210,6 @@ class ZoomInAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { containerView.sendSubview(toBack: transitionBackgroundView) #endif - let finalFrame = toViewController.view.frame var transitionView: UIImageView? @@ -225,7 +235,7 @@ class ZoomInAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { let duration: TimeInterval = transitionDuration(using: transitionContext) - UIView.animate(withDuration: duration, delay:0, usingSpringWithDamping:0.7, initialSpringVelocity:0, options: UIViewAnimationOptions.curveLinear, animations: { + UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveLinear, animations: { fromViewController.view.alpha = 0 transitionView?.frame = transitionViewFinalFrame transitionView?.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY) @@ -254,7 +264,7 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { if let animatorForCurrentSession = animatorForCurrentTransition { return animatorForCurrentSession } - + let params = animationParams(using: transitionContext) let animator = UIViewPropertyAnimator(duration: params.0, curve: .linear, animations: params.1) @@ -264,7 +274,7 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { return animator } - private func animationParams(using transitionContext: UIViewControllerContextTransitioning) -> (TimeInterval, () -> (), (Any) -> ()) { + private func animationParams(using transitionContext: UIViewControllerContextTransitioning) -> (TimeInterval, () -> Void, (Any) -> Void) { let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as? FullScreenSlideshowViewController else { @@ -273,14 +283,6 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { let containerView = transitionContext.containerView - toViewController.view.frame = transitionContext.finalFrame(for: toViewController) - toViewController.view.alpha = 0 - containerView.addSubview(toViewController.view) - #if swift(>=4.2) - containerView.sendSubviewToBack(toViewController.view) - #else - containerView.sendSubview(toBack: toViewController.view) - #endif var transitionViewInitialFrame: CGRect if let currentSlideshowItem = fromViewController.slideshow.currentSlideshowItem { if let image = currentSlideshowItem.imageView.image { @@ -322,16 +324,16 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { containerView.sendSubview(toBack: transitionBackgroundView) #endif - let transitionView: UIImageView = UIImageView(image: fromViewController.slideshow.currentSlideshowItem?.imageView.image) + let transitionView = UIImageView(image: fromViewController.slideshow.currentSlideshowItem?.imageView.image) transitionView.contentMode = UIViewContentMode.scaleAspectFill transitionView.clipsToBounds = true transitionView.frame = transitionViewInitialFrame containerView.addSubview(transitionView) fromViewController.view.isHidden = true - let duration: TimeInterval = transitionDuration(using: transitionContext) + let duration = transitionDuration(using: transitionContext) let animations = { - toViewController.view.alpha = 1 + transitionBackgroundView.alpha = 0 transitionView.frame = transitionViewFinalFrame } let completion = { (_: Any) in diff --git a/ImageSlideshow/Classes/InputSources/AFURLSource.swift b/ImageSlideshow/Classes/InputSources/AFURLSource.swift index 1cb30c64..2f48cbd5 100644 --- a/ImageSlideshow/Classes/InputSources/AFURLSource.swift +++ b/ImageSlideshow/Classes/InputSources/AFURLSource.swift @@ -46,7 +46,7 @@ public class AFURLSource: NSObject, InputSource { callback(placeholder) }) } - + public func cancelLoad(on imageView: UIImageView) { imageView.cancelImageDownloadTask() } diff --git a/ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift b/ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift new file mode 100644 index 00000000..4e4b5311 --- /dev/null +++ b/ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift @@ -0,0 +1,65 @@ +// +// AlamofireLegacySource.swift +// ImageSlideshow +// +// Created by Petr Zvoníček +// +// + +import UIKit +#if SWIFT_PACKAGE +import ImageSlideshow +#endif +import Alamofire +import AlamofireImage + +/// Input Source to image using Alamofire 3 +@objcMembers +public class AlamofireSource: NSObject, InputSource { + /// url to load + public var url: URL + + /// placeholder used before image is loaded + public var placeholder: UIImage? + + /// Initializes a new source with a URL + /// - parameter url: a url to load + /// - parameter placeholder: a placeholder used before image is loaded + public init(url: URL, placeholder: UIImage? = nil) { + self.url = url + self.placeholder = placeholder + super.init() + } + + /// Initializes a new source with a URL string + /// - parameter urlString: a string url to load + /// - parameter placeholder: a placeholder used before image is loaded + public init?(urlString: String, placeholder: UIImage? = nil) { + if let validUrl = URL(string: urlString) { + self.url = validUrl + self.placeholder = placeholder + super.init() + } else { + return nil + } + } + + public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + imageView.af_setImage(withURL: self.url, placeholderImage: placeholder, filter: nil, progress: nil) { [weak self] (response) in + switch response.result { + case .success(let image): + callback(image) + case .failure: + if let strongSelf = self { + callback(strongSelf.placeholder) + } else { + callback(nil) + } + } + } + } + + public func cancelLoad(on imageView: UIImageView) { + imageView.af_cancelImageRequest() + } +} diff --git a/ImageSlideshow/Classes/InputSources/AlamofireSource.swift b/ImageSlideshow/Classes/InputSources/AlamofireSource.swift index b7eed0cf..fcee148a 100644 --- a/ImageSlideshow/Classes/InputSources/AlamofireSource.swift +++ b/ImageSlideshow/Classes/InputSources/AlamofireSource.swift @@ -18,7 +18,7 @@ import AlamofireImage public class AlamofireSource: NSObject, InputSource { /// url to load public var url: URL - + /// placeholder used before image is loaded public var placeholder: UIImage? @@ -45,21 +45,17 @@ public class AlamofireSource: NSObject, InputSource { } public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { - imageView.af_setImage(withURL: self.url, placeholderImage: placeholder, filter: nil, progress: nil) { [weak self] (response) in + imageView.af.setImage(withURL: self.url, placeholderImage: placeholder, filter: nil, progress: nil) { [weak self] (response) in switch response.result { - case .success(let image): - callback(image) - case .failure(_): - if let strongSelf = self { - callback(strongSelf.placeholder) - } else { - callback(nil) - } + case .success(let image): + callback(image) + case .failure: + callback(self?.placeholder) + } } } - } public func cancelLoad(on imageView: UIImageView) { - imageView.af_cancelImageRequest() + imageView.af.cancelImageRequest() } } diff --git a/ImageSlideshow/Classes/InputSources/KingfisherSource.swift b/ImageSlideshow/Classes/InputSources/KingfisherSource.swift index 882b31ab..f02394d2 100644 --- a/ImageSlideshow/Classes/InputSources/KingfisherSource.swift +++ b/ImageSlideshow/Classes/InputSources/KingfisherSource.swift @@ -19,7 +19,7 @@ public class KingfisherSource: NSObject, InputSource { /// placeholder used before image is loaded public var placeholder: UIImage? - + /// options for displaying, ie. [.transition(.fade(0.2))] public var options: KingfisherOptionsInfo? @@ -48,7 +48,7 @@ public class KingfisherSource: NSObject, InputSource { return nil } } - + /// Load an image to an UIImageView /// /// - Parameters: @@ -61,11 +61,11 @@ public class KingfisherSource: NSObject, InputSource { case .success(let image): callback(image.image) case .failure: - callback(nil) + callback(self.placeholder) } } } - + /// Cancel an image download task /// /// - Parameter imageView: UIImage view with the download task that should be canceled diff --git a/ImageSlideshow/Classes/InputSources/ParseSource.swift b/ImageSlideshow/Classes/InputSources/ParseSource.swift index 78377f73..8e7acc22 100644 --- a/ImageSlideshow/Classes/InputSources/ParseSource.swift +++ b/ImageSlideshow/Classes/InputSources/ParseSource.swift @@ -23,7 +23,7 @@ public class ParseSource: NSObject, InputSource { @objc public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { imageView.image = self.placeholder - self.file.getDataInBackground {(data: Data?, error: Error?) in + self.file.getDataInBackground {(data: Data?, _: Error?) in if let data = data, let image = UIImage(data: data) { imageView.image = image callback(image) @@ -32,4 +32,4 @@ public class ParseSource: NSObject, InputSource { } } } -} \ No newline at end of file +} diff --git a/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift b/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift index f220169e..b1c189a9 100644 --- a/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift +++ b/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift @@ -48,7 +48,7 @@ public class SDWebImageSource: NSObject, InputSource { callback(image) }) } - + public func cancelLoad(on imageView: UIImageView) { imageView.sd_cancelCurrentImageLoad() } diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..28277c26 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,43 @@ +{ + "object": { + "pins": [ + { + "package": "Alamofire", + "repositoryURL": "https://github.com/Alamofire/Alamofire.git", + "state": { + "branch": null, + "revision": "fca036f7aeca07124067cb6e0c12b0ad6359e3d4", + "version": "5.1.0" + } + }, + { + "package": "AlamofireImage", + "repositoryURL": "https://github.com/Alamofire/AlamofireImage.git", + "state": { + "branch": null, + "revision": "3e8edbeb75227f8542aa87f90240cf0424d6362f", + "version": "4.1.0" + } + }, + { + "package": "Kingfisher", + "repositoryURL": "https://github.com/onevcat/Kingfisher.git", + "state": { + "branch": null, + "revision": "349ed06467a6f8a4939bcb83db301542bc84eac9", + "version": "5.13.4" + } + }, + { + "package": "SDWebImage", + "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", + "state": { + "branch": null, + "revision": "74ae21337c415bb542eb901803f3e84799265c34", + "version": "5.7.2" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index f27e97a8..3e4f65c9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -23,7 +23,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/onevcat/Kingfisher.git", from: "5.8.0"), - .package(url: "https://github.com/Alamofire/AlamofireImage.git", .branch("master")), + .package(url: "https://github.com/Alamofire/AlamofireImage.git", from: "4.0.0"), .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0") ], targets: [ @@ -32,6 +32,7 @@ let package = Package( path: "ImageSlideshow", sources: [ "Classes/Core/ActivityIndicator.swift", + "Classes/Core/Bundle+Module.swift", "Classes/Core/FullScreenSlideshowViewController.swift", "Classes/Core/ImageSlideshow.swift", "Classes/Core/ImageSlideshowItem.swift", @@ -42,8 +43,10 @@ let package = Package( "Classes/Core/UIImage+AspectFit.swift", "Classes/Core/UIImageView+Tools.swift", "Classes/Core/ZoomAnimatedTransitioning.swift", - "Assets/ic_cross_white@2x.png", - "Assets/ic_cross_white@3x.png", + ], + resources: [ + .copy("Assets/ic_cross_white@2x.png"), + .copy("Assets/ic_cross_white@3x.png"), ]), .target( name: "ImageSlideshowAlamofire", diff --git a/README.md b/README.md index 1a04b579..3364d073 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ImageSlideshow is available through [CocoaPods](http://cocoapods.org). To instal it, simply add the following line to your Podfile: ```ruby -pod 'ImageSlideshow', '~> 1.8.3' +pod 'ImageSlideshow', '~> 1.9.0' ``` ### Carthage To integrate ImageSlideshow into your Xcode project using Carthage, specify it in your Cartfile: ```ruby -github "zvonicek/ImageSlideshow" ~> 1.8.3 +github "zvonicek/ImageSlideshow" ~> 1.9.0 ``` Carthage does not include InputSources for external providers (due to dependency on those providers) so you need to grab the one you need from `ImageSlideshow/Classes/InputSources` manually.