├── .swift-version
├── ImageSlideshow
├── Assets
│ ├── .gitkeep
│ ├── ic_cross_white@2x.png
│ └── ic_cross_white@3x.png
└── Classes
│ ├── .gitkeep
│ ├── Core
│ ├── Bundle+Module.swift
│ ├── SwiftSupport.swift
│ ├── UIImage+AspectFit.swift
│ ├── UIImageView+Tools.swift
│ ├── ActivityIndicator.swift
│ ├── PageIndicator.swift
│ ├── InputSource.swift
│ ├── PageIndicatorPosition.swift
│ ├── FullScreenSlideshowViewController.swift
│ ├── ImageSlideshowItem.swift
│ ├── ZoomAnimatedTransitioning.swift
│ └── ImageSlideshow.swift
│ └── InputSources
│ ├── ParseSource.swift
│ ├── SDWebImageSource.swift
│ ├── AFURLSource.swift
│ ├── AlamofireSource.swift
│ ├── AlamofireLegacySource.swift
│ └── KingfisherSource.swift
├── _Pods.xcodeproj
├── Example
├── ImageSlideshow
│ ├── Images.xcassets
│ │ ├── Contents.json
│ │ ├── img1.imageset
│ │ │ ├── img1.png
│ │ │ └── Contents.json
│ │ ├── img2.imageset
│ │ │ ├── img2.png
│ │ │ └── Contents.json
│ │ ├── img3.imageset
│ │ │ ├── img3.png
│ │ │ └── Contents.json
│ │ ├── img4.imageset
│ │ │ ├── img4.png
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── TableViewController.swift
│ ├── ViewController.swift
│ └── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
├── ImageSlideshow.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ ├── ImageSlideshow_framework.xcscheme
│ │ │ └── ImageSlideshow-Example.xcscheme
│ └── project.pbxproj
├── ImageSlideshow.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Podfile
├── ImageSlideshow_framework
│ ├── ImageSlideshow_framework.h
│ └── Info.plist
├── Tests
│ ├── Info.plist
│ └── Tests.swift
└── Podfile.lock
├── .travis.yml
├── .gitignore
├── LICENSE
├── Package.resolved
├── Package.swift
├── ImageSlideshow.podspec
├── CHANGELOG.md
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.2
2 |
--------------------------------------------------------------------------------
/ImageSlideshow/Assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/_Pods.xcodeproj:
--------------------------------------------------------------------------------
1 | Example/Pods/Pods.xcodeproj
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ImageSlideshow/Assets/ic_cross_white@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/ImageSlideshow/Assets/ic_cross_white@2x.png
--------------------------------------------------------------------------------
/ImageSlideshow/Assets/ic_cross_white@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/ImageSlideshow/Assets/ic_cross_white@3x.png
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img1.imageset/img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/Example/ImageSlideshow/Images.xcassets/img1.imageset/img1.png
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img2.imageset/img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/Example/ImageSlideshow/Images.xcassets/img2.imageset/img2.png
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img3.imageset/img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/Example/ImageSlideshow/Images.xcassets/img3.imageset/img3.png
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img4.imageset/img4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zvonicek/ImageSlideshow/HEAD/Example/ImageSlideshow/Images.xcassets/img4.imageset/img4.png
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/Bundle+Module.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bundle+Module.swift
3 | // ImageSlideshow
4 | //
5 | // Created by woxtu on 20/11/21.
6 | //
7 |
8 | import Foundation
9 |
10 | #if !SWIFT_PACKAGE
11 | extension Bundle {
12 | static var module: Bundle = {
13 | return Bundle(for: ImageSlideshow.self)
14 | }()
15 | }
16 | #endif
17 |
--------------------------------------------------------------------------------
/Example/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '10.0'
2 | use_frameworks!
3 |
4 | target 'ImageSlideshow_Example' do
5 | pod "ImageSlideshow", :path => "../"
6 | pod "ImageSlideshow/AFURL", :path => "../"
7 | pod "ImageSlideshow/Alamofire", :path => "../"
8 | pod "ImageSlideshow/SDWebImage", :path => "../"
9 | pod "ImageSlideshow/Kingfisher", :path => "../"
10 | end
11 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "img1.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "img2.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "img3.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/img4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "img4.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/SwiftSupport.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftSupport.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Pierluigi Cifani on 30/07/2018.
6 | //
7 |
8 | import UIKit
9 |
10 | #if swift(>=4.2)
11 | public typealias UIViewContentMode = UIView.ContentMode
12 | public typealias UIActivityIndicatorViewStyle = UIActivityIndicatorView.Style
13 | typealias UIControlState = UIControl.State
14 | typealias UIViewAnimationOptions = UIView.AnimationOptions
15 | typealias UIControlEvents = UIControl.Event
16 | typealias UIViewAutoresizing = UIView.AutoresizingMask
17 | #else
18 | #endif
19 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * http://www.objc.io/issue-6/travis-ci.html
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | osx_image: xcode7.3
6 | language: objective-c
7 | cache: cocoapods
8 | podfile: Example/Podfile
9 | before_install:
10 | - gem install cocoapods # Since Travis is not always on latest version
11 | - pod install --project-directory=Example
12 | install:
13 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
14 | script:
15 | - set -e && xcodebuild -workspace -workspace Example/ImageSlideshow.xcworkspace -scheme ImageSlideshow-Example -sdk iphonesimulator8.1 test | xcpretty -c
16 | - pod lib lint --quick
17 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow_framework/ImageSlideshow_framework.h:
--------------------------------------------------------------------------------
1 | //
2 | // ImageSlideshow_framework.h
3 | // ImageSlideshow_framework
4 | //
5 | // Created by Petr Zvoníček on 25.09.16.
6 | // Copyright © 2016 CocoaPods. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for ImageSlideshow_framework.
12 | FOUNDATION_EXPORT double ImageSlideshow_frameworkVersionNumber;
13 |
14 | //! Project version string for ImageSlideshow_framework.
15 | FOUNDATION_EXPORT const unsigned char ImageSlideshow_frameworkVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # Bundler
23 | .bundle
24 |
25 | Carthage
26 | # We recommend against adding the Pods directory to your .gitignore. However
27 | # you should judge for yourself, the pros and cons are mentioned at:
28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
29 | #
30 | # Note: if you ignore the Pods directory, make sure to uncomment
31 | # `pod install` in .travis.yml
32 |
33 | Pods/
34 |
35 | # Swift Package Manager
36 | .build/
37 |
--------------------------------------------------------------------------------
/Example/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/Tests/Tests.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import XCTest
3 |
4 | class Tests: XCTestCase {
5 |
6 | override func setUp() {
7 | super.setUp()
8 | // Put setup code here. This method is called before the invocation of each test method in the class.
9 | }
10 |
11 | override func tearDown() {
12 | // Put teardown code here. This method is called after the invocation of each test method in the class.
13 | super.tearDown()
14 | }
15 |
16 | func testExample() {
17 | // This is an example of a functional test case.
18 | XCTAssert(true, "Pass")
19 | }
20 |
21 | func testPerformanceExample() {
22 | // This is an example of a performance test case.
23 | self.measure {
24 | // Put the code you want to measure the time of here.
25 | }
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow_framework/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/UIImage+AspectFit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+AspectFit.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 31.08.15.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 |
13 | func tgr_aspectFitRectForSize(_ size: CGSize) -> CGRect {
14 | let targetAspect: CGFloat = size.width / size.height
15 | let sourceAspect: CGFloat = self.size.width / self.size.height
16 | var rect: CGRect = CGRect.zero
17 |
18 | if targetAspect > sourceAspect {
19 | rect.size.height = size.height
20 | rect.size.width = ceil(rect.size.height * sourceAspect)
21 | rect.origin.x = ceil((size.width - rect.size.width) * 0.5)
22 | } else {
23 | rect.size.width = size.width
24 | rect.size.height = ceil(rect.size.width / sourceAspect)
25 | rect.origin.y = ceil((size.height - rect.size.height) * 0.5)
26 | }
27 |
28 | return rect
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | }
43 | ],
44 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Petr Zvoníček
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/ParseSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParseImageSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Jaime Agudo Lopez on 14/01/2017.
6 | //
7 | import Parse
8 |
9 | /// Input Source to image using Parse
10 | public class ParseSource: NSObject, InputSource {
11 | var file: PFFileObject
12 | var placeholder: UIImage?
13 |
14 | /// Initializes a new source with URL and optionally a placeholder
15 | /// - parameter url: a url to be loaded
16 | /// - parameter placeholder: a placeholder used before image is loaded
17 | public init(file: PFFileObject, placeholder: UIImage? = nil) {
18 | self.file = file
19 | self.placeholder = placeholder
20 | super.init()
21 | }
22 |
23 | @objc public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
24 | imageView.image = self.placeholder
25 |
26 | self.file.getDataInBackground {(data: Data?, _: Error?) in
27 | if let data = data, let image = UIImage(data: data) {
28 | imageView.image = image
29 | callback(image)
30 | } else {
31 | callback(nil)
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/UIImageView+Tools.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImageView+Tools.swift
3 | // Pods
4 | //
5 | // Created by Aleš Kocur on 20/04/16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImageView {
12 |
13 | func aspectToFitFrame() -> CGRect {
14 |
15 | guard let image = image else {
16 | assertionFailure("No image found!")
17 | return CGRect.zero
18 | }
19 |
20 | let imageRatio: CGFloat = image.size.width / image.size.height
21 | let viewRatio: CGFloat = frame.size.width / frame.size.height
22 |
23 | if imageRatio < viewRatio {
24 | let scale: CGFloat = frame.size.height / image.size.height
25 | let width: CGFloat = scale * image.size.width
26 | let topLeftX: CGFloat = (frame.size.width - width) * 0.5
27 | return CGRect(x: topLeftX, y: 0, width: width, height: frame.size.height)
28 | } else {
29 | let scale: CGFloat = frame.size.width / image.size.width
30 | let height: CGFloat = scale * image.size.height
31 | let topLeftY: CGFloat = (frame.size.height - height) * 0.5
32 | return CGRect(x: 0, y: topLeftY, width: frame.size.width, height: height)
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "Alamofire",
6 | "repositoryURL": "https://github.com/Alamofire/Alamofire.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "fca036f7aeca07124067cb6e0c12b0ad6359e3d4",
10 | "version": "5.1.0"
11 | }
12 | },
13 | {
14 | "package": "AlamofireImage",
15 | "repositoryURL": "https://github.com/Alamofire/AlamofireImage.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "3e8edbeb75227f8542aa87f90240cf0424d6362f",
19 | "version": "4.1.0"
20 | }
21 | },
22 | {
23 | "package": "Kingfisher",
24 | "repositoryURL": "https://github.com/onevcat/Kingfisher.git",
25 | "state": {
26 | "branch": null,
27 | "revision": "349ed06467a6f8a4939bcb83db301542bc84eac9",
28 | "version": "5.13.4"
29 | }
30 | },
31 | {
32 | "package": "SDWebImage",
33 | "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git",
34 | "state": {
35 | "branch": null,
36 | "revision": "74ae21337c415bb542eb901803f3e84799265c34",
37 | "version": "5.7.2"
38 | }
39 | }
40 | ]
41 | },
42 | "version": 1
43 | }
44 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SDWebImageSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Nik Kov on 06.07.16.
6 | //
7 | //
8 |
9 | import UIKit
10 | #if SWIFT_PACKAGE
11 | import ImageSlideshow
12 | #endif
13 | import SDWebImage
14 |
15 | /// Input Source to image using SDWebImage
16 | @objcMembers
17 | public class SDWebImageSource: NSObject, InputSource {
18 | /// url to load
19 | public var url: URL
20 |
21 | /// placeholder used before image is loaded
22 | public var placeholder: UIImage?
23 |
24 | /// Initializes a new source with a URL
25 | /// - parameter url: a url to be loaded
26 | /// - parameter placeholder: a placeholder used before image is loaded
27 | public init(url: URL, placeholder: UIImage? = nil) {
28 | self.url = url
29 | self.placeholder = placeholder
30 | super.init()
31 | }
32 |
33 | /// Initializes a new source with a URL string
34 | /// - parameter urlString: a string url to load
35 | /// - parameter placeholder: a placeholder used before image is loaded
36 | public init?(urlString: String, placeholder: UIImage? = nil) {
37 | if let validUrl = URL(string: urlString) {
38 | self.url = validUrl
39 | self.placeholder = placeholder
40 | super.init()
41 | } else {
42 | return nil
43 | }
44 | }
45 |
46 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
47 | imageView.sd_setImage(with: self.url, placeholderImage: self.placeholder, options: [], completed: { (image, _, _, _) in
48 | callback(image)
49 | })
50 | }
51 |
52 | public func cancelLoad(on imageView: UIImageView) {
53 | imageView.sd_cancelCurrentImageLoad()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/AFURLSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AFURLSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 30.07.15.
6 | //
7 |
8 | import UIKit
9 | import AFNetworking
10 |
11 | /// Input Source to image using AFNetworking
12 | @objcMembers
13 | public class AFURLSource: NSObject, InputSource {
14 | /// url to load
15 | public var url: URL
16 |
17 | /// placeholder used before image is loaded
18 | public var placeholder: UIImage?
19 |
20 | /// Initializes a new source with URL and placeholder
21 | /// - parameter url: a url to load
22 | /// - parameter placeholder: a placeholder used before image is loaded
23 | public init(url: URL, placeholder: UIImage? = nil) {
24 | self.url = url
25 | self.placeholder = placeholder
26 | super.init()
27 | }
28 |
29 | /// Initializes a new source with a URL string
30 | /// - parameter urlString: a string url to load
31 | /// - parameter placeholder: a placeholder used before image is loaded
32 | public init?(urlString: String, placeholder: UIImage? = nil) {
33 | if let validUrl = URL(string: urlString) {
34 | self.placeholder = placeholder
35 | self.url = validUrl
36 | super.init()
37 | } else {
38 | return nil
39 | }
40 | }
41 |
42 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
43 | imageView.setImageWith(URLRequest(url: url), placeholderImage: self.placeholder, success: { (_, _, image: UIImage) in
44 | callback(image)
45 | }, failure: {[placeholder = self.placeholder] _, _, _ in
46 | callback(placeholder)
47 | })
48 | }
49 |
50 | public func cancelLoad(on imageView: UIImageView) {
51 | imageView.cancelImageDownloadTask()
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/AlamofireSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlamofireSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 14.01.16.
6 | //
7 | //
8 |
9 | import UIKit
10 | #if SWIFT_PACKAGE
11 | import ImageSlideshow
12 | #endif
13 | import Alamofire
14 | import AlamofireImage
15 |
16 | /// Input Source to image using Alamofire
17 | @objcMembers
18 | public class AlamofireSource: NSObject, InputSource {
19 | /// url to load
20 | public var url: URL
21 |
22 | /// placeholder used before image is loaded
23 | public var placeholder: UIImage?
24 |
25 | /// Initializes a new source with a URL
26 | /// - parameter url: a url to load
27 | /// - parameter placeholder: a placeholder used before image is loaded
28 | public init(url: URL, placeholder: UIImage? = nil) {
29 | self.url = url
30 | self.placeholder = placeholder
31 | super.init()
32 | }
33 |
34 | /// Initializes a new source with a URL string
35 | /// - parameter urlString: a string url to load
36 | /// - parameter placeholder: a placeholder used before image is loaded
37 | public init?(urlString: String, placeholder: UIImage? = nil) {
38 | if let validUrl = URL(string: urlString) {
39 | self.url = validUrl
40 | self.placeholder = placeholder
41 | super.init()
42 | } else {
43 | return nil
44 | }
45 | }
46 |
47 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
48 | imageView.af.setImage(withURL: self.url, placeholderImage: placeholder, filter: nil, progress: nil) { [weak self] (response) in
49 | switch response.result {
50 | case .success(let image):
51 | callback(image)
52 | case .failure:
53 | callback(self?.placeholder)
54 | }
55 | }
56 | }
57 |
58 | public func cancelLoad(on imageView: UIImageView) {
59 | imageView.af.cancelImageRequest()
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlamofireLegacySource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček
6 | //
7 | //
8 |
9 | import UIKit
10 | #if SWIFT_PACKAGE
11 | import ImageSlideshow
12 | #endif
13 | import Alamofire
14 | import AlamofireImage
15 |
16 | /// Input Source to image using Alamofire 3
17 | @objcMembers
18 | public class AlamofireSource: NSObject, InputSource {
19 | /// url to load
20 | public var url: URL
21 |
22 | /// placeholder used before image is loaded
23 | public var placeholder: UIImage?
24 |
25 | /// Initializes a new source with a URL
26 | /// - parameter url: a url to load
27 | /// - parameter placeholder: a placeholder used before image is loaded
28 | public init(url: URL, placeholder: UIImage? = nil) {
29 | self.url = url
30 | self.placeholder = placeholder
31 | super.init()
32 | }
33 |
34 | /// Initializes a new source with a URL string
35 | /// - parameter urlString: a string url to load
36 | /// - parameter placeholder: a placeholder used before image is loaded
37 | public init?(urlString: String, placeholder: UIImage? = nil) {
38 | if let validUrl = URL(string: urlString) {
39 | self.url = validUrl
40 | self.placeholder = placeholder
41 | super.init()
42 | } else {
43 | return nil
44 | }
45 | }
46 |
47 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
48 | imageView.af_setImage(withURL: self.url, placeholderImage: placeholder, filter: nil, progress: nil) { [weak self] (response) in
49 | switch response.result {
50 | case .success(let image):
51 | callback(image)
52 | case .failure:
53 | if let strongSelf = self {
54 | callback(strongSelf.placeholder)
55 | } else {
56 | callback(nil)
57 | }
58 | }
59 | }
60 | }
61 |
62 | public func cancelLoad(on imageView: UIImageView) {
63 | imageView.af_cancelImageRequest()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/ActivityIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivityIndicator.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 01.05.17.
6 | //
7 |
8 | import UIKit
9 |
10 | /// Cusotm Activity Indicator can be used by implementing this protocol
11 | public protocol ActivityIndicatorView {
12 | /// View of the activity indicator
13 | var view: UIView { get }
14 |
15 | /// Show activity indicator
16 | func show()
17 |
18 | /// Hide activity indicator
19 | func hide()
20 | }
21 |
22 | /// Factory protocol to create new ActivityIndicatorViews. Meant to be implemented when creating custom activity indicator.
23 | public protocol ActivityIndicatorFactory {
24 | func create() -> ActivityIndicatorView
25 | }
26 |
27 | /// Default ActivityIndicatorView implementation for UIActivityIndicatorView
28 | extension UIActivityIndicatorView: ActivityIndicatorView {
29 | public var view: UIView {
30 | return self
31 | }
32 |
33 | public func show() {
34 | startAnimating()
35 | }
36 |
37 | public func hide() {
38 | stopAnimating()
39 | }
40 | }
41 |
42 | /// Default activity indicator factory creating UIActivityIndicatorView instances
43 | @objcMembers
44 | open class DefaultActivityIndicator: ActivityIndicatorFactory {
45 | /// activity indicator style
46 | open var style: UIActivityIndicatorViewStyle
47 |
48 | /// activity indicator color
49 | open var color: UIColor?
50 |
51 | /// Create a new ActivityIndicator for UIActivityIndicatorView
52 | ///
53 | /// - style: activity indicator style
54 | /// - color: activity indicator color
55 | public init(style: UIActivityIndicatorViewStyle = .gray, color: UIColor? = nil) {
56 | self.style = style
57 | self.color = color
58 | }
59 |
60 | /// create ActivityIndicatorView instance
61 | open func create() -> ActivityIndicatorView {
62 | #if swift(>=4.2)
63 | let activityIndicator = UIActivityIndicatorView(style: style)
64 | #else
65 | let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: style)
66 | #endif
67 | activityIndicator.color = color
68 | activityIndicator.hidesWhenStopped = true
69 |
70 | return activityIndicator
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 09/01/2015.
6 | // Copyright (c) 2015 Petr Zvoníček. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | private func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | func applicationWillResignActive(_ application: UIApplication) {
22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
24 | }
25 |
26 | func applicationDidEnterBackground(_ application: UIApplication) {
27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
29 | }
30 |
31 | func applicationWillEnterForeground(_ application: UIApplication) {
32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
33 | }
34 |
35 | func applicationDidBecomeActive(_ application: UIApplication) {
36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
37 | }
38 |
39 | func applicationWillTerminate(_ application: UIApplication) {
40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/Example/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFNetworking (3.2.1):
3 | - AFNetworking/NSURLSession (= 3.2.1)
4 | - AFNetworking/Reachability (= 3.2.1)
5 | - AFNetworking/Security (= 3.2.1)
6 | - AFNetworking/Serialization (= 3.2.1)
7 | - AFNetworking/UIKit (= 3.2.1)
8 | - AFNetworking/NSURLSession (3.2.1):
9 | - AFNetworking/Reachability
10 | - AFNetworking/Security
11 | - AFNetworking/Serialization
12 | - AFNetworking/Reachability (3.2.1)
13 | - AFNetworking/Security (3.2.1)
14 | - AFNetworking/Serialization (3.2.1)
15 | - AFNetworking/UIKit (3.2.1):
16 | - AFNetworking/NSURLSession
17 | - Alamofire (5.2.1)
18 | - AlamofireImage (4.1.0):
19 | - Alamofire (~> 5.1)
20 | - ImageSlideshow (1.9.0):
21 | - ImageSlideshow/Core (= 1.9.0)
22 | - ImageSlideshow/AFURL (1.9.0):
23 | - AFNetworking (~> 3.0)
24 | - ImageSlideshow/Core
25 | - ImageSlideshow/Alamofire (1.9.0):
26 | - AlamofireImage (~> 4.0)
27 | - ImageSlideshow/Core
28 | - ImageSlideshow/Core (1.9.0)
29 | - ImageSlideshow/Kingfisher (1.9.0):
30 | - ImageSlideshow/Core
31 | - Kingfisher (> 3.0)
32 | - ImageSlideshow/SDWebImage (1.9.0):
33 | - ImageSlideshow/Core
34 | - SDWebImage (>= 3.7)
35 | - Kingfisher (5.14.0):
36 | - Kingfisher/Core (= 5.14.0)
37 | - Kingfisher/Core (5.14.0)
38 | - SDWebImage (5.8.1):
39 | - SDWebImage/Core (= 5.8.1)
40 | - SDWebImage/Core (5.8.1)
41 |
42 | DEPENDENCIES:
43 | - ImageSlideshow (from `../`)
44 | - ImageSlideshow/AFURL (from `../`)
45 | - ImageSlideshow/Alamofire (from `../`)
46 | - ImageSlideshow/Kingfisher (from `../`)
47 | - ImageSlideshow/SDWebImage (from `../`)
48 |
49 | SPEC REPOS:
50 | trunk:
51 | - AFNetworking
52 | - Alamofire
53 | - AlamofireImage
54 | - Kingfisher
55 | - SDWebImage
56 |
57 | EXTERNAL SOURCES:
58 | ImageSlideshow:
59 | :path: "../"
60 |
61 | SPEC CHECKSUMS:
62 | AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057
63 | Alamofire: e911732990610fe89af59ac0077f923d72dc3dfd
64 | AlamofireImage: c4a2ba349885fb3064feb74d2e547bd42ce9be10
65 | ImageSlideshow: a90cc3568a325cdf1bd543732a2cfcf6361d0dc1
66 | Kingfisher: 7b64389a43139c903ec434788344c288217c792d
67 | SDWebImage: e3eae2eda88578db0685a0c88597fdadd9433f05
68 |
69 | PODFILE CHECKSUM: 46015634277c8c8bfd78d1b363e41ea2dfd179d9
70 |
71 | COCOAPODS: 1.9.3
72 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/InputSources/KingfisherSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KingfisherSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by feiin
6 | //
7 | //
8 |
9 | import UIKit
10 | #if SWIFT_PACKAGE
11 | import ImageSlideshow
12 | #endif
13 | import Kingfisher
14 |
15 | /// Input Source to image using Kingfisher
16 | public class KingfisherSource: NSObject, InputSource {
17 | /// url to load
18 | public var url: URL
19 |
20 | /// placeholder used before image is loaded
21 | public var placeholder: UIImage?
22 |
23 | /// options for displaying, ie. [.transition(.fade(0.2))]
24 | public var options: KingfisherOptionsInfo?
25 |
26 | /// Initializes a new source with a URL
27 | /// - parameter url: a url to be loaded
28 | /// - parameter placeholder: a placeholder used before image is loaded
29 | /// - parameter options: options for displaying
30 | public init(url: URL, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil) {
31 | self.url = url
32 | self.placeholder = placeholder
33 | self.options = options
34 | super.init()
35 | }
36 |
37 | /// Initializes a new source with a URL string
38 | /// - parameter urlString: a string url to load
39 | /// - parameter placeholder: a placeholder used before image is loaded
40 | /// - parameter options: options for displaying
41 | public init?(urlString: String, placeholder: UIImage? = nil, options: KingfisherOptionsInfo? = nil) {
42 | if let validUrl = URL(string: urlString) {
43 | self.url = validUrl
44 | self.placeholder = placeholder
45 | self.options = options
46 | super.init()
47 | } else {
48 | return nil
49 | }
50 | }
51 |
52 | /// Load an image to an UIImageView
53 | ///
54 | /// - Parameters:
55 | /// - imageView: UIImageView that receives the loaded image
56 | /// - callback: Completion callback with an optional image
57 | @objc
58 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
59 | imageView.kf.setImage(with: self.url, placeholder: self.placeholder, options: self.options, progressBlock: nil) { result in
60 | switch result {
61 | case .success(let image):
62 | callback(image.image)
63 | case .failure:
64 | callback(self.placeholder)
65 | }
66 | }
67 | }
68 |
69 | /// Cancel an image download task
70 | ///
71 | /// - Parameter imageView: UIImage view with the download task that should be canceled
72 | public func cancelLoad(on imageView: UIImageView) {
73 | imageView.kf.cancelDownloadTask()
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/TableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TableViewController.swift
3 | // ImageSlideshow_Example
4 | //
5 | // Created by Petr Zvoníček on 25.02.18.
6 | // Copyright © 2018 CocoaPods. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ImageSlideshow
11 |
12 | struct Model {
13 | let image: UIImage
14 | let title: String
15 |
16 | var inputSource: InputSource {
17 | return ImageSource(image: image)
18 | }
19 | }
20 |
21 | class TableViewController: UITableViewController {
22 |
23 | 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")]
24 |
25 | var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? = nil
26 |
27 | // MARK: - Table view data source
28 |
29 | override func numberOfSections(in tableView: UITableView) -> Int {
30 | return 1
31 | }
32 |
33 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
34 | return models.count
35 | }
36 |
37 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
38 | let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
39 | let model = models[indexPath.row]
40 | cell.imageView?.image = model.image
41 | cell.textLabel?.text = model.title
42 |
43 | return cell
44 | }
45 |
46 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
47 | tableView.deselectRow(at: indexPath, animated: true)
48 |
49 | let fullScreenController = FullScreenSlideshowViewController()
50 | fullScreenController.inputs = models.map { $0.inputSource }
51 | fullScreenController.initialPage = indexPath.row
52 |
53 | if let cell = tableView.cellForRow(at: indexPath), let imageView = cell.imageView {
54 | slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(imageView: imageView, slideshowController: fullScreenController)
55 | fullScreenController.modalPresentationStyle = .custom
56 | fullScreenController.transitioningDelegate = slideshowTransitioningDelegate
57 | }
58 |
59 | fullScreenController.slideshow.currentPageChanged = { [weak self] page in
60 | if let cell = tableView.cellForRow(at: IndexPath(row: page, section: 0)), let imageView = cell.imageView {
61 | self?.slideshowTransitioningDelegate?.referenceImageView = imageView
62 | }
63 | }
64 |
65 | present(fullScreenController, animated: true, completion: nil)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "ImageSlideshow",
7 | platforms: [
8 | .iOS(.v10),
9 | ],
10 | products: [
11 | .library(
12 | name: "ImageSlideshow",
13 | targets: ["ImageSlideshow"]),
14 | .library(
15 | name: "ImageSlideshow/Alamofire",
16 | targets: ["ImageSlideshowAlamofire"]),
17 | .library(
18 | name: "ImageSlideshow/SDWebImage",
19 | targets: ["ImageSlideshowSDWebImage"]),
20 | .library(
21 | name: "ImageSlideshow/Kingfisher",
22 | targets: ["ImageSlideshowKingfisher"])
23 | ],
24 | dependencies: [
25 | .package(url: "https://github.com/onevcat/Kingfisher.git", from: "5.8.0"),
26 | .package(url: "https://github.com/Alamofire/AlamofireImage.git", from: "4.0.0"),
27 | .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0")
28 | ],
29 | targets: [
30 | .target(
31 | name: "ImageSlideshow",
32 | path: "ImageSlideshow",
33 | sources: [
34 | "Classes/Core/ActivityIndicator.swift",
35 | "Classes/Core/Bundle+Module.swift",
36 | "Classes/Core/FullScreenSlideshowViewController.swift",
37 | "Classes/Core/ImageSlideshow.swift",
38 | "Classes/Core/ImageSlideshowItem.swift",
39 | "Classes/Core/InputSource.swift",
40 | "Classes/Core/PageIndicator.swift",
41 | "Classes/Core/PageIndicatorPosition.swift",
42 | "Classes/Core/SwiftSupport.swift",
43 | "Classes/Core/UIImage+AspectFit.swift",
44 | "Classes/Core/UIImageView+Tools.swift",
45 | "Classes/Core/ZoomAnimatedTransitioning.swift",
46 | ],
47 | resources: [
48 | .copy("Assets/ic_cross_white@2x.png"),
49 | .copy("Assets/ic_cross_white@3x.png"),
50 | ]),
51 | .target(
52 | name: "ImageSlideshowAlamofire",
53 | dependencies: ["ImageSlideshow", "AlamofireImage"],
54 | path: "ImageSlideshow/Classes/InputSources",
55 | sources: ["AlamofireSource.swift"]),
56 | .target(
57 | name: "ImageSlideshowSDWebImage",
58 | dependencies: ["ImageSlideshow", "SDWebImage"],
59 | path: "ImageSlideshow/Classes/InputSources",
60 | sources: ["SDWebImageSource.swift"]),
61 | .target(
62 | name: "ImageSlideshowKingfisher",
63 | dependencies: ["ImageSlideshow", "Kingfisher"],
64 | path: "ImageSlideshow/Classes/InputSources",
65 | sources: ["KingfisherSource.swift"])
66 | ],
67 | swiftLanguageVersions: [.v4, .v4_2, .v5]
68 | )
69 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/PageIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageIndicator.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 27.05.18.
6 | //
7 |
8 | import UIKit
9 |
10 | /// Cusotm Page Indicator can be used by implementing this protocol
11 | public protocol PageIndicatorView: class {
12 | /// View of the page indicator
13 | var view: UIView { get }
14 |
15 | /// Current page of the page indicator
16 | var page: Int { get set }
17 |
18 | /// Total number of pages of the page indicator
19 | var numberOfPages: Int { get set}
20 | }
21 |
22 | extension UIPageControl: PageIndicatorView {
23 | public var view: UIView {
24 | return self
25 | }
26 |
27 | public var page: Int {
28 | get {
29 | return currentPage
30 | }
31 | set {
32 | currentPage = newValue
33 | }
34 | }
35 |
36 | open override func sizeToFit() {
37 | var frame = self.frame
38 | frame.size = size(forNumberOfPages: numberOfPages)
39 | frame.size.height = 30
40 | self.frame = frame
41 | }
42 |
43 | public static func withSlideshowColors() -> UIPageControl {
44 | let pageControl = UIPageControl()
45 |
46 | if #available(iOS 13.0, *) {
47 | pageControl.currentPageIndicatorTintColor = UIColor { traits in
48 | traits.userInterfaceStyle == .dark ? .white : .lightGray
49 | }
50 | } else {
51 | pageControl.currentPageIndicatorTintColor = .lightGray
52 | }
53 |
54 | if #available(iOS 13.0, *) {
55 | pageControl.pageIndicatorTintColor = UIColor { traits in
56 | traits.userInterfaceStyle == .dark ? .systemGray : .black
57 | }
58 | } else {
59 | pageControl.pageIndicatorTintColor = .black
60 | }
61 |
62 | return pageControl
63 | }
64 | }
65 |
66 | /// Page indicator that shows page in numeric style, eg. "5/21"
67 | public class LabelPageIndicator: UILabel, PageIndicatorView {
68 | public var view: UIView {
69 | return self
70 | }
71 |
72 | public var numberOfPages: Int = 0 {
73 | didSet {
74 | updateLabel()
75 | }
76 | }
77 |
78 | public var page: Int = 0 {
79 | didSet {
80 | updateLabel()
81 | }
82 | }
83 |
84 | public override init(frame: CGRect) {
85 | super.init(frame: frame)
86 | initialize()
87 | }
88 |
89 | required public init?(coder aDecoder: NSCoder) {
90 | super.init(coder: aDecoder)
91 | initialize()
92 | }
93 |
94 | private func initialize() {
95 | self.textAlignment = .center
96 | }
97 |
98 | private func updateLabel() {
99 | text = "\(page+1)/\(numberOfPages)"
100 | }
101 |
102 | public override func sizeToFit() {
103 | let maximumString = String(repeating: "8", count: numberOfPages) as NSString
104 | self.frame.size = maximumString.size(withAttributes: [.font: font as Any])
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/InputSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InputSource.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 14.01.16.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | /// A protocol that can be adapted by different Input Source providers
12 | @objc public protocol InputSource {
13 | /**
14 | Load image from the source to image view.
15 | - parameter imageView: Image view to load the image into.
16 | - parameter callback: Callback called after image was set to the image view.
17 | - parameter image: Image that was set to the image view.
18 | */
19 | func load(to imageView: UIImageView, with callback: @escaping (_ image: UIImage?) -> Void)
20 |
21 | /**
22 | Cancel image load on the image view
23 | - parameter imageView: Image view that is loading the image
24 | */
25 | @objc optional func cancelLoad(on imageView: UIImageView)
26 | }
27 |
28 | /// Input Source to load plain UIImage
29 | @objcMembers
30 | open class ImageSource: NSObject, InputSource {
31 | var image: UIImage
32 |
33 | /// Initializes a new Image Source with UIImage
34 | /// - parameter image: Image to be loaded
35 | public init(image: UIImage) {
36 | self.image = image
37 | }
38 |
39 | /// Initializes a new Image Source with an image name from the main bundle
40 | /// - parameter imageString: name of the file in the application's main bundle
41 | @available(*, deprecated, message: "Use `BundleImageSource` instead")
42 | public init?(imageString: String) {
43 | if let image = UIImage(named: imageString) {
44 | self.image = image
45 | super.init()
46 | } else {
47 | return nil
48 | }
49 | }
50 |
51 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
52 | imageView.image = image
53 | callback(image)
54 | }
55 | }
56 |
57 | /// Input Source to load an image from the main bundle
58 | @objcMembers
59 | open class BundleImageSource: NSObject, InputSource {
60 | var imageString: String
61 |
62 | /// Initializes a new Image Source with an image name from the main bundle
63 | /// - parameter imageString: name of the file in the application's main bundle
64 | public init(imageString: String) {
65 | self.imageString = imageString
66 | super.init()
67 | }
68 |
69 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
70 | let image = UIImage(named: imageString)
71 | imageView.image = image
72 | callback(image)
73 | }
74 | }
75 |
76 | /// Input Source to load an image from a local file path
77 | @objcMembers
78 | open class FileImageSource: NSObject, InputSource {
79 | var path: String
80 |
81 | /// Initializes a new Image Source with an image name from the main bundle
82 | /// - parameter imageString: name of the file in the application's main bundle
83 | public init(path: String) {
84 | self.path = path
85 | super.init()
86 | }
87 |
88 | public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) {
89 | let image = UIImage(contentsOfFile: path)
90 | imageView.image = image
91 | callback(image)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow_framework.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/PageIndicatorPosition.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PageIndicator.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 04.02.18.
6 | //
7 |
8 | import UIKit
9 |
10 | /// Describes the configuration of the page indicator position
11 | public struct PageIndicatorPosition {
12 | public enum Horizontal {
13 | case left(padding: CGFloat), center, right(padding: CGFloat)
14 | }
15 |
16 | public enum Vertical {
17 | case top, bottom, under, customTop(padding: CGFloat), customBottom(padding: CGFloat), customUnder(padding: CGFloat)
18 | }
19 |
20 | /// Horizontal position of the page indicator
21 | var horizontal: Horizontal
22 |
23 | /// Vertical position of the page indicator
24 | var vertical: Vertical
25 |
26 | /// Creates a new PageIndicatorPosition struct
27 | ///
28 | /// - Parameters:
29 | /// - horizontal: horizontal position of the page indicator
30 | /// - vertical: vertical position of the page indicator
31 | public init(horizontal: Horizontal = .center, vertical: Vertical = .bottom) {
32 | self.horizontal = horizontal
33 | self.vertical = vertical
34 | }
35 |
36 | /// Computes the additional padding needed for the page indicator under the ImageSlideshow
37 | ///
38 | /// - Parameter indicatorSize: size of the page indicator
39 | /// - Returns: padding needed under the ImageSlideshow
40 | func underPadding(for indicatorSize: CGSize) -> CGFloat {
41 | switch vertical {
42 | case .under:
43 | return indicatorSize.height
44 | case .customUnder(let padding):
45 | return indicatorSize.height + padding
46 | default:
47 | return 0
48 | }
49 | }
50 |
51 | /// Computes the page indicator frame
52 | ///
53 | /// - Parameters:
54 | /// - parentFrame: frame of the parent view – ImageSlideshow
55 | /// - indicatorSize: size of the page indicator
56 | /// - edgeInsets: edge insets of the parent view – ImageSlideshow (used for SafeAreaInsets adjustment)
57 | /// - Returns: frame of the indicator by computing the origin and using `indicatorSize` as size
58 | func indicatorFrame(for parentFrame: CGRect, indicatorSize: CGSize, edgeInsets: UIEdgeInsets) -> CGRect {
59 | var xSize: CGFloat = 0
60 | var ySize: CGFloat = 0
61 |
62 | switch horizontal {
63 | case .center:
64 | xSize = parentFrame.size.width / 2 - indicatorSize.width / 2
65 | case .left(let padding):
66 | xSize = padding + edgeInsets.left
67 | case .right(let padding):
68 | xSize = parentFrame.size.width - indicatorSize.width - padding - edgeInsets.right
69 | }
70 |
71 | switch vertical {
72 | case .bottom, .under, .customUnder:
73 | ySize = parentFrame.size.height - indicatorSize.height - edgeInsets.bottom
74 | case .customBottom(let padding):
75 | ySize = parentFrame.size.height - indicatorSize.height - padding - edgeInsets.bottom
76 | case .top:
77 | ySize = edgeInsets.top
78 | case .customTop(let padding):
79 | ySize = padding + edgeInsets.top
80 | }
81 |
82 | return CGRect(x: xSize, y: ySize, width: indicatorSize.width, height: indicatorSize.height)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ImageSlideshow.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint ImageSlideshow.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = "ImageSlideshow"
11 | s.version = "1.9.2"
12 | s.summary = "Image slideshow written in Swift with circular scrolling, timer and full screen viewer"
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 | s.description = <<-DESC
20 | Image slideshow is a Swift library providing customizable image slideshow with circular scrolling, timer and full screen viewer and extendable image source (AFNetworking image source available in AFURL subspec).
21 | DESC
22 |
23 | s.homepage = "https://github.com/zvonicek/ImageSlideshow"
24 | s.screenshots = "https://dzwonsemrish7.cloudfront.net/items/2R06283n040V3P3p0i42/ezgif.com-optimize.gif"
25 | s.license = 'MIT'
26 | s.author = { "Petr Zvonicek" => "zvonicek@gmail.com" }
27 | s.source = { :git => "https://github.com/zvonicek/ImageSlideshow.git", :tag => s.version.to_s }
28 | s.social_media_url = 'https://twitter.com/zvonicek'
29 |
30 | s.swift_versions = ['4.0', '4.1', '4.2', '5', '5.1', '5.2']
31 | s.platform = :ios, '8.0'
32 | s.requires_arc = true
33 |
34 | s.subspec 'Core' do |core|
35 | core.source_files = 'ImageSlideshow/Classes/Core/**/*'
36 | core.resources = 'ImageSlideshow/Assets/*.png'
37 | end
38 |
39 | s.subspec 'AFURL' do |subspec|
40 | subspec.dependency 'ImageSlideshow/Core'
41 | subspec.dependency 'AFNetworking', '~> 3.0'
42 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/AFURLSource.swift'
43 | end
44 |
45 | s.subspec 'Alamofire3' do |subspec|
46 | subspec.dependency 'ImageSlideshow/Core'
47 | subspec.dependency 'AlamofireImage', '~> 3.0'
48 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/AlamofireLegacySource.swift'
49 | end
50 |
51 | s.subspec 'Alamofire' do |subspec|
52 | subspec.dependency 'ImageSlideshow/Core'
53 | subspec.dependency 'AlamofireImage', '~> 4.0'
54 | subspec.platform = :ios, '10.0'
55 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/AlamofireSource.swift'
56 | end
57 |
58 | s.subspec 'SDWebImage' do |subspec|
59 | subspec.dependency 'ImageSlideshow/Core'
60 | subspec.dependency 'SDWebImage', '>= 3.7'
61 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/SDWebImageSource.swift'
62 | end
63 |
64 | s.subspec 'Kingfisher' do |subspec|
65 | subspec.dependency 'ImageSlideshow/Core'
66 | subspec.dependency 'Kingfisher', '> 3.0'
67 | subspec.platform = :ios, '10.0'
68 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/KingfisherSource.swift'
69 | end
70 |
71 | s.subspec 'Parse' do |subspec|
72 | subspec.dependency 'ImageSlideshow/Core'
73 | subspec.dependency 'Parse', '~> 1.14'
74 | subspec.source_files = 'ImageSlideshow/Classes/InputSources/ParseSource.swift'
75 | end
76 |
77 | s.default_subspec = 'Core'
78 |
79 | end
80 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 30.07.15.
6 | // Copyright (c) 2015 Petr Zvonicek. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ImageSlideshow
11 |
12 | class ViewController: UIViewController {
13 |
14 | open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
15 | return .portrait
16 | }
17 |
18 | @IBOutlet var slideshow: ImageSlideshow!
19 |
20 | let localSource = [BundleImageSource(imageString: "img1"), BundleImageSource(imageString: "img2"), BundleImageSource(imageString: "img3"), BundleImageSource(imageString: "img4")]
21 | let afNetworkingSource = [AFURLSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080")!, AFURLSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!, AFURLSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
22 | let alamofireSource = [AlamofireSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080")!, AlamofireSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!, AlamofireSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
23 | let sdWebImageSource = [SDWebImageSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080")!, SDWebImageSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!, SDWebImageSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
24 | let kingfisherSource = [KingfisherSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080")!, KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!, KingfisherSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
25 |
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 | slideshow.slideshowInterval = 5.0
30 | slideshow.pageIndicatorPosition = .init(horizontal: .center, vertical: .under)
31 | slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill
32 |
33 | slideshow.pageIndicator = UIPageControl.withSlideshowColors()
34 |
35 | // optional way to show activity indicator during image load (skipping the line will show no activity indicator)
36 | slideshow.activityIndicator = DefaultActivityIndicator()
37 | slideshow.delegate = self
38 |
39 | // can be used with other sample sources as `afNetworkingSource`, `alamofireSource` or `sdWebImageSource` or `kingfisherSource`
40 | slideshow.setImageInputs(localSource)
41 |
42 | let recognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap))
43 | slideshow.addGestureRecognizer(recognizer)
44 | }
45 |
46 | @objc func didTap() {
47 | let fullScreenController = slideshow.presentFullScreenController(from: self)
48 | // set the activity indicator for full screen controller (skipping the line will show no activity indicator)
49 | fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
50 | }
51 | }
52 |
53 | extension ViewController: ImageSlideshowDelegate {
54 | func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int) {
55 | print("current page:", page)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FullScreenSlideshowViewController.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 31.08.15.
6 | //
7 |
8 | import UIKit
9 |
10 | @objcMembers
11 | open class FullScreenSlideshowViewController: UIViewController {
12 |
13 | open var slideshow: ImageSlideshow = {
14 | let slideshow = ImageSlideshow()
15 | slideshow.zoomEnabled = true
16 | slideshow.contentScaleMode = UIViewContentMode.scaleAspectFit
17 | slideshow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .center, vertical: .bottom)
18 | // turns off the timer
19 | slideshow.slideshowInterval = 0
20 | slideshow.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
21 |
22 | return slideshow
23 | }()
24 |
25 | /// Close button
26 | open var closeButton = UIButton()
27 |
28 | /// Close button frame
29 | open var closeButtonFrame: CGRect?
30 |
31 | /// Closure called on page selection
32 | open var pageSelected: ((_ page: Int) -> Void)?
33 |
34 | /// Index of initial image
35 | open var initialPage: Int = 0
36 |
37 | /// Input sources to
38 | open var inputs: [InputSource]?
39 |
40 | /// Background color
41 | open var backgroundColor = UIColor.black
42 |
43 | /// Enables/disable zoom
44 | open var zoomEnabled = true {
45 | didSet {
46 | slideshow.zoomEnabled = zoomEnabled
47 | }
48 | }
49 |
50 | fileprivate var isInit = true
51 |
52 | convenience init() {
53 | self.init(nibName: nil, bundle: nil)
54 |
55 | self.modalPresentationStyle = .custom
56 | if #available(iOS 13.0, *) {
57 | // Use KVC to set the value to preserve backwards compatiblity with Xcode < 11
58 | self.setValue(true, forKey: "modalInPresentation")
59 | }
60 | }
61 |
62 | override open func viewDidLoad() {
63 | super.viewDidLoad()
64 |
65 | view.backgroundColor = backgroundColor
66 | slideshow.backgroundColor = backgroundColor
67 |
68 | if let inputs = inputs {
69 | slideshow.setImageInputs(inputs)
70 | }
71 |
72 | view.addSubview(slideshow)
73 |
74 | // close button configuration
75 | closeButton.setImage(UIImage(named: "ic_cross_white", in: .module, compatibleWith: nil), for: UIControlState())
76 | closeButton.addTarget(self, action: #selector(FullScreenSlideshowViewController.close), for: UIControlEvents.touchUpInside)
77 | view.addSubview(closeButton)
78 | }
79 |
80 | override open var prefersStatusBarHidden: Bool {
81 | return true
82 | }
83 |
84 | override open func viewWillAppear(_ animated: Bool) {
85 | super.viewWillAppear(animated)
86 |
87 | if isInit {
88 | isInit = false
89 | slideshow.setCurrentPage(initialPage, animated: false)
90 | }
91 | }
92 |
93 | override open func viewWillDisappear(_ animated: Bool) {
94 | super.viewWillDisappear(animated)
95 |
96 | slideshow.slideshowItems.forEach { $0.cancelPendingLoad() }
97 |
98 | // Prevents broken dismiss transition when image is zoomed in
99 | slideshow.currentSlideshowItem?.zoomOut()
100 | }
101 |
102 | open override func viewDidLayoutSubviews() {
103 | if !isBeingDismissed {
104 | let safeAreaInsets: UIEdgeInsets
105 | if #available(iOS 11.0, *) {
106 | safeAreaInsets = view.safeAreaInsets
107 | } else {
108 | safeAreaInsets = UIEdgeInsets.zero
109 | }
110 |
111 | closeButton.frame = closeButtonFrame ?? CGRect(x: max(10, safeAreaInsets.left), y: max(10, safeAreaInsets.top), width: 40, height: 40)
112 | }
113 |
114 | slideshow.frame = view.frame
115 | }
116 |
117 | func close() {
118 | // if pageSelected closure set, send call it with current page
119 | if let pageSelected = pageSelected {
120 | pageSelected(slideshow.currentPage)
121 | }
122 |
123 | dismiss(animated: true, completion: nil)
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
97 |
99 |
105 |
106 |
107 |
108 |
110 |
111 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | ## [1.6.1](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.6.1) (11/06/2018)
4 |
5 | ## Fixes
6 |
7 | - Fixed Carthage build (#258)
8 | - Fixed memory issues (#255)
9 |
10 | ## [1.6.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.6.0) (27/05/2018)
11 |
12 | ## New Features
13 |
14 | - Page Indicator customization (#251)
15 |
16 | ## Fixes
17 |
18 | - Fixed animation problem on orientation change (#234)
19 | - Fixed missing close button image in Carthage build (#247)
20 |
21 | ## [1.5.3](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.5.3) (17/04/2018)
22 |
23 | ## Fixes
24 |
25 | - Fixed Close broken button in Full screen controller (#242)
26 | - Fixed retain cycle (#241, @piercifani)
27 |
28 | ## [1.5.2](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.5.2) (16/04/2018)
29 |
30 | ## New Features
31 |
32 | - Example project demonstrating usage of Image Slideshow with parent UIImageView
33 |
34 | ## Fixes
35 |
36 | - Fix image shift on iPhone X landscape (#200)
37 | - Add closeButtonFrame property to Full Screen controller (#226)
38 | - Don't trigger interactive gesture recognizer on horizontal pans, fixes single image sliding issue (#224)
39 | - Fix interactive dismiss regression on iOS 11
40 |
41 | ## [1.5.1](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.5.1) (04/02/2018)
42 |
43 | ## Fixes
44 | - Fix division by zero error (#223)
45 |
46 | ## [1.5.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.5.0) (21/01/2018)
47 |
48 | ## New Features
49 |
50 | - Implement image load cancelling to optimize memory usage
51 | - Improve Kingfisher InputSource to take Options parameters
52 | - Update page control selected page during scrolling (#204)
53 | - Add possibility to change maximum zoom scale (#221)
54 |
55 | ## Fixes
56 |
57 | - SDWebImage dependency improvements (#205)
58 | - Fix possible division by zero crash (#187)
59 | - Adjust close button frame to respect SafeAreaInsets (#209)
60 | - Fix missing placeholder on AFURLSource (#218)
61 | - Fix incorrect currentPageChanged calls (#222)
62 |
63 | ## [1.4.1](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.4.1) (04/10/2017)
64 |
65 | ## Fixes
66 |
67 | - iPhone X fixes
68 |
69 |
70 | ## [1.4.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.4.0) (23/09/2017)
71 |
72 | ## New Features
73 |
74 | - Support for Swift 4
75 |
76 | ## [1.3.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.3.0) (08/05/2017)
77 |
78 | ## New Features
79 |
80 | - Possibility to show activity indicator during async loading
81 | - Hide UIPageControl when sllideshow has single item
82 | - UIScrollViewDelegate methods are now `open` instead of `public`
83 |
84 | ## Fixes
85 |
86 | - Fix zoom transition for when a slideshow has just a single item
87 | - Fix issue on `zoomEnabled` change
88 |
89 |
90 | ## [1.2.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.2.0) (20/03/2017)
91 |
92 | ## New Features
93 |
94 | - Improved placeholder handling on all remote input sources
95 | - Deprecated `pauseTimerIfNeeded` and `unpauseTimerIfNeeded` in favour of `pauseTimer` and `unpauseTimer`
96 |
97 | ## Fixes
98 |
99 | - Fix memory leak caused by incorrect timer invalidation
100 | - Partially fix an UI glitch happening when "in-call" status bar is on
101 |
102 | ## [1.1.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.1.0) (19/02/2017)
103 |
104 | ## New Features
105 |
106 | - Add `willBeginDragging` and `didEndDecelerating` callback closures (@El-Fitz)
107 | - Add Parse input source (@jaimeagudo)
108 |
109 | ## Fixes
110 |
111 | - Fix image preload issue when scrolling between edges (#115)
112 | - Fix issue caused by disabling `circular` after setting input sources (#104)
113 | - Improve example project
114 | - Style fixes (@dogo)
115 |
116 | ## [1.0.0](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.0.0) (11/12/2016)
117 |
118 | Version 1.0 aims to improve stability and brings couple of new features. Also contains few backward complatibility breaking changes.
119 |
120 | ## New Features
121 | - New input source for Kingfisher (@feiin)
122 | - Add `currentPageChanged` closure to notify about page change (#54)
123 | - Add possibility to lazy load and release images (#42)
124 | - Easier way to present `FullScreenSlideshowViewController` (#72)
125 | - Documentation improvements
126 |
127 | ## Fixes
128 | - Fix the case when containing VC automatically adjusts scrollview insets (#71)
129 | - Fix crash during transition when no images set (#74)
130 | - Rounding error fix for page calculation (#90, @mjrehder)
131 | - Fix for black image when using fullscreen zoom (#92, @mjrehder)
132 | - Change page on UIPageControl page change (#80)
133 | - iOS 10 interactive transition interruption fix (5d5f22f)
134 | - Memory fixes
135 |
136 | ## API changes
137 | - `currentItemIndex` was renamed to `currentPage`
138 | - `set` function from `InputSource` protocol was renamed to `load` and have a new closure parameter called on image load
139 |
140 |
141 | ## [0.6](https://github.com/zvonicek/ImageSlideshow/releases/tag/0.6.0) (21/06/2016)
142 |
143 | Add support for Swift 2.3 and Carthage. Equivalent version *0.6-swift3* supports Swift 3.
144 |
145 | ## [0.5](https://github.com/zvonicek/ImageSlideshow/releases/tag/0.5.0) (09/06/2016)
146 |
147 | The version 0.5 cleans up the code, adds interactive fullscreen dismiss and fixes few minor issues (thanks again, @kafejo)
148 | This version also contains several background compatibility breaks, so please keep this in mind when upgrading to it.
149 |
150 | ### New Features
151 | - Interactive dismiss transition on full screen controller
152 | - A possibility to open full screen controller from plain UIImageView
153 |
154 | ### API changes
155 | - ImageSlideshow: `currentPage` renamed to `currentItemIndex`
156 | - FullScreenSlideshowViewController: `initialPage` renamed to `initialPageIndex`
157 | - ZoomAnimatedTransitioning: added second parameter `slideshowController: FullScreenSlideshowViewController` to constructor
158 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🖼 ImageSlideshow
2 |
3 | **Customizable Swift image slideshow with circular scrolling, timer and full screen viewer**
4 |
5 | [](https://www.bitrise.io/app/9aaf3e552f3a575c)
6 | [](http://cocoapods.org/pods/ImageSlideshow)
7 | [](https://github.com/Carthage/Carthage)
8 | [](http://cocoapods.org/pods/ImageSlideshow)
9 | [](http://cocoapods.org/pods/ImageSlideshow)
10 |
11 |
12 |
13 | 
14 |
15 | ## 📱 Example
16 |
17 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
18 |
19 | ## 🔧 Installation
20 |
21 | ### CocoaPods
22 | ImageSlideshow is available through [CocoaPods](http://cocoapods.org). To install
23 | it, simply add the following line to your Podfile:
24 |
25 | ```ruby
26 | pod 'ImageSlideshow', '~> 1.9.0'
27 | ```
28 |
29 | ### Carthage
30 | To integrate ImageSlideshow into your Xcode project using Carthage, specify it in your Cartfile:
31 |
32 | ```ruby
33 | github "zvonicek/ImageSlideshow" ~> 1.9.0
34 | ```
35 |
36 | 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.
37 |
38 | ### Manually
39 | One possibility is to download a built framework (ImageSlideshow.framework.zip) from [releases page](https://github.com/zvonicek/ImageSlideshow/releases/) and link it with your project (under`Linked Frameworks and Libraries` in your target). This is, however, currently problematic because of rapid Swift development -- the framework is built for a single Swift version and may not work on previous/future versions.
40 |
41 | Alternatively can also grab the whole `ImageSlideshow` directory and copy it to your project. Be sure to remove those external Input Sources you don't need.
42 |
43 | **Note on Swift 2.3, Swift 3 and Swift 4 support**
44 |
45 | Version 1.4 supports Swift 4. Swift 3 is supported from version 1.0, for Swift 2.2 and Swift 2.3 compatible code use version 0.6 or branch *swift-2.3*.
46 |
47 |
48 | ## 🔨 How to use
49 |
50 | Add ImageSlideshow view to your view hiearchy either in Interface Builder or in code.
51 |
52 | ### Loading images
53 |
54 | Set images by using ```setImageInputs``` method on ```ImageSlideshow``` instance with an array of *InputSource*s. By default you can use ```ImageSource``` which takes ```UIImage``` or few other *InputSource*s for most popular networking libraries. You can also create your own input source by implementing ```InputSource``` protocol.
55 |
56 | | Library | InputSource name | Pod |
57 | | ------------------------------------------------------------- |:----------------:| ---------------------------------:|
58 | | [AlamofireImage](https://github.com/Alamofire/AlamofireImage) | AlamofireSource | `pod "ImageSlideshow/Alamofire"` |
59 | | [AFNetworking](https://github.com/AFNetworking/AFNetworking) | AFURLSource | `pod "ImageSlideshow/AFURL"` |
60 | | [SDWebImage](https://github.com/rs/SDWebImage) | SDWebImageSource | `pod "ImageSlideshow/SDWebImage"` |
61 | | [Kingfisher](https://github.com/onevcat/Kingfisher) | KingfisherSource | `pod "ImageSlideshow/Kingfisher"` |
62 | | [Parse](https://github.com/ParsePlatform/Parse-SDK-iOS-OSX) | ParseSource | `pod "ImageSlideshow/Parse"` |
63 |
64 |
65 | ```swift
66 | slideshow.setImageInputs([
67 | ImageSource(image: UIImage(named: "myImage"))!,
68 | ImageSource(image: UIImage(named: "myImage2"))!,
69 | AlamofireSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080"),
70 | KingfisherSource(urlString: "https://images.unsplash.com/photo-1432679963831-2dab49187847?w=1080"),
71 | ParseSource(file: PFFile(name:"image.jpg", data:data))
72 | ])
73 | ```
74 |
75 | ### Configuration
76 |
77 | Behaviour is configurable by those properties:
78 |
79 | - ```slideshowInterval``` - slideshow interval in seconds (default `0` – disabled)
80 | - ```zoomEnabled``` - enables zooming (default `false`)
81 | - ```circular``` - enables circular scrolling (default `true`)
82 | - ```activityIndicator``` – allows to set custom activity indicator, see *Activity indicator* section
83 | - ```pageIndicator``` – allows to set custom page indicator, see *Page indicator* section; assign `nil` to hide page indicator
84 | - ```pageIndicatorPosition``` - configures position of the page indicator
85 | - ```contentScaleMode``` - configures the scaling (default `ScaleAspectFit`)
86 | - ```draggingEnabled``` - enables dragging (default `true`)
87 | - ```currentPageChanged``` - closure called on page change
88 | - ```willBeginDragging``` - closure called on scrollViewWillBeginDragging
89 | - ```didEndDecelerating``` - closure called on scrollViewDidEndDecelerating
90 | - ```preload``` - image preloading configuration (default `all` preloading, also `fixed`)
91 |
92 | ### Page Indicator
93 |
94 | Page indicator can be customized using the `pageIndicator` property on ImageSlideshow. By defualt, a plain UIPageControl is used. If needed, page control can be customized:
95 |
96 | ```swift
97 | let pageIndicator = UIPageControl()
98 | pageIndicator.currentPageIndicatorTintColor = UIColor.lightGray
99 | pageIndicator.pageIndicatorTintColor = UIColor.black
100 | slideshow.pageIndicator = pageIndicator
101 | ```
102 |
103 | Also, a simple label page indicator that shows pages in style "5/21" (fifth page from twenty one) is provided:
104 |
105 | ```swift
106 | slideshow.pageIndicator = LabelPageIndicator()
107 | ```
108 |
109 | You can also use your own page indicator by adopting the `PageIndicatorView` protocol.
110 |
111 | Position of the page indicator can be configured by assigning a `PageIndicatorPosition` value to the `pageIndicatorPosition` property on ImageSlideshow. You may specify the horizontal and vertical positioning separately.
112 |
113 | **Horizontal** positioning options are: `.left(padding: Int)`, `.center`, `.right(padding: Int)`
114 |
115 | **Vertical** positioning options are: `.top`, `.bottom`, `.under`, `customTop(padding: Int)`, `customBottom(padding: Int)`, `customUnder(padding: Int)`
116 |
117 | Example:
118 | ```swift
119 | slideshow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .left(padding: 20), vertical: .bottom)
120 | ```
121 |
122 |
123 | ### Activity Indicator
124 |
125 | By default activity indicator is not shown, but you can enable it by setting `DefaultActivityIndicator` instance to Image Slideshow:
126 |
127 | ```swift
128 | slideshow.activityIndicator = DefaultActivityIndicator()
129 | ```
130 |
131 | You can customize style and color of the indicator:
132 |
133 | ```swift
134 | slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
135 | ```
136 |
137 | There's also an option to use your own activity indicator. You just need to implement `ActivityIndicatorView` and `ActivityIndicatorFactory` protocols. See `ActivityIndicator.swift` for more information.
138 |
139 | ### Full Screen view
140 |
141 | There is also a possibility to open full-screen image view using attached `FullScreenSlideshowViewController`. The simplest way is to call:
142 |
143 | ```swift
144 | override func viewDidLoad() {
145 | let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap))
146 | slideshow.addGestureRecognizer(gestureRecognizer)
147 | }
148 |
149 | func didTap() {
150 | slideshow.presentFullScreenController(from: self)
151 | }
152 | ```
153 |
154 | `FullScreenSlideshowViewController` can also be instantiated and configured manually if more advanced behavior is needed.
155 |
156 | ## 👤 Author
157 |
158 | Petr Zvoníček
159 |
160 | ## 📄 License
161 |
162 | ImageSlideshow is available under the MIT license. See the LICENSE file for more info.
163 |
164 | ## 👀 References
165 |
166 | Inspired by projects:
167 | - https://github.com/gonzalezreal/Vertigo
168 | - https://github.com/kimar/KIImagePager
169 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ZoomablePhotoView.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 30.07.15.
6 | //
7 |
8 | import UIKit
9 |
10 | /// Used to wrap a single slideshow item and allow zooming on it
11 | @objcMembers
12 | open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate {
13 |
14 | /// Image view to hold the image
15 | public let imageView = UIImageView()
16 |
17 | /// Activity indicator shown during image loading, when nil there won't be shown any
18 | public let activityIndicator: ActivityIndicatorView?
19 |
20 | /// Input Source for the item
21 | public let image: InputSource
22 |
23 | /// Guesture recognizer to detect double tap to zoom
24 | open var gestureRecognizer: UITapGestureRecognizer?
25 |
26 | /// Holds if the zoom feature is enabled
27 | public let zoomEnabled: Bool
28 |
29 | /// If set to true image is initially zoomed in
30 | open var zoomInInitially = false
31 |
32 | /// Maximum zoom scale
33 | open var maximumScale: CGFloat = 2.0
34 |
35 | fileprivate var lastFrame = CGRect.zero
36 | fileprivate var imageReleased = false
37 | fileprivate var isLoading = false
38 | fileprivate var singleTapGestureRecognizer: UITapGestureRecognizer?
39 | fileprivate var loadFailed = false {
40 | didSet {
41 | singleTapGestureRecognizer?.isEnabled = loadFailed
42 | gestureRecognizer?.isEnabled = !loadFailed
43 | }
44 | }
45 |
46 | /// Wraps around ImageView so RTL transformation on it doesn't interfere with UIScrollView zooming
47 | private let imageViewWrapper = UIView()
48 |
49 | // MARK: - Life cycle
50 |
51 | /**
52 | Initializes a new ImageSlideshowItem
53 | - parameter image: Input Source to load the image
54 | - parameter zoomEnabled: holds if it should be possible to zoom-in the image
55 | */
56 | init(image: InputSource, zoomEnabled: Bool, activityIndicator: ActivityIndicatorView? = nil, maximumScale: CGFloat = 2.0) {
57 | self.zoomEnabled = zoomEnabled
58 | self.image = image
59 | self.activityIndicator = activityIndicator
60 | self.maximumScale = maximumScale
61 |
62 | super.init(frame: CGRect.null)
63 |
64 | imageViewWrapper.addSubview(imageView)
65 | imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
66 | imageView.isAccessibilityElement = true
67 | imageView.accessibilityTraits = .image
68 | if #available(iOS 11.0, *) {
69 | imageView.accessibilityIgnoresInvertColors = true
70 | }
71 |
72 | imageViewWrapper.clipsToBounds = true
73 | imageViewWrapper.isUserInteractionEnabled = true
74 | if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
75 | imageView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
76 | }
77 |
78 | setPictoCenter()
79 |
80 | // scroll view configuration
81 | delegate = self
82 | showsVerticalScrollIndicator = false
83 | showsHorizontalScrollIndicator = false
84 | addSubview(imageViewWrapper)
85 | minimumZoomScale = 1.0
86 | maximumZoomScale = calculateMaximumScale()
87 |
88 | if let activityIndicator = activityIndicator {
89 | addSubview(activityIndicator.view)
90 | }
91 |
92 | // tap gesture recognizer
93 | let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ImageSlideshowItem.tapZoom))
94 | tapRecognizer.numberOfTapsRequired = 2
95 | imageViewWrapper.addGestureRecognizer(tapRecognizer)
96 | gestureRecognizer = tapRecognizer
97 |
98 | singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(retryLoadImage))
99 | singleTapGestureRecognizer!.numberOfTapsRequired = 1
100 | singleTapGestureRecognizer!.isEnabled = false
101 | imageViewWrapper.addGestureRecognizer(singleTapGestureRecognizer!)
102 | }
103 |
104 | required public init?(coder aDecoder: NSCoder) {
105 | fatalError("init(coder:) has not been implemented")
106 | }
107 |
108 | override open func layoutSubviews() {
109 | super.layoutSubviews()
110 |
111 | if !zoomEnabled {
112 | imageViewWrapper.frame.size = frame.size
113 | } else if !isZoomed() {
114 | imageViewWrapper.frame.size = calculatePictureSize()
115 | }
116 |
117 | if isFullScreen() {
118 | clearContentInsets()
119 | } else {
120 | setPictoCenter()
121 | }
122 |
123 | self.activityIndicator?.view.center = imageViewWrapper.center
124 |
125 | // if self.frame was changed and zoomInInitially enabled, zoom in
126 | if lastFrame != frame && zoomInInitially {
127 | setZoomScale(maximumZoomScale, animated: false)
128 | }
129 |
130 | lastFrame = self.frame
131 |
132 | contentSize = imageViewWrapper.frame.size
133 | maximumZoomScale = calculateMaximumScale()
134 | }
135 |
136 | /// Request to load Image Source to Image View
137 | public func loadImage() {
138 | if self.imageView.image == nil && !isLoading {
139 | isLoading = true
140 | imageReleased = false
141 | activityIndicator?.show()
142 | image.load(to: self.imageView) {[weak self] image in
143 | // set image to nil if there was a release request during the image load
144 | if let imageRelease = self?.imageReleased, imageRelease {
145 | self?.imageView.image = nil
146 | } else {
147 | self?.imageView.image = image
148 | }
149 | self?.activityIndicator?.hide()
150 | self?.loadFailed = image == nil
151 | self?.isLoading = false
152 |
153 | self?.setNeedsLayout()
154 | }
155 | }
156 | }
157 |
158 | func releaseImage() {
159 | imageReleased = true
160 | cancelPendingLoad()
161 | self.imageView.image = nil
162 | }
163 |
164 | public func cancelPendingLoad() {
165 | image.cancelLoad?(on: imageView)
166 | }
167 |
168 | func retryLoadImage() {
169 | self.loadImage()
170 | }
171 |
172 | // MARK: - Image zoom & size
173 |
174 | func isZoomed() -> Bool {
175 | return self.zoomScale != self.minimumZoomScale
176 | }
177 |
178 | func zoomOut() {
179 | self.setZoomScale(minimumZoomScale, animated: false)
180 | }
181 |
182 | func tapZoom() {
183 | if isZoomed() {
184 | self.setZoomScale(minimumZoomScale, animated: true)
185 | } else {
186 | self.setZoomScale(maximumZoomScale, animated: true)
187 | }
188 | }
189 |
190 | fileprivate func screenSize() -> CGSize {
191 | return CGSize(width: frame.width, height: frame.height)
192 | }
193 |
194 | fileprivate func calculatePictureSize() -> CGSize {
195 | if let image = imageView.image, imageView.contentMode == .scaleAspectFit {
196 | let picSize = image.size
197 | let picRatio = picSize.width / picSize.height
198 | let screenRatio = screenSize().width / screenSize().height
199 |
200 | if picRatio > screenRatio {
201 | return CGSize(width: screenSize().width, height: screenSize().width / picSize.width * picSize.height)
202 | } else {
203 | return CGSize(width: screenSize().height / picSize.height * picSize.width, height: screenSize().height)
204 | }
205 | } else {
206 | return CGSize(width: screenSize().width, height: screenSize().height)
207 | }
208 | }
209 |
210 | fileprivate func calculateMaximumScale() -> CGFloat {
211 | return maximumScale
212 | }
213 |
214 | fileprivate func setPictoCenter() {
215 | var intendHorizon = (screenSize().width - imageViewWrapper.frame.width ) / 2
216 | var intendVertical = (screenSize().height - imageViewWrapper.frame.height ) / 2
217 | intendHorizon = intendHorizon > 0 ? intendHorizon : 0
218 | intendVertical = intendVertical > 0 ? intendVertical : 0
219 | contentInset = UIEdgeInsets(top: intendVertical, left: intendHorizon, bottom: intendVertical, right: intendHorizon)
220 | }
221 |
222 | private func isFullScreen() -> Bool {
223 | return imageViewWrapper.frame.width >= screenSize().width && imageViewWrapper.frame.height >= screenSize().height
224 | }
225 |
226 | func clearContentInsets() {
227 | contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
228 | }
229 |
230 | // MARK: UIScrollViewDelegate
231 |
232 | open func scrollViewDidZoom(_ scrollView: UIScrollView) {
233 | setPictoCenter()
234 | }
235 |
236 | open func viewForZooming(in scrollView: UIScrollView) -> UIView? {
237 | return zoomEnabled ? imageViewWrapper : nil
238 | }
239 |
240 | }
241 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ZoomAnimatedTransitioning.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 31.08.15.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | @objcMembers
12 | open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
13 | /// parent image view used for animated transition
14 | open var referenceImageView: UIImageView?
15 | /// parent slideshow view used for animated transition
16 | open weak var referenceSlideshowView: ImageSlideshow?
17 |
18 | // must be weak because FullScreenSlideshowViewController has strong reference to its transitioning delegate
19 | weak var referenceSlideshowController: FullScreenSlideshowViewController?
20 |
21 | var referenceSlideshowViewFrame: CGRect?
22 | var gestureRecognizer: UIPanGestureRecognizer!
23 | fileprivate var interactionController: UIPercentDrivenInteractiveTransition?
24 |
25 | /// Enables or disables swipe-to-dismiss interactive transition
26 | open var slideToDismissEnabled: Bool = true
27 |
28 | /**
29 | Init the transitioning delegate with a source ImageSlideshow
30 | - parameter slideshowView: ImageSlideshow instance to animate the transition from
31 | - parameter slideshowController: FullScreenViewController instance to animate the transition to
32 | */
33 | public init(slideshowView: ImageSlideshow, slideshowController: FullScreenSlideshowViewController) {
34 | self.referenceSlideshowView = slideshowView
35 | self.referenceSlideshowController = slideshowController
36 |
37 | super.init()
38 |
39 | initialize()
40 | }
41 |
42 | /**
43 | Init the transitioning delegate with a source ImageView
44 | - parameter imageView: UIImageView instance to animate the transition from
45 | - parameter slideshowController: FullScreenViewController instance to animate the transition to
46 | */
47 | public init(imageView: UIImageView, slideshowController: FullScreenSlideshowViewController) {
48 | self.referenceImageView = imageView
49 | self.referenceSlideshowController = slideshowController
50 |
51 | super.init()
52 |
53 | initialize()
54 | }
55 |
56 | func initialize() {
57 | // Pan gesture recognizer for interactive dismiss
58 | gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ZoomAnimatedTransitioningDelegate.handleSwipe(_:)))
59 | gestureRecognizer.delegate = self
60 | // Append it to a window otherwise it will be canceled during the transition
61 | UIApplication.shared.keyWindow?.addGestureRecognizer(gestureRecognizer)
62 | }
63 |
64 | func handleSwipe(_ gesture: UIPanGestureRecognizer) {
65 | guard let referenceSlideshowController = referenceSlideshowController else {
66 | return
67 | }
68 |
69 | let percent = min(max(abs(gesture.translation(in: gesture.view!).y) / 200.0, 0.0), 1.0)
70 |
71 | if gesture.state == .began {
72 | interactionController = UIPercentDrivenInteractiveTransition()
73 | referenceSlideshowController.dismiss(animated: true, completion: nil)
74 | } else if gesture.state == .changed {
75 | interactionController?.update(percent)
76 | } else if gesture.state == .ended || gesture.state == .cancelled || gesture.state == .failed {
77 | let velocity = gesture.velocity(in: referenceSlideshowView)
78 |
79 | if abs(velocity.y) > 500 {
80 | if let pageSelected = referenceSlideshowController.pageSelected {
81 | pageSelected(referenceSlideshowController.slideshow.currentPage)
82 | }
83 |
84 | interactionController?.finish()
85 | } else if percent > 0.5 {
86 | if let pageSelected = referenceSlideshowController.pageSelected {
87 | pageSelected(referenceSlideshowController.slideshow.currentPage)
88 | }
89 |
90 | interactionController?.finish()
91 | } else {
92 | interactionController?.cancel()
93 | }
94 |
95 | interactionController = nil
96 | }
97 | }
98 |
99 | open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
100 | if let reference = referenceSlideshowView {
101 | return ZoomInAnimator(referenceSlideshowView: reference, parent: self)
102 | } else if let reference = referenceImageView {
103 | return ZoomInAnimator(referenceImageView: reference, parent: self)
104 | } else {
105 | return nil
106 | }
107 | }
108 |
109 | open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
110 | if let reference = referenceSlideshowView {
111 | return ZoomOutAnimator(referenceSlideshowView: reference, parent: self)
112 | } else if let reference = referenceImageView {
113 | return ZoomOutAnimator(referenceImageView: reference, parent: self)
114 | } else {
115 | return nil
116 | }
117 | }
118 |
119 | open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
120 | return interactionController
121 | }
122 |
123 | open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
124 | return interactionController
125 | }
126 |
127 | public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
128 | return PresentationController(presentedViewController: presented, presenting: presenting)
129 | }
130 | }
131 |
132 | private class PresentationController: UIPresentationController {
133 | // Needed for interactive dismiss to keep the presenter View Controller visible
134 | override var shouldRemovePresentersView: Bool {
135 | return false
136 | }
137 | }
138 |
139 | extension ZoomAnimatedTransitioningDelegate: UIGestureRecognizerDelegate {
140 | public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
141 | guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else {
142 | return false
143 | }
144 |
145 | if !slideToDismissEnabled {
146 | return false
147 | }
148 |
149 | if let currentItem = referenceSlideshowController?.slideshow.currentSlideshowItem, currentItem.isZoomed() {
150 | return false
151 | }
152 |
153 | if let view = gestureRecognizer.view {
154 | let velocity = gestureRecognizer.velocity(in: view)
155 | return abs(velocity.x) < abs(velocity.y)
156 | }
157 |
158 | return true
159 | }
160 | }
161 |
162 | @objcMembers
163 | class ZoomAnimator: NSObject {
164 |
165 | var referenceImageView: UIImageView?
166 | var referenceSlideshowView: ImageSlideshow?
167 | var parent: ZoomAnimatedTransitioningDelegate
168 |
169 | init(referenceSlideshowView: ImageSlideshow, parent: ZoomAnimatedTransitioningDelegate) {
170 | self.referenceSlideshowView = referenceSlideshowView
171 | self.referenceImageView = referenceSlideshowView.currentSlideshowItem?.imageView
172 | self.parent = parent
173 | super.init()
174 | }
175 |
176 | init(referenceImageView: UIImageView, parent: ZoomAnimatedTransitioningDelegate) {
177 | self.referenceImageView = referenceImageView
178 | self.parent = parent
179 | super.init()
180 | }
181 | }
182 |
183 | @objcMembers
184 | class ZoomInAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning {
185 |
186 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
187 | return 0.5
188 | }
189 |
190 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
191 | // Pauses slideshow
192 | self.referenceSlideshowView?.pauseTimer()
193 |
194 | let containerView = transitionContext.containerView
195 | let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
196 |
197 | guard let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? FullScreenSlideshowViewController else {
198 | return
199 | }
200 |
201 | toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
202 |
203 | let transitionBackgroundView = UIView(frame: containerView.frame)
204 | transitionBackgroundView.backgroundColor = toViewController.backgroundColor
205 | containerView.addSubview(transitionBackgroundView)
206 |
207 | #if swift(>=4.2)
208 | containerView.sendSubviewToBack(transitionBackgroundView)
209 | #else
210 | containerView.sendSubview(toBack: transitionBackgroundView)
211 | #endif
212 |
213 | let finalFrame = toViewController.view.frame
214 |
215 | var transitionView: UIImageView?
216 | var transitionViewFinalFrame = finalFrame
217 | if let referenceImageView = referenceImageView {
218 | transitionView = UIImageView(image: referenceImageView.image)
219 | transitionView!.contentMode = UIViewContentMode.scaleAspectFill
220 | transitionView!.clipsToBounds = true
221 | transitionView!.frame = containerView.convert(referenceImageView.bounds, from: referenceImageView)
222 | containerView.addSubview(transitionView!)
223 | self.parent.referenceSlideshowViewFrame = transitionView!.frame
224 |
225 | referenceImageView.alpha = 0
226 |
227 | if let image = referenceImageView.image {
228 | transitionViewFinalFrame = image.tgr_aspectFitRectForSize(finalFrame.size)
229 | }
230 | }
231 |
232 | if let item = toViewController.slideshow.currentSlideshowItem, item.zoomInInitially {
233 | transitionViewFinalFrame.size = CGSize(width: transitionViewFinalFrame.size.width * item.maximumZoomScale, height: transitionViewFinalFrame.size.height * item.maximumZoomScale)
234 | }
235 |
236 | let duration: TimeInterval = transitionDuration(using: transitionContext)
237 |
238 | UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveLinear, animations: {
239 | fromViewController.view.alpha = 0
240 | transitionView?.frame = transitionViewFinalFrame
241 | transitionView?.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY)
242 | }, completion: {[ref = self.referenceImageView] _ in
243 | fromViewController.view.alpha = 1
244 | ref?.alpha = 1
245 | transitionView?.removeFromSuperview()
246 | transitionBackgroundView.removeFromSuperview()
247 | containerView.addSubview(toViewController.view)
248 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
249 | })
250 | }
251 | }
252 |
253 | class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning {
254 |
255 | private var animatorForCurrentTransition: UIViewImplicitlyAnimating?
256 |
257 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
258 | return 0.25
259 | }
260 |
261 | @available(iOS 10.0, *)
262 | func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
263 | // as per documentation, the same object should be returned for the ongoing transition
264 | if let animatorForCurrentSession = animatorForCurrentTransition {
265 | return animatorForCurrentSession
266 | }
267 |
268 | let params = animationParams(using: transitionContext)
269 |
270 | let animator = UIViewPropertyAnimator(duration: params.0, curve: .linear, animations: params.1)
271 | animator.addCompletion(params.2)
272 | animatorForCurrentTransition = animator
273 |
274 | return animator
275 | }
276 |
277 | private func animationParams(using transitionContext: UIViewControllerContextTransitioning) -> (TimeInterval, () -> Void, (Any) -> Void) {
278 | let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
279 |
280 | guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as? FullScreenSlideshowViewController else {
281 | fatalError("Transition not used with FullScreenSlideshowViewController")
282 | }
283 |
284 | let containerView = transitionContext.containerView
285 |
286 | var transitionViewInitialFrame: CGRect
287 | if let currentSlideshowItem = fromViewController.slideshow.currentSlideshowItem {
288 | if let image = currentSlideshowItem.imageView.image {
289 | transitionViewInitialFrame = image.tgr_aspectFitRectForSize(currentSlideshowItem.imageView.frame.size)
290 | } else {
291 | transitionViewInitialFrame = currentSlideshowItem.imageView.frame
292 | }
293 | transitionViewInitialFrame = containerView.convert(transitionViewInitialFrame, from: currentSlideshowItem)
294 | } else {
295 | transitionViewInitialFrame = fromViewController.slideshow.frame
296 | }
297 |
298 | var transitionViewFinalFrame: CGRect
299 | if let referenceImageView = referenceImageView {
300 | referenceImageView.alpha = 0
301 |
302 | let referenceSlideshowViewFrame = containerView.convert(referenceImageView.bounds, from: referenceImageView)
303 | transitionViewFinalFrame = referenceSlideshowViewFrame
304 |
305 | // do a frame scaling when AspectFit content mode enabled
306 | if fromViewController.slideshow.currentSlideshowItem?.imageView.image != nil && referenceImageView.contentMode == UIViewContentMode.scaleAspectFit {
307 | transitionViewFinalFrame = containerView.convert(referenceImageView.aspectToFitFrame(), from: referenceImageView)
308 | }
309 |
310 | // fixes the problem when the referenceSlideshowViewFrame was shifted during change of the status bar hidden state
311 | if UIApplication.shared.isStatusBarHidden && !toViewController.prefersStatusBarHidden && referenceSlideshowViewFrame.origin.y != parent.referenceSlideshowViewFrame?.origin.y {
312 | transitionViewFinalFrame = transitionViewFinalFrame.offsetBy(dx: 0, dy: 20)
313 | }
314 | } else {
315 | transitionViewFinalFrame = referenceSlideshowView?.frame ?? CGRect.zero
316 | }
317 |
318 | let transitionBackgroundView = UIView(frame: containerView.frame)
319 | transitionBackgroundView.backgroundColor = fromViewController.backgroundColor
320 | containerView.addSubview(transitionBackgroundView)
321 | #if swift(>=4.2)
322 | containerView.sendSubviewToBack(transitionBackgroundView)
323 | #else
324 | containerView.sendSubview(toBack: transitionBackgroundView)
325 | #endif
326 |
327 | let transitionView = UIImageView(image: fromViewController.slideshow.currentSlideshowItem?.imageView.image)
328 | transitionView.contentMode = UIViewContentMode.scaleAspectFill
329 | transitionView.clipsToBounds = true
330 | transitionView.frame = transitionViewInitialFrame
331 | containerView.addSubview(transitionView)
332 | fromViewController.view.isHidden = true
333 |
334 | let duration = transitionDuration(using: transitionContext)
335 | let animations = {
336 | transitionBackgroundView.alpha = 0
337 | transitionView.frame = transitionViewFinalFrame
338 | }
339 | let completion = { (_: Any) in
340 | let completed = !transitionContext.transitionWasCancelled
341 | self.referenceImageView?.alpha = 1
342 |
343 | if completed {
344 | fromViewController.view.removeFromSuperview()
345 | UIApplication.shared.keyWindow?.removeGestureRecognizer(self.parent.gestureRecognizer)
346 | // Unpauses slideshow
347 | self.referenceSlideshowView?.unpauseTimer()
348 | } else {
349 | fromViewController.view.isHidden = false
350 | }
351 |
352 | transitionView.removeFromSuperview()
353 | transitionBackgroundView.removeFromSuperview()
354 |
355 | self.animatorForCurrentTransition = nil
356 |
357 | transitionContext.completeTransition(completed)
358 | }
359 |
360 | return (duration, animations, completion)
361 | }
362 |
363 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
364 | // Working around iOS 10+ breaking change requiring to use UIPropertyAnimator for proper interactive transition instead of UIView.animate
365 | if #available(iOS 10.0, *) {
366 | interruptibleAnimator(using: transitionContext).startAnimation()
367 | } else {
368 | let params = animationParams(using: transitionContext)
369 | UIView.animate(withDuration: params.0, delay: 0, options: UIViewAnimationOptions(), animations: params.1, completion: params.2)
370 | }
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/ImageSlideshow/Classes/Core/ImageSlideshow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageSlideshow.swift
3 | // ImageSlideshow
4 | //
5 | // Created by Petr Zvoníček on 30.07.15.
6 | //
7 |
8 | import UIKit
9 |
10 | @objc
11 | /// The delegate protocol informing about image slideshow state changes
12 | public protocol ImageSlideshowDelegate: class {
13 | /// Tells the delegate that the current page has changed
14 | ///
15 | /// - Parameters:
16 | /// - imageSlideshow: image slideshow instance
17 | /// - page: new page
18 | @objc optional func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int)
19 |
20 | /// Tells the delegate that the slideshow will begin dragging
21 | ///
22 | /// - Parameter imageSlideshow: image slideshow instance
23 | @objc optional func imageSlideshowWillBeginDragging(_ imageSlideshow: ImageSlideshow)
24 |
25 | /// Tells the delegate that the slideshow did end decelerating
26 | ///
27 | /// - Parameter imageSlideshow: image slideshow instance
28 | @objc optional func imageSlideshowDidEndDecelerating(_ imageSlideshow: ImageSlideshow)
29 | }
30 |
31 | /**
32 | Used to represent position of the Page Control
33 | - hidden: Page Control is hidden
34 | - insideScrollView: Page Control is inside image slideshow
35 | - underScrollView: Page Control is under image slideshow
36 | - custom: Custom vertical padding, relative to "insideScrollView" position
37 | */
38 | public enum PageControlPosition {
39 | case hidden
40 | case insideScrollView
41 | case underScrollView
42 | case custom(padding: CGFloat)
43 | }
44 |
45 | /// Used to represent image preload strategy
46 | ///
47 | /// - fixed: preload only fixed number of images before and after the current image
48 | /// - all: preload all images in the slideshow
49 | public enum ImagePreload {
50 | case fixed(offset: Int)
51 | case all
52 | }
53 |
54 | /// Main view containing the Image Slideshow
55 | @objcMembers
56 | open class ImageSlideshow: UIView {
57 |
58 | /// Scroll View to wrap the slideshow
59 | public let scrollView = UIScrollView()
60 |
61 | /// Page Control shown in the slideshow
62 | @available(*, deprecated, message: "Use pageIndicator.view instead")
63 | open var pageControl: UIPageControl {
64 | if let pageIndicator = pageIndicator as? UIPageControl {
65 | return pageIndicator
66 | }
67 | fatalError("pageIndicator is not an instance of UIPageControl")
68 | }
69 |
70 | /// Activity indicator shown when loading image
71 | open var activityIndicator: ActivityIndicatorFactory? {
72 | didSet {
73 | reloadScrollView()
74 | }
75 | }
76 |
77 | open var pageIndicator: PageIndicatorView? {
78 | didSet {
79 | oldValue?.view.removeFromSuperview()
80 | if let pageIndicator = pageIndicator {
81 | addSubview(pageIndicator.view)
82 | if let pageIndicator = pageIndicator as? UIControl {
83 | pageIndicator.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged)
84 | }
85 | }
86 | setNeedsLayout()
87 | }
88 | }
89 |
90 | open var pageIndicatorPosition: PageIndicatorPosition = PageIndicatorPosition() {
91 | didSet {
92 | setNeedsLayout()
93 | }
94 | }
95 |
96 | // MARK: - State properties
97 |
98 | /// Page control position
99 | @available(*, deprecated, message: "Use pageIndicatorPosition instead")
100 | open var pageControlPosition = PageControlPosition.insideScrollView {
101 | didSet {
102 | pageIndicator = UIPageControl()
103 | switch pageControlPosition {
104 | case .hidden:
105 | pageIndicator = nil
106 | case .insideScrollView:
107 | pageIndicatorPosition = PageIndicatorPosition(vertical: .bottom)
108 | case .underScrollView:
109 | pageIndicatorPosition = PageIndicatorPosition(vertical: .under)
110 | case .custom(let padding):
111 | pageIndicatorPosition = PageIndicatorPosition(vertical: .customUnder(padding: padding-30))
112 | }
113 | }
114 | }
115 |
116 | /// Current page
117 | open fileprivate(set) var currentPage: Int = 0 {
118 | didSet {
119 | if oldValue != currentPage {
120 | pageIndicator?.page = currentPage
121 | currentPageChanged?(currentPage)
122 | delegate?.imageSlideshow?(self, didChangeCurrentPageTo: currentPage)
123 | }
124 | }
125 | }
126 |
127 | /// Delegate called on image slideshow state change
128 | open weak var delegate: ImageSlideshowDelegate?
129 |
130 | /// Called on each currentPage change
131 | open var currentPageChanged: ((_ page: Int) -> Void)?
132 |
133 | /// Called on scrollViewWillBeginDragging
134 | open var willBeginDragging: (() -> Void)?
135 |
136 | /// Called on scrollViewDidEndDecelerating
137 | open var didEndDecelerating: (() -> Void)?
138 |
139 | /// Currenlty displayed slideshow item
140 | open var currentSlideshowItem: ImageSlideshowItem? {
141 | if slideshowItems.count > scrollViewPage {
142 | return slideshowItems[scrollViewPage]
143 | } else {
144 | return nil
145 | }
146 | }
147 |
148 | /// 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.
149 | open fileprivate(set) var scrollViewPage: Int = 0
150 |
151 | /// Input Sources loaded to slideshow
152 | open fileprivate(set) var images = [InputSource]()
153 |
154 | /// Image Slideshow Items loaded to slideshow
155 | open fileprivate(set) var slideshowItems = [ImageSlideshowItem]()
156 |
157 | // MARK: - Preferences
158 |
159 | /// Enables/disables infinite scrolling between images
160 | open var circular = true {
161 | didSet {
162 | if images.count > 0 {
163 | setImageInputs(images)
164 | }
165 | }
166 | }
167 |
168 | /// Enables/disables user interactions
169 | open var draggingEnabled = true {
170 | didSet {
171 | scrollView.isUserInteractionEnabled = draggingEnabled
172 | }
173 | }
174 |
175 | /// Enables/disables zoom
176 | open var zoomEnabled = false {
177 | didSet {
178 | reloadScrollView()
179 | }
180 | }
181 |
182 | /// Maximum zoom scale
183 | open var maximumScale: CGFloat = 2.0 {
184 | didSet {
185 | reloadScrollView()
186 | }
187 | }
188 |
189 | /// Image change interval, zero stops the auto-scrolling
190 | open var slideshowInterval = 0.0 {
191 | didSet {
192 | slideshowTimer?.invalidate()
193 | slideshowTimer = nil
194 | setTimerIfNeeded()
195 | }
196 | }
197 |
198 | /// Image preload configuration, can be sed to .fixed to enable lazy load or .all
199 | open var preload = ImagePreload.all
200 |
201 | /// Content mode of each image in the slideshow
202 | open var contentScaleMode: UIViewContentMode = UIViewContentMode.scaleAspectFit {
203 | didSet {
204 | for view in slideshowItems {
205 | view.imageView.contentMode = contentScaleMode
206 | }
207 | }
208 | }
209 |
210 | fileprivate var slideshowTimer: Timer?
211 | fileprivate var scrollViewImages = [InputSource]()
212 | fileprivate var isAnimating: Bool = false
213 |
214 | /// Transitioning delegate to manage the transition to full screen controller
215 | open fileprivate(set) var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? // swiftlint:disable:this weak_delegate
216 |
217 | private var primaryVisiblePage: Int {
218 | return scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x + scrollView.frame.size.width / 2) / Int(scrollView.frame.size.width) : 0
219 | }
220 |
221 | // MARK: - Life cycle
222 |
223 | override public init(frame: CGRect) {
224 | super.init(frame: frame)
225 | initialize()
226 | }
227 |
228 | convenience init() {
229 | self.init(frame: CGRect.zero)
230 | }
231 |
232 | required public init?(coder aDecoder: NSCoder) {
233 | super.init(coder: aDecoder)
234 | initialize()
235 | }
236 |
237 | fileprivate func initialize() {
238 | autoresizesSubviews = true
239 | clipsToBounds = true
240 | if #available(iOS 13.0, *) {
241 | backgroundColor = .systemBackground
242 | }
243 |
244 | // scroll view configuration
245 | scrollView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height - 50.0)
246 | scrollView.delegate = self
247 | scrollView.isPagingEnabled = true
248 | scrollView.bounces = true
249 | scrollView.showsHorizontalScrollIndicator = false
250 | scrollView.showsVerticalScrollIndicator = false
251 | scrollView.autoresizingMask = autoresizingMask
252 | if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft {
253 | scrollView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
254 | }
255 |
256 | if #available(iOS 11.0, *) {
257 | scrollView.contentInsetAdjustmentBehavior = .never
258 | }
259 | addSubview(scrollView)
260 |
261 | if pageIndicator == nil {
262 | pageIndicator = UIPageControl()
263 | }
264 |
265 | setTimerIfNeeded()
266 | layoutScrollView()
267 | }
268 |
269 | open override func removeFromSuperview() {
270 | super.removeFromSuperview()
271 | pauseTimer()
272 | }
273 |
274 | open override func layoutSubviews() {
275 | super.layoutSubviews()
276 |
277 | // fixes the case when automaticallyAdjustsScrollViewInsets on parenting view controller is set to true
278 | scrollView.contentInset = UIEdgeInsets.zero
279 |
280 | layoutPageControl()
281 | layoutScrollView()
282 | }
283 |
284 | open func layoutPageControl() {
285 | if let pageIndicatorView = pageIndicator?.view {
286 | pageIndicatorView.isHidden = images.count < 2
287 |
288 | var edgeInsets: UIEdgeInsets = UIEdgeInsets.zero
289 | if #available(iOS 11.0, *) {
290 | edgeInsets = safeAreaInsets
291 | }
292 |
293 | pageIndicatorView.sizeToFit()
294 | pageIndicatorView.frame = pageIndicatorPosition.indicatorFrame(for: frame, indicatorSize: pageIndicatorView.frame.size, edgeInsets: edgeInsets)
295 | }
296 | }
297 |
298 | /// updates frame of the scroll view and its inner items
299 | func layoutScrollView() {
300 | let pageIndicatorViewSize = pageIndicator?.view.frame.size
301 | let scrollViewBottomPadding = pageIndicatorViewSize.flatMap { pageIndicatorPosition.underPadding(for: $0) } ?? 0
302 |
303 | scrollView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height - scrollViewBottomPadding)
304 | scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(scrollViewImages.count), height: scrollView.frame.size.height)
305 |
306 | for (index, view) in slideshowItems.enumerated() {
307 | if !view.zoomInInitially {
308 | view.zoomOut()
309 | }
310 | view.frame = CGRect(x: scrollView.frame.size.width * CGFloat(index), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height)
311 | }
312 |
313 | setScrollViewPage(scrollViewPage, animated: false)
314 | }
315 |
316 | /// reloads scroll view with latest slideshow items
317 | func reloadScrollView() {
318 | // remove previous slideshow items
319 | for view in slideshowItems {
320 | view.removeFromSuperview()
321 | }
322 | slideshowItems = []
323 |
324 | var i = 0
325 | for image in scrollViewImages {
326 | let item = ImageSlideshowItem(image: image, zoomEnabled: zoomEnabled, activityIndicator: activityIndicator?.create(), maximumScale: maximumScale)
327 | item.imageView.contentMode = contentScaleMode
328 | slideshowItems.append(item)
329 | scrollView.addSubview(item)
330 | i += 1
331 | }
332 |
333 | if circular && (scrollViewImages.count > 1) {
334 | scrollViewPage = 1
335 | scrollView.scrollRectToVisible(CGRect(x: scrollView.frame.size.width, y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height), animated: false)
336 | } else {
337 | scrollViewPage = 0
338 | }
339 |
340 | loadImages(for: scrollViewPage)
341 | }
342 |
343 | private func loadImages(for scrollViewPage: Int) {
344 | let totalCount = slideshowItems.count
345 |
346 | for i in 0.. totalCount-offset || circularEdgeLoad
357 | shouldLoad ? item.loadImage() : item.releaseImage()
358 | }
359 | }
360 | }
361 |
362 | // MARK: - Image setting
363 |
364 | /**
365 | Set image inputs into the image slideshow
366 | - parameter inputs: Array of InputSource instances.
367 | */
368 | open func setImageInputs(_ inputs: [InputSource]) {
369 | images = inputs
370 | pageIndicator?.numberOfPages = inputs.count
371 |
372 | // in circular mode we add dummy first and last image to enable smooth scrolling
373 | if circular && images.count > 1 {
374 | var scImages = [InputSource]()
375 |
376 | if let last = images.last {
377 | scImages.append(last)
378 | }
379 | scImages += images
380 | if let first = images.first {
381 | scImages.append(first)
382 | }
383 |
384 | scrollViewImages = scImages
385 | } else {
386 | scrollViewImages = images
387 | }
388 |
389 | reloadScrollView()
390 | layoutScrollView()
391 | layoutPageControl()
392 | setTimerIfNeeded()
393 | }
394 |
395 | // MARK: paging methods
396 |
397 | /**
398 | Change the current page
399 | - parameter newPage: new page
400 | - parameter animated: true if animate the change
401 | */
402 | open func setCurrentPage(_ newPage: Int, animated: Bool) {
403 | var pageOffset = newPage
404 | if circular && (scrollViewImages.count > 1) {
405 | pageOffset += 1
406 | }
407 |
408 | setScrollViewPage(pageOffset, animated: animated)
409 | }
410 |
411 | /**
412 | Change the scroll view page. This may differ from `setCurrentPage` as circular slider has two more dummy pages at indexes 0 and n-1 to provide fluent scrolling between first and last item.
413 | - parameter newScrollViewPage: new scroll view page
414 | - parameter animated: true if animate the change
415 | */
416 | open func setScrollViewPage(_ newScrollViewPage: Int, animated: Bool) {
417 | if scrollViewPage < scrollViewImages.count {
418 | scrollView.scrollRectToVisible(CGRect(x: scrollView.frame.size.width * CGFloat(newScrollViewPage), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height), animated: animated)
419 | setCurrentPageForScrollViewPage(newScrollViewPage)
420 | if animated {
421 | isAnimating = true
422 | }
423 | }
424 | }
425 |
426 | fileprivate func setTimerIfNeeded() {
427 | if slideshowInterval > 0 && scrollViewImages.count > 1 && slideshowTimer == nil {
428 | slideshowTimer = Timer.scheduledTimer(timeInterval: slideshowInterval, target: self, selector: #selector(ImageSlideshow.slideshowTick(_:)), userInfo: nil, repeats: true)
429 | }
430 | }
431 |
432 | func slideshowTick(_ timer: Timer) {
433 | let page = scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x / scrollView.frame.size.width) : 0
434 | var nextPage = page + 1
435 |
436 | if !circular && page == scrollViewImages.count - 1 {
437 | nextPage = 0
438 | }
439 |
440 | setScrollViewPage(nextPage, animated: true)
441 | }
442 |
443 | fileprivate func setCurrentPageForScrollViewPage(_ page: Int) {
444 | if scrollViewPage != page {
445 | // current page has changed, zoom out this image
446 | if slideshowItems.count > scrollViewPage {
447 | slideshowItems[scrollViewPage].zoomOut()
448 | }
449 | }
450 |
451 | if page != scrollViewPage {
452 | loadImages(for: page)
453 | }
454 | scrollViewPage = page
455 | currentPage = currentPageForScrollViewPage(page)
456 | }
457 |
458 | fileprivate func currentPageForScrollViewPage(_ page: Int) -> Int {
459 | if circular {
460 | if page == 0 {
461 | // first page contains the last image
462 | return Int(images.count) - 1
463 | } else if page == scrollViewImages.count - 1 {
464 | // last page contains the first image
465 | return 0
466 | } else {
467 | return page - 1
468 | }
469 | } else {
470 | return page
471 | }
472 | }
473 |
474 | fileprivate func restartTimer() {
475 | if slideshowTimer?.isValid != nil {
476 | slideshowTimer?.invalidate()
477 | slideshowTimer = nil
478 | }
479 |
480 | setTimerIfNeeded()
481 | }
482 |
483 | /// Stops slideshow timer
484 | open func pauseTimer() {
485 | slideshowTimer?.invalidate()
486 | slideshowTimer = nil
487 | }
488 |
489 | /// Restarts slideshow timer
490 | open func unpauseTimer() {
491 | setTimerIfNeeded()
492 | }
493 |
494 | @available(*, deprecated, message: "use pauseTimer instead")
495 | open func pauseTimerIfNeeded() {
496 | pauseTimer()
497 | }
498 |
499 | @available(*, deprecated, message: "use unpauseTimer instead")
500 | open func unpauseTimerIfNeeded() {
501 | unpauseTimer()
502 | }
503 |
504 | /**
505 | Change the page to the next one
506 | - Parameter animated: true if animate the change
507 | */
508 | open func nextPage(animated: Bool) {
509 | if !circular && currentPage == images.count - 1 {
510 | return
511 | }
512 | if isAnimating {
513 | return
514 | }
515 |
516 | setCurrentPage(currentPage + 1, animated: animated)
517 | restartTimer()
518 | }
519 |
520 | /**
521 | Change the page to the previous one
522 | - Parameter animated: true if animate the change
523 | */
524 | open func previousPage(animated: Bool) {
525 | if !circular && currentPage == 0 {
526 | return
527 | }
528 | if isAnimating {
529 | return
530 | }
531 |
532 | let newPage = scrollViewPage > 0 ? scrollViewPage - 1 : scrollViewImages.count - 3
533 | setScrollViewPage(newPage, animated: animated)
534 | restartTimer()
535 | }
536 |
537 | /**
538 | Open full screen slideshow
539 | - parameter controller: Controller to present the full screen controller from
540 | - returns: FullScreenSlideshowViewController instance
541 | */
542 | @discardableResult
543 | open func presentFullScreenController(from controller: UIViewController, completion: (() -> Void)? = nil) -> FullScreenSlideshowViewController {
544 | let fullscreen = FullScreenSlideshowViewController()
545 | fullscreen.pageSelected = {[weak self] (page: Int) in
546 | self?.setCurrentPage(page, animated: false)
547 | }
548 |
549 | fullscreen.initialPage = currentPage
550 | fullscreen.inputs = images
551 | slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(slideshowView: self, slideshowController: fullscreen)
552 | fullscreen.transitioningDelegate = slideshowTransitioningDelegate
553 | fullscreen.modalPresentationStyle = .custom
554 | controller.present(fullscreen, animated: true, completion: completion)
555 |
556 | return fullscreen
557 | }
558 |
559 | @objc private func pageControlValueChanged() {
560 | if let currentPage = pageIndicator?.page {
561 | setCurrentPage(currentPage, animated: true)
562 | }
563 | }
564 | }
565 |
566 | extension ImageSlideshow: UIScrollViewDelegate {
567 |
568 | open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
569 | restartTimer()
570 | willBeginDragging?()
571 | delegate?.imageSlideshowWillBeginDragging?(self)
572 | }
573 |
574 | open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
575 | setCurrentPageForScrollViewPage(primaryVisiblePage)
576 | didEndDecelerating?()
577 | delegate?.imageSlideshowDidEndDecelerating?(self)
578 | }
579 |
580 | open func scrollViewDidScroll(_ scrollView: UIScrollView) {
581 | if circular && (scrollViewImages.count > 1) {
582 | let regularContentOffset = scrollView.frame.size.width * CGFloat(images.count)
583 |
584 | if scrollView.contentOffset.x >= scrollView.frame.size.width * CGFloat(images.count + 1) {
585 | scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x - regularContentOffset, y: 0)
586 | } else if scrollView.contentOffset.x <= 0 {
587 | scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x + regularContentOffset, y: 0)
588 | }
589 | }
590 |
591 | // Updates the page indicator as the user scrolls (#204). Not called when not dragging to prevent flickers
592 | // when interacting with PageControl directly (#376).
593 | if scrollView.isDragging {
594 | pageIndicator?.page = currentPageForScrollViewPage(primaryVisiblePage)
595 | }
596 | }
597 |
598 | public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
599 | isAnimating = false
600 | }
601 | }
602 |
--------------------------------------------------------------------------------
/Example/ImageSlideshow.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */; };
11 | 48A9DE9233FBEA3CF63A8E54 /* Pods_ImageSlideshow_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */; };
12 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
13 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
14 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
15 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
16 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
17 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
18 | D007954F2043262C0053E25F /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007954E2043262C0053E25F /* TableViewController.swift */; };
19 | D0083DCE1EB739E700126B21 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */; };
20 | D00C7A2720B4C0A100E5725B /* ic_cross_white@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D00C7A2520B4C0A000E5725B /* ic_cross_white@3x.png */; };
21 | D00C7A2820B4C0A100E5725B /* ic_cross_white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D00C7A2620B4C0A100E5725B /* ic_cross_white@2x.png */; };
22 | D0B974B0202738F6006217CF /* PageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B974AF202738F6006217CF /* PageIndicator.swift */; };
23 | D0E8A9E61D97EB6D007EC517 /* ImageSlideshow_framework.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */; settings = {ATTRIBUTES = (Public, ); }; };
24 | D0E8A9F11D97EB94007EC517 /* FullScreenSlideshowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */; };
25 | D0E8A9F21D97EB94007EC517 /* ImageSlideshow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */; };
26 | D0E8A9F31D97EB94007EC517 /* ImageSlideshowItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */; };
27 | D0E8A9F41D97EB94007EC517 /* InputSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9ED1D97EB94007EC517 /* InputSource.swift */; };
28 | D0E8A9F51D97EB94007EC517 /* UIImage+AspectFit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */; };
29 | D0E8A9F61D97EB94007EC517 /* UIImageView+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */; };
30 | D0E8A9F71D97EB94007EC517 /* ZoomAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */; };
31 | F539204C210F03610057EFB3 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F539204B210F03600057EFB3 /* SwiftSupport.swift */; };
32 | F802998F20CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; };
33 | F802999020CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; };
34 | F802999120CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; };
35 | /* End PBXBuildFile section */
36 |
37 | /* Begin PBXContainerItemProxy section */
38 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */;
41 | proxyType = 1;
42 | remoteGlobalIDString = 607FACCF1AFB9204008FA782;
43 | remoteInfo = ImageSlideshow;
44 | };
45 | /* End PBXContainerItemProxy section */
46 |
47 | /* Begin PBXFileReference section */
48 | 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 = ""; };
49 | 1C4985EDB005E63A830176CE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
50 | 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Bundle+Module.swift"; path = "../../ImageSlideshow/Classes/Core/Bundle+Module.swift"; sourceTree = ""; };
51 | 5FD91AF3667236846934E8B9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
52 | 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageSlideshow_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
53 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
54 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
55 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
56 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
57 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
58 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
59 | 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageSlideshow_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
60 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
61 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; };
62 | 749E212B852A3FE6757C1D36 /* ImageSlideshow.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = ImageSlideshow.podspec; path = ../ImageSlideshow.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
63 | 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageSlideshow_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
64 | D007954E2043262C0053E25F /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; };
65 | D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActivityIndicator.swift; path = ../../ImageSlideshow/Classes/Core/ActivityIndicator.swift; sourceTree = ""; };
66 | D00C7A2520B4C0A000E5725B /* ic_cross_white@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ic_cross_white@3x.png"; path = "../../../ImageSlideshow/Assets/ic_cross_white@3x.png"; sourceTree = ""; };
67 | D00C7A2620B4C0A100E5725B /* ic_cross_white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ic_cross_white@2x.png"; path = "../../../ImageSlideshow/Assets/ic_cross_white@2x.png"; sourceTree = ""; };
68 | D0B974AF202738F6006217CF /* PageIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageIndicator.swift; path = ../../ImageSlideshow/Classes/Core/PageIndicator.swift; sourceTree = ""; };
69 | D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageSlideshow.framework; sourceTree = BUILT_PRODUCTS_DIR; };
70 | D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageSlideshow_framework.h; sourceTree = ""; };
71 | D0E8A9E51D97EB6D007EC517 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
72 | D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FullScreenSlideshowViewController.swift; path = ../../ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift; sourceTree = ""; };
73 | D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageSlideshow.swift; path = ../../ImageSlideshow/Classes/Core/ImageSlideshow.swift; sourceTree = ""; };
74 | D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageSlideshowItem.swift; path = ../../ImageSlideshow/Classes/Core/ImageSlideshowItem.swift; sourceTree = ""; };
75 | D0E8A9ED1D97EB94007EC517 /* InputSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputSource.swift; path = ../../ImageSlideshow/Classes/Core/InputSource.swift; sourceTree = ""; };
76 | D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+AspectFit.swift"; path = "../../ImageSlideshow/Classes/Core/UIImage+AspectFit.swift"; sourceTree = ""; };
77 | D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImageView+Tools.swift"; path = "../../ImageSlideshow/Classes/Core/UIImageView+Tools.swift"; sourceTree = ""; };
78 | D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ZoomAnimatedTransitioning.swift; path = ../../ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift; sourceTree = ""; };
79 | F539204B210F03600057EFB3 /* SwiftSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftSupport.swift; path = ../../ImageSlideshow/Classes/Core/SwiftSupport.swift; sourceTree = ""; };
80 | F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageIndicatorPosition.swift; path = ../../ImageSlideshow/Classes/Core/PageIndicatorPosition.swift; sourceTree = ""; };
81 | FD45C56C18E7B8EC08371B86 /* Pods-ImageSlideshow_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageSlideshow_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example.debug.xcconfig"; sourceTree = ""; };
82 | /* End PBXFileReference section */
83 |
84 | /* Begin PBXFrameworksBuildPhase section */
85 | 607FACCD1AFB9204008FA782 /* Frameworks */ = {
86 | isa = PBXFrameworksBuildPhase;
87 | buildActionMask = 2147483647;
88 | files = (
89 | 48A9DE9233FBEA3CF63A8E54 /* Pods_ImageSlideshow_Example.framework in Frameworks */,
90 | );
91 | runOnlyForDeploymentPostprocessing = 0;
92 | };
93 | 607FACE21AFB9204008FA782 /* Frameworks */ = {
94 | isa = PBXFrameworksBuildPhase;
95 | buildActionMask = 2147483647;
96 | files = (
97 | );
98 | runOnlyForDeploymentPostprocessing = 0;
99 | };
100 | D0E8A9DE1D97EB6D007EC517 /* Frameworks */ = {
101 | isa = PBXFrameworksBuildPhase;
102 | buildActionMask = 2147483647;
103 | files = (
104 | );
105 | runOnlyForDeploymentPostprocessing = 0;
106 | };
107 | /* End PBXFrameworksBuildPhase section */
108 |
109 | /* Begin PBXGroup section */
110 | 607FACC71AFB9204008FA782 = {
111 | isa = PBXGroup;
112 | children = (
113 | 607FACF51AFB993E008FA782 /* Podspec Metadata */,
114 | 607FACD21AFB9204008FA782 /* Example for ImageSlideshow */,
115 | 607FACE81AFB9204008FA782 /* Tests */,
116 | D0E8A9E31D97EB6D007EC517 /* ImageSlideshow_framework */,
117 | 607FACD11AFB9204008FA782 /* Products */,
118 | 67EF8AB7524DB4FF7F66A17E /* Pods */,
119 | 88C40B0495D05599E2889C22 /* Frameworks */,
120 | );
121 | sourceTree = "";
122 | };
123 | 607FACD11AFB9204008FA782 /* Products */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */,
127 | 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */,
128 | D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */,
129 | );
130 | name = Products;
131 | sourceTree = "";
132 | };
133 | 607FACD21AFB9204008FA782 /* Example for ImageSlideshow */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */,
137 | 607FACD71AFB9204008FA782 /* ViewController.swift */,
138 | D007954E2043262C0053E25F /* TableViewController.swift */,
139 | 607FACD91AFB9204008FA782 /* Main.storyboard */,
140 | 607FACDC1AFB9204008FA782 /* Images.xcassets */,
141 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
142 | 607FACD31AFB9204008FA782 /* Supporting Files */,
143 | );
144 | name = "Example for ImageSlideshow";
145 | path = ImageSlideshow;
146 | sourceTree = "";
147 | };
148 | 607FACD31AFB9204008FA782 /* Supporting Files */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 607FACD41AFB9204008FA782 /* Info.plist */,
152 | );
153 | name = "Supporting Files";
154 | sourceTree = "";
155 | };
156 | 607FACE81AFB9204008FA782 /* Tests */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 607FACEB1AFB9204008FA782 /* Tests.swift */,
160 | 607FACE91AFB9204008FA782 /* Supporting Files */,
161 | );
162 | path = Tests;
163 | sourceTree = "";
164 | };
165 | 607FACE91AFB9204008FA782 /* Supporting Files */ = {
166 | isa = PBXGroup;
167 | children = (
168 | 607FACEA1AFB9204008FA782 /* Info.plist */,
169 | );
170 | name = "Supporting Files";
171 | sourceTree = "";
172 | };
173 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
174 | isa = PBXGroup;
175 | children = (
176 | 749E212B852A3FE6757C1D36 /* ImageSlideshow.podspec */,
177 | 1C4985EDB005E63A830176CE /* README.md */,
178 | 5FD91AF3667236846934E8B9 /* LICENSE */,
179 | );
180 | name = "Podspec Metadata";
181 | sourceTree = "";
182 | };
183 | 67EF8AB7524DB4FF7F66A17E /* Pods */ = {
184 | isa = PBXGroup;
185 | children = (
186 | FD45C56C18E7B8EC08371B86 /* Pods-ImageSlideshow_Example.debug.xcconfig */,
187 | 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */,
188 | );
189 | name = Pods;
190 | sourceTree = "";
191 | };
192 | 88C40B0495D05599E2889C22 /* Frameworks */ = {
193 | isa = PBXGroup;
194 | children = (
195 | 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */,
196 | );
197 | name = Frameworks;
198 | sourceTree = "";
199 | };
200 | D00C7A2420B4C05C00E5725B /* Resources */ = {
201 | isa = PBXGroup;
202 | children = (
203 | D00C7A2620B4C0A100E5725B /* ic_cross_white@2x.png */,
204 | D00C7A2520B4C0A000E5725B /* ic_cross_white@3x.png */,
205 | );
206 | path = Resources;
207 | sourceTree = "";
208 | };
209 | D0E8A9E31D97EB6D007EC517 /* ImageSlideshow_framework */ = {
210 | isa = PBXGroup;
211 | children = (
212 | D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */,
213 | 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */,
214 | D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */,
215 | D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */,
216 | D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */,
217 | D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */,
218 | D0E8A9E51D97EB6D007EC517 /* Info.plist */,
219 | D0E8A9ED1D97EB94007EC517 /* InputSource.swift */,
220 | D0B974AF202738F6006217CF /* PageIndicator.swift */,
221 | F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */,
222 | D00C7A2420B4C05C00E5725B /* Resources */,
223 | F539204B210F03600057EFB3 /* SwiftSupport.swift */,
224 | D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */,
225 | D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */,
226 | D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */,
227 | );
228 | path = ImageSlideshow_framework;
229 | sourceTree = "";
230 | };
231 | /* End PBXGroup section */
232 |
233 | /* Begin PBXHeadersBuildPhase section */
234 | D0E8A9DF1D97EB6D007EC517 /* Headers */ = {
235 | isa = PBXHeadersBuildPhase;
236 | buildActionMask = 2147483647;
237 | files = (
238 | D0E8A9E61D97EB6D007EC517 /* ImageSlideshow_framework.h in Headers */,
239 | );
240 | runOnlyForDeploymentPostprocessing = 0;
241 | };
242 | /* End PBXHeadersBuildPhase section */
243 |
244 | /* Begin PBXNativeTarget section */
245 | 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */ = {
246 | isa = PBXNativeTarget;
247 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Example" */;
248 | buildPhases = (
249 | C32710DF8FE75A6D757948CC /* [CP] Check Pods Manifest.lock */,
250 | 607FACCC1AFB9204008FA782 /* Sources */,
251 | 607FACCD1AFB9204008FA782 /* Frameworks */,
252 | 607FACCE1AFB9204008FA782 /* Resources */,
253 | 7EF425DA4D49DBDDA22C665D /* [CP] Embed Pods Frameworks */,
254 | );
255 | buildRules = (
256 | );
257 | dependencies = (
258 | );
259 | name = ImageSlideshow_Example;
260 | productName = ImageSlideshow;
261 | productReference = 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */;
262 | productType = "com.apple.product-type.application";
263 | };
264 | 607FACE41AFB9204008FA782 /* ImageSlideshow_Tests */ = {
265 | isa = PBXNativeTarget;
266 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Tests" */;
267 | buildPhases = (
268 | 607FACE11AFB9204008FA782 /* Sources */,
269 | 607FACE21AFB9204008FA782 /* Frameworks */,
270 | 607FACE31AFB9204008FA782 /* Resources */,
271 | );
272 | buildRules = (
273 | );
274 | dependencies = (
275 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */,
276 | );
277 | name = ImageSlideshow_Tests;
278 | productName = Tests;
279 | productReference = 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */;
280 | productType = "com.apple.product-type.bundle.unit-test";
281 | };
282 | D0E8A9E11D97EB6D007EC517 /* ImageSlideshow_framework */ = {
283 | isa = PBXNativeTarget;
284 | buildConfigurationList = D0E8A9E91D97EB6D007EC517 /* Build configuration list for PBXNativeTarget "ImageSlideshow_framework" */;
285 | buildPhases = (
286 | D0E8A9DD1D97EB6D007EC517 /* Sources */,
287 | D0E8A9DE1D97EB6D007EC517 /* Frameworks */,
288 | D0E8A9DF1D97EB6D007EC517 /* Headers */,
289 | D0E8A9E01D97EB6D007EC517 /* Resources */,
290 | );
291 | buildRules = (
292 | );
293 | dependencies = (
294 | );
295 | name = ImageSlideshow_framework;
296 | productName = ImageSlideshow_framework;
297 | productReference = D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */;
298 | productType = "com.apple.product-type.framework";
299 | };
300 | /* End PBXNativeTarget section */
301 |
302 | /* Begin PBXProject section */
303 | 607FACC81AFB9204008FA782 /* Project object */ = {
304 | isa = PBXProject;
305 | attributes = {
306 | LastSwiftMigration = 0700;
307 | LastSwiftUpdateCheck = 0700;
308 | LastUpgradeCheck = 0940;
309 | ORGANIZATIONNAME = CocoaPods;
310 | TargetAttributes = {
311 | 607FACCF1AFB9204008FA782 = {
312 | CreatedOnToolsVersion = 6.3.1;
313 | DevelopmentTeam = 5VWB99DS38;
314 | LastSwiftMigration = 1020;
315 | };
316 | 607FACE41AFB9204008FA782 = {
317 | CreatedOnToolsVersion = 6.3.1;
318 | DevelopmentTeam = 5VWB99DS38;
319 | LastSwiftMigration = 1020;
320 | TestTargetID = 607FACCF1AFB9204008FA782;
321 | };
322 | D0E8A9E11D97EB6D007EC517 = {
323 | CreatedOnToolsVersion = 8.0;
324 | DevelopmentTeam = 9X4J83EL7M;
325 | LastSwiftMigration = 0800;
326 | ProvisioningStyle = Automatic;
327 | };
328 | };
329 | };
330 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageSlideshow" */;
331 | compatibilityVersion = "Xcode 3.2";
332 | developmentRegion = English;
333 | hasScannedForEncodings = 0;
334 | knownRegions = (
335 | English,
336 | en,
337 | Base,
338 | );
339 | mainGroup = 607FACC71AFB9204008FA782;
340 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
341 | projectDirPath = "";
342 | projectRoot = "";
343 | targets = (
344 | 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */,
345 | 607FACE41AFB9204008FA782 /* ImageSlideshow_Tests */,
346 | D0E8A9E11D97EB6D007EC517 /* ImageSlideshow_framework */,
347 | );
348 | };
349 | /* End PBXProject section */
350 |
351 | /* Begin PBXResourcesBuildPhase section */
352 | 607FACCE1AFB9204008FA782 /* Resources */ = {
353 | isa = PBXResourcesBuildPhase;
354 | buildActionMask = 2147483647;
355 | files = (
356 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
357 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
358 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
359 | );
360 | runOnlyForDeploymentPostprocessing = 0;
361 | };
362 | 607FACE31AFB9204008FA782 /* Resources */ = {
363 | isa = PBXResourcesBuildPhase;
364 | buildActionMask = 2147483647;
365 | files = (
366 | );
367 | runOnlyForDeploymentPostprocessing = 0;
368 | };
369 | D0E8A9E01D97EB6D007EC517 /* Resources */ = {
370 | isa = PBXResourcesBuildPhase;
371 | buildActionMask = 2147483647;
372 | files = (
373 | D00C7A2720B4C0A100E5725B /* ic_cross_white@3x.png in Resources */,
374 | D00C7A2820B4C0A100E5725B /* ic_cross_white@2x.png in Resources */,
375 | );
376 | runOnlyForDeploymentPostprocessing = 0;
377 | };
378 | /* End PBXResourcesBuildPhase section */
379 |
380 | /* Begin PBXShellScriptBuildPhase section */
381 | 7EF425DA4D49DBDDA22C665D /* [CP] Embed Pods Frameworks */ = {
382 | isa = PBXShellScriptBuildPhase;
383 | buildActionMask = 2147483647;
384 | files = (
385 | );
386 | inputPaths = (
387 | "${PODS_ROOT}/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example-frameworks.sh",
388 | "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework",
389 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework",
390 | "${BUILT_PRODUCTS_DIR}/AlamofireImage/AlamofireImage.framework",
391 | "${BUILT_PRODUCTS_DIR}/ImageSlideshow/ImageSlideshow.framework",
392 | "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework",
393 | "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
394 | );
395 | name = "[CP] Embed Pods Frameworks";
396 | outputPaths = (
397 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework",
398 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework",
399 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AlamofireImage.framework",
400 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ImageSlideshow.framework",
401 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework",
402 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
403 | );
404 | runOnlyForDeploymentPostprocessing = 0;
405 | shellPath = /bin/sh;
406 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example-frameworks.sh\"\n";
407 | showEnvVarsInLog = 0;
408 | };
409 | C32710DF8FE75A6D757948CC /* [CP] Check Pods Manifest.lock */ = {
410 | isa = PBXShellScriptBuildPhase;
411 | buildActionMask = 2147483647;
412 | files = (
413 | );
414 | inputPaths = (
415 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
416 | "${PODS_ROOT}/Manifest.lock",
417 | );
418 | name = "[CP] Check Pods Manifest.lock";
419 | outputPaths = (
420 | "$(DERIVED_FILE_DIR)/Pods-ImageSlideshow_Example-checkManifestLockResult.txt",
421 | );
422 | runOnlyForDeploymentPostprocessing = 0;
423 | shellPath = /bin/sh;
424 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
425 | showEnvVarsInLog = 0;
426 | };
427 | /* End PBXShellScriptBuildPhase section */
428 |
429 | /* Begin PBXSourcesBuildPhase section */
430 | 607FACCC1AFB9204008FA782 /* Sources */ = {
431 | isa = PBXSourcesBuildPhase;
432 | buildActionMask = 2147483647;
433 | files = (
434 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
435 | F802998F20CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */,
436 | D007954F2043262C0053E25F /* TableViewController.swift in Sources */,
437 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
438 | );
439 | runOnlyForDeploymentPostprocessing = 0;
440 | };
441 | 607FACE11AFB9204008FA782 /* Sources */ = {
442 | isa = PBXSourcesBuildPhase;
443 | buildActionMask = 2147483647;
444 | files = (
445 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */,
446 | F802999020CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */,
447 | );
448 | runOnlyForDeploymentPostprocessing = 0;
449 | };
450 | D0E8A9DD1D97EB6D007EC517 /* Sources */ = {
451 | isa = PBXSourcesBuildPhase;
452 | buildActionMask = 2147483647;
453 | files = (
454 | D0E8A9F21D97EB94007EC517 /* ImageSlideshow.swift in Sources */,
455 | D0E8A9F41D97EB94007EC517 /* InputSource.swift in Sources */,
456 | D0E8A9F61D97EB94007EC517 /* UIImageView+Tools.swift in Sources */,
457 | D0E8A9F71D97EB94007EC517 /* ZoomAnimatedTransitioning.swift in Sources */,
458 | D0083DCE1EB739E700126B21 /* ActivityIndicator.swift in Sources */,
459 | 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */,
460 | F802999120CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */,
461 | D0E8A9F51D97EB94007EC517 /* UIImage+AspectFit.swift in Sources */,
462 | D0E8A9F11D97EB94007EC517 /* FullScreenSlideshowViewController.swift in Sources */,
463 | F539204C210F03610057EFB3 /* SwiftSupport.swift in Sources */,
464 | D0B974B0202738F6006217CF /* PageIndicator.swift in Sources */,
465 | D0E8A9F31D97EB94007EC517 /* ImageSlideshowItem.swift in Sources */,
466 | );
467 | runOnlyForDeploymentPostprocessing = 0;
468 | };
469 | /* End PBXSourcesBuildPhase section */
470 |
471 | /* Begin PBXTargetDependency section */
472 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = {
473 | isa = PBXTargetDependency;
474 | target = 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */;
475 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */;
476 | };
477 | /* End PBXTargetDependency section */
478 |
479 | /* Begin PBXVariantGroup section */
480 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = {
481 | isa = PBXVariantGroup;
482 | children = (
483 | 607FACDA1AFB9204008FA782 /* Base */,
484 | );
485 | name = Main.storyboard;
486 | sourceTree = "";
487 | };
488 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
489 | isa = PBXVariantGroup;
490 | children = (
491 | 607FACDF1AFB9204008FA782 /* Base */,
492 | );
493 | name = LaunchScreen.xib;
494 | sourceTree = "";
495 | };
496 | /* End PBXVariantGroup section */
497 |
498 | /* Begin XCBuildConfiguration section */
499 | 607FACED1AFB9204008FA782 /* Debug */ = {
500 | isa = XCBuildConfiguration;
501 | buildSettings = {
502 | ALWAYS_SEARCH_USER_PATHS = NO;
503 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
504 | CLANG_CXX_LIBRARY = "libc++";
505 | CLANG_ENABLE_MODULES = YES;
506 | CLANG_ENABLE_OBJC_ARC = YES;
507 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
508 | CLANG_WARN_BOOL_CONVERSION = YES;
509 | CLANG_WARN_COMMA = YES;
510 | CLANG_WARN_CONSTANT_CONVERSION = YES;
511 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
512 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
513 | CLANG_WARN_EMPTY_BODY = YES;
514 | CLANG_WARN_ENUM_CONVERSION = YES;
515 | CLANG_WARN_INFINITE_RECURSION = YES;
516 | CLANG_WARN_INT_CONVERSION = YES;
517 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
518 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
519 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
520 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
521 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
522 | CLANG_WARN_STRICT_PROTOTYPES = YES;
523 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
524 | CLANG_WARN_UNREACHABLE_CODE = YES;
525 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
526 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
527 | COPY_PHASE_STRIP = NO;
528 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
529 | ENABLE_STRICT_OBJC_MSGSEND = YES;
530 | ENABLE_TESTABILITY = YES;
531 | GCC_C_LANGUAGE_STANDARD = gnu99;
532 | GCC_DYNAMIC_NO_PIC = NO;
533 | GCC_NO_COMMON_BLOCKS = YES;
534 | GCC_OPTIMIZATION_LEVEL = 0;
535 | GCC_PREPROCESSOR_DEFINITIONS = (
536 | "DEBUG=1",
537 | "$(inherited)",
538 | );
539 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
540 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
541 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
542 | GCC_WARN_UNDECLARED_SELECTOR = YES;
543 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
544 | GCC_WARN_UNUSED_FUNCTION = YES;
545 | GCC_WARN_UNUSED_VARIABLE = YES;
546 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
547 | MTL_ENABLE_DEBUG_INFO = YES;
548 | ONLY_ACTIVE_ARCH = YES;
549 | SDKROOT = iphoneos;
550 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
551 | SWIFT_VERSION = 4.2;
552 | };
553 | name = Debug;
554 | };
555 | 607FACEE1AFB9204008FA782 /* Release */ = {
556 | isa = XCBuildConfiguration;
557 | buildSettings = {
558 | ALWAYS_SEARCH_USER_PATHS = NO;
559 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
560 | CLANG_CXX_LIBRARY = "libc++";
561 | CLANG_ENABLE_MODULES = YES;
562 | CLANG_ENABLE_OBJC_ARC = YES;
563 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
564 | CLANG_WARN_BOOL_CONVERSION = YES;
565 | CLANG_WARN_COMMA = YES;
566 | CLANG_WARN_CONSTANT_CONVERSION = YES;
567 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
568 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
569 | CLANG_WARN_EMPTY_BODY = YES;
570 | CLANG_WARN_ENUM_CONVERSION = YES;
571 | CLANG_WARN_INFINITE_RECURSION = YES;
572 | CLANG_WARN_INT_CONVERSION = YES;
573 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
574 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
575 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
576 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
577 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
578 | CLANG_WARN_STRICT_PROTOTYPES = YES;
579 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
580 | CLANG_WARN_UNREACHABLE_CODE = YES;
581 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
582 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
583 | COPY_PHASE_STRIP = NO;
584 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
585 | ENABLE_NS_ASSERTIONS = NO;
586 | ENABLE_STRICT_OBJC_MSGSEND = YES;
587 | GCC_C_LANGUAGE_STANDARD = gnu99;
588 | GCC_NO_COMMON_BLOCKS = YES;
589 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
590 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
591 | GCC_WARN_UNDECLARED_SELECTOR = YES;
592 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
593 | GCC_WARN_UNUSED_FUNCTION = YES;
594 | GCC_WARN_UNUSED_VARIABLE = YES;
595 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
596 | MTL_ENABLE_DEBUG_INFO = NO;
597 | SDKROOT = iphoneos;
598 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
599 | SWIFT_VERSION = 4.2;
600 | VALIDATE_PRODUCT = YES;
601 | };
602 | name = Release;
603 | };
604 | 607FACF01AFB9204008FA782 /* Debug */ = {
605 | isa = XCBuildConfiguration;
606 | baseConfigurationReference = FD45C56C18E7B8EC08371B86 /* Pods-ImageSlideshow_Example.debug.xcconfig */;
607 | buildSettings = {
608 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
609 | DEVELOPMENT_TEAM = 5VWB99DS38;
610 | INFOPLIST_FILE = ImageSlideshow/Info.plist;
611 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
612 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
613 | MODULE_NAME = ExampleApp;
614 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
615 | PRODUCT_NAME = "$(TARGET_NAME)";
616 | SWIFT_VERSION = 5.0;
617 | };
618 | name = Debug;
619 | };
620 | 607FACF11AFB9204008FA782 /* Release */ = {
621 | isa = XCBuildConfiguration;
622 | baseConfigurationReference = 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */;
623 | buildSettings = {
624 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
625 | DEVELOPMENT_TEAM = 5VWB99DS38;
626 | INFOPLIST_FILE = ImageSlideshow/Info.plist;
627 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
628 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
629 | MODULE_NAME = ExampleApp;
630 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
631 | PRODUCT_NAME = "$(TARGET_NAME)";
632 | SWIFT_VERSION = 5.0;
633 | };
634 | name = Release;
635 | };
636 | 607FACF31AFB9204008FA782 /* Debug */ = {
637 | isa = XCBuildConfiguration;
638 | buildSettings = {
639 | BUNDLE_LOADER = "$(TEST_HOST)";
640 | DEVELOPMENT_TEAM = 5VWB99DS38;
641 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
642 | GCC_PREPROCESSOR_DEFINITIONS = (
643 | "DEBUG=1",
644 | "$(inherited)",
645 | );
646 | INFOPLIST_FILE = Tests/Info.plist;
647 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
648 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
649 | PRODUCT_NAME = "$(TARGET_NAME)";
650 | SWIFT_VERSION = 5.0;
651 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageSlideshow_Example.app/ImageSlideshow_Example";
652 | };
653 | name = Debug;
654 | };
655 | 607FACF41AFB9204008FA782 /* Release */ = {
656 | isa = XCBuildConfiguration;
657 | buildSettings = {
658 | BUNDLE_LOADER = "$(TEST_HOST)";
659 | DEVELOPMENT_TEAM = 5VWB99DS38;
660 | FRAMEWORK_SEARCH_PATHS = "$(inherited)";
661 | INFOPLIST_FILE = Tests/Info.plist;
662 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
663 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
664 | PRODUCT_NAME = "$(TARGET_NAME)";
665 | SWIFT_VERSION = 5.0;
666 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageSlideshow_Example.app/ImageSlideshow_Example";
667 | };
668 | name = Release;
669 | };
670 | D0E8A9E71D97EB6D007EC517 /* Debug */ = {
671 | isa = XCBuildConfiguration;
672 | buildSettings = {
673 | CLANG_ANALYZER_NONNULL = YES;
674 | CLANG_ENABLE_MODULES = YES;
675 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
676 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
677 | CODE_SIGN_IDENTITY = "";
678 | CURRENT_PROJECT_VERSION = 1;
679 | DEBUG_INFORMATION_FORMAT = dwarf;
680 | DEFINES_MODULE = YES;
681 | DEVELOPMENT_TEAM = 9X4J83EL7M;
682 | DYLIB_COMPATIBILITY_VERSION = 1;
683 | DYLIB_CURRENT_VERSION = 1;
684 | DYLIB_INSTALL_NAME_BASE = "@rpath";
685 | INFOPLIST_FILE = ImageSlideshow_framework/Info.plist;
686 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
687 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
688 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
689 | PRODUCT_BUNDLE_IDENTIFIER = "io.zvo.ImageSlideshow-framework";
690 | PRODUCT_NAME = ImageSlideshow;
691 | SKIP_INSTALL = YES;
692 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
693 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
694 | TARGETED_DEVICE_FAMILY = "1,2";
695 | VERSIONING_SYSTEM = "apple-generic";
696 | VERSION_INFO_PREFIX = "";
697 | };
698 | name = Debug;
699 | };
700 | D0E8A9E81D97EB6D007EC517 /* Release */ = {
701 | isa = XCBuildConfiguration;
702 | buildSettings = {
703 | CLANG_ANALYZER_NONNULL = YES;
704 | CLANG_ENABLE_MODULES = YES;
705 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
706 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
707 | CODE_SIGN_IDENTITY = "";
708 | CURRENT_PROJECT_VERSION = 1;
709 | DEFINES_MODULE = YES;
710 | DEVELOPMENT_TEAM = 9X4J83EL7M;
711 | DYLIB_COMPATIBILITY_VERSION = 1;
712 | DYLIB_CURRENT_VERSION = 1;
713 | DYLIB_INSTALL_NAME_BASE = "@rpath";
714 | INFOPLIST_FILE = ImageSlideshow_framework/Info.plist;
715 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
716 | IPHONEOS_DEPLOYMENT_TARGET = 10.0;
717 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
718 | PRODUCT_BUNDLE_IDENTIFIER = "io.zvo.ImageSlideshow-framework";
719 | PRODUCT_NAME = ImageSlideshow;
720 | SKIP_INSTALL = YES;
721 | TARGETED_DEVICE_FAMILY = "1,2";
722 | VERSIONING_SYSTEM = "apple-generic";
723 | VERSION_INFO_PREFIX = "";
724 | };
725 | name = Release;
726 | };
727 | /* End XCBuildConfiguration section */
728 |
729 | /* Begin XCConfigurationList section */
730 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageSlideshow" */ = {
731 | isa = XCConfigurationList;
732 | buildConfigurations = (
733 | 607FACED1AFB9204008FA782 /* Debug */,
734 | 607FACEE1AFB9204008FA782 /* Release */,
735 | );
736 | defaultConfigurationIsVisible = 0;
737 | defaultConfigurationName = Release;
738 | };
739 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Example" */ = {
740 | isa = XCConfigurationList;
741 | buildConfigurations = (
742 | 607FACF01AFB9204008FA782 /* Debug */,
743 | 607FACF11AFB9204008FA782 /* Release */,
744 | );
745 | defaultConfigurationIsVisible = 0;
746 | defaultConfigurationName = Release;
747 | };
748 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Tests" */ = {
749 | isa = XCConfigurationList;
750 | buildConfigurations = (
751 | 607FACF31AFB9204008FA782 /* Debug */,
752 | 607FACF41AFB9204008FA782 /* Release */,
753 | );
754 | defaultConfigurationIsVisible = 0;
755 | defaultConfigurationName = Release;
756 | };
757 | D0E8A9E91D97EB6D007EC517 /* Build configuration list for PBXNativeTarget "ImageSlideshow_framework" */ = {
758 | isa = XCConfigurationList;
759 | buildConfigurations = (
760 | D0E8A9E71D97EB6D007EC517 /* Debug */,
761 | D0E8A9E81D97EB6D007EC517 /* Release */,
762 | );
763 | defaultConfigurationIsVisible = 0;
764 | defaultConfigurationName = Release;
765 | };
766 | /* End XCConfigurationList section */
767 | };
768 | rootObject = 607FACC81AFB9204008FA782 /* Project object */;
769 | }
770 |
--------------------------------------------------------------------------------