diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..f441fde5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +jobs: + spm: + name: SPM build (iOS) + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + # _Pods.xcodeproj would shadow the Swift package for xcodebuild + - name: Remove CocoaPods development project placeholder + run: rm -rf _Pods.xcodeproj + - name: Build all package products + run: | + xcodebuild -scheme ImageSlideshow-Package \ + -destination 'generic/platform=iOS Simulator' \ + build + + example-tests: + name: Example app tests + runs-on: macos-15 + steps: + - uses: actions/checkout@v4 + - name: Install XcodeGen + run: brew install xcodegen + - name: Generate Xcode project + working-directory: Example + run: xcodegen generate + - name: Install pods + working-directory: Example + run: pod install + - name: Run tests + working-directory: Example + run: | + DEVICE=$(xcrun simctl list devices available | grep -m1 -oE 'iPhone [^(]+' | sed 's/ *$//') + echo "Using simulator: $DEVICE" + xcodebuild test \ + -workspace ImageSlideshow.xcworkspace \ + -scheme ImageSlideshow-Example \ + -destination "platform=iOS Simulator,name=$DEVICE" \ + CODE_SIGNING_ALLOWED=NO diff --git a/.gitignore b/.gitignore index 669d819a..9c38ad7b 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ Pods/ # Swift Package Manager .build/ + +# XcodeGen — generated project (regenerated by CI and locally via `xcodegen generate`) +Example/ImageSlideshow.xcodeproj/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a44c2d0..53cb571d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Change log +## [1.9.4](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.9.4) (12/06/2026) + +## Breaking + +- Minimum deployment target raised to iOS 15 (CocoaPods and Swift Package Manager) +- Removed the unmaintained `Parse` subspec +- Removed the Swift 4.2 compatibility typealiases (`UIViewContentMode` etc.) + +## New Features + +- Input sources, activity indicators and page indicators adopt main-actor isolation for Swift concurrency + +## Fixes + +- Fixed Swift Package Manager dependency resolution (AlamofireImage requirement pointed at a nonexistent version) +- Kingfisher loads run synchronously on the main actor, fixing a race where a deferred cancel could abort a newly started download +- Failed image loads now report nil so the retry gesture is enabled (KingfisherSource, AFURLSource) +- Interactive dismiss gesture is removed from the window it was added to, fixing a leak when the key window changes + ## [1.6.1](https://github.com/zvonicek/ImageSlideshow/releases/tag/1.6.1) (11/06/2018) ## Fixes diff --git a/Example/ImageSlideshow.xcodeproj/project.pbxproj b/Example/ImageSlideshow.xcodeproj/project.pbxproj deleted file mode 100644 index 888725d1..00000000 --- a/Example/ImageSlideshow.xcodeproj/project.pbxproj +++ /dev/null @@ -1,769 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */; }; - 48A9DE9233FBEA3CF63A8E54 /* Pods_ImageSlideshow_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */; }; - 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; - 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; - 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; - 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; - 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; - 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; - D007954F2043262C0053E25F /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007954E2043262C0053E25F /* TableViewController.swift */; }; - D0083DCE1EB739E700126B21 /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */; }; - D00C7A2720B4C0A100E5725B /* ic_cross_white@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D00C7A2520B4C0A000E5725B /* ic_cross_white@3x.png */; }; - D00C7A2820B4C0A100E5725B /* ic_cross_white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D00C7A2620B4C0A100E5725B /* ic_cross_white@2x.png */; }; - D0B974B0202738F6006217CF /* PageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B974AF202738F6006217CF /* PageIndicator.swift */; }; - D0E8A9E61D97EB6D007EC517 /* ImageSlideshow_framework.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D0E8A9F11D97EB94007EC517 /* FullScreenSlideshowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */; }; - D0E8A9F21D97EB94007EC517 /* ImageSlideshow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */; }; - D0E8A9F31D97EB94007EC517 /* ImageSlideshowItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */; }; - D0E8A9F41D97EB94007EC517 /* InputSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9ED1D97EB94007EC517 /* InputSource.swift */; }; - D0E8A9F51D97EB94007EC517 /* UIImage+AspectFit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */; }; - D0E8A9F61D97EB94007EC517 /* UIImageView+Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */; }; - D0E8A9F71D97EB94007EC517 /* ZoomAnimatedTransitioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */; }; - F539204C210F03610057EFB3 /* SwiftSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F539204B210F03600057EFB3 /* SwiftSupport.swift */; }; - F802998F20CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; }; - F802999020CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; }; - F802999120CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 607FACC81AFB9204008FA782 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 607FACCF1AFB9204008FA782; - remoteInfo = ImageSlideshow; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ImageSlideshow_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example.release.xcconfig"; sourceTree = ""; }; - 1C4985EDB005E63A830176CE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; - 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Bundle+Module.swift"; path = "../../ImageSlideshow/Classes/Core/Bundle+Module.swift"; sourceTree = ""; }; - 5FD91AF3667236846934E8B9 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; - 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageSlideshow_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageSlideshow_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; - 749E212B852A3FE6757C1D36 /* ImageSlideshow.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = ImageSlideshow.podspec; path = ../ImageSlideshow.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ImageSlideshow_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D007954E2043262C0053E25F /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; - D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ActivityIndicator.swift; path = ../../ImageSlideshow/Classes/Core/ActivityIndicator.swift; sourceTree = ""; }; - 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 = ""; }; - 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 = ""; }; - D0B974AF202738F6006217CF /* PageIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageIndicator.swift; path = ../../ImageSlideshow/Classes/Core/PageIndicator.swift; sourceTree = ""; }; - D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ImageSlideshow.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageSlideshow_framework.h; sourceTree = ""; }; - D0E8A9E51D97EB6D007EC517 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FullScreenSlideshowViewController.swift; path = ../../ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift; sourceTree = ""; }; - D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageSlideshow.swift; path = ../../ImageSlideshow/Classes/Core/ImageSlideshow.swift; sourceTree = ""; }; - D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageSlideshowItem.swift; path = ../../ImageSlideshow/Classes/Core/ImageSlideshowItem.swift; sourceTree = ""; }; - D0E8A9ED1D97EB94007EC517 /* InputSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputSource.swift; path = ../../ImageSlideshow/Classes/Core/InputSource.swift; sourceTree = ""; }; - D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImage+AspectFit.swift"; path = "../../ImageSlideshow/Classes/Core/UIImage+AspectFit.swift"; sourceTree = ""; }; - D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImageView+Tools.swift"; path = "../../ImageSlideshow/Classes/Core/UIImageView+Tools.swift"; sourceTree = ""; }; - D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ZoomAnimatedTransitioning.swift; path = ../../ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift; sourceTree = ""; }; - F539204B210F03600057EFB3 /* SwiftSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftSupport.swift; path = ../../ImageSlideshow/Classes/Core/SwiftSupport.swift; sourceTree = ""; }; - F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PageIndicatorPosition.swift; path = ../../ImageSlideshow/Classes/Core/PageIndicatorPosition.swift; sourceTree = ""; }; - 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 = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 607FACCD1AFB9204008FA782 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 48A9DE9233FBEA3CF63A8E54 /* Pods_ImageSlideshow_Example.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 607FACE21AFB9204008FA782 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0E8A9DE1D97EB6D007EC517 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 607FACC71AFB9204008FA782 = { - isa = PBXGroup; - children = ( - 607FACF51AFB993E008FA782 /* Podspec Metadata */, - 607FACD21AFB9204008FA782 /* Example for ImageSlideshow */, - 607FACE81AFB9204008FA782 /* Tests */, - D0E8A9E31D97EB6D007EC517 /* ImageSlideshow_framework */, - 607FACD11AFB9204008FA782 /* Products */, - 67EF8AB7524DB4FF7F66A17E /* Pods */, - 88C40B0495D05599E2889C22 /* Frameworks */, - ); - sourceTree = ""; - }; - 607FACD11AFB9204008FA782 /* Products */ = { - isa = PBXGroup; - children = ( - 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */, - 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */, - D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */, - ); - name = Products; - sourceTree = ""; - }; - 607FACD21AFB9204008FA782 /* Example for ImageSlideshow */ = { - isa = PBXGroup; - children = ( - 607FACD51AFB9204008FA782 /* AppDelegate.swift */, - 607FACD71AFB9204008FA782 /* ViewController.swift */, - D007954E2043262C0053E25F /* TableViewController.swift */, - 607FACD91AFB9204008FA782 /* Main.storyboard */, - 607FACDC1AFB9204008FA782 /* Images.xcassets */, - 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, - 607FACD31AFB9204008FA782 /* Supporting Files */, - ); - name = "Example for ImageSlideshow"; - path = ImageSlideshow; - sourceTree = ""; - }; - 607FACD31AFB9204008FA782 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 607FACD41AFB9204008FA782 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 607FACE81AFB9204008FA782 /* Tests */ = { - isa = PBXGroup; - children = ( - 607FACEB1AFB9204008FA782 /* Tests.swift */, - 607FACE91AFB9204008FA782 /* Supporting Files */, - ); - path = Tests; - sourceTree = ""; - }; - 607FACE91AFB9204008FA782 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 607FACEA1AFB9204008FA782 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { - isa = PBXGroup; - children = ( - 749E212B852A3FE6757C1D36 /* ImageSlideshow.podspec */, - 1C4985EDB005E63A830176CE /* README.md */, - 5FD91AF3667236846934E8B9 /* LICENSE */, - ); - name = "Podspec Metadata"; - sourceTree = ""; - }; - 67EF8AB7524DB4FF7F66A17E /* Pods */ = { - isa = PBXGroup; - children = ( - FD45C56C18E7B8EC08371B86 /* Pods-ImageSlideshow_Example.debug.xcconfig */, - 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 88C40B0495D05599E2889C22 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 8AA07A6EDAC622F1407E6C80 /* Pods_ImageSlideshow_Example.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - D00C7A2420B4C05C00E5725B /* Resources */ = { - isa = PBXGroup; - children = ( - D00C7A2620B4C0A100E5725B /* ic_cross_white@2x.png */, - D00C7A2520B4C0A000E5725B /* ic_cross_white@3x.png */, - ); - path = Resources; - sourceTree = ""; - }; - D0E8A9E31D97EB6D007EC517 /* ImageSlideshow_framework */ = { - isa = PBXGroup; - children = ( - D0083DCD1EB739E700126B21 /* ActivityIndicator.swift */, - 43F0FAD32568BAEB008FFD39 /* Bundle+Module.swift */, - D0E8A9EA1D97EB94007EC517 /* FullScreenSlideshowViewController.swift */, - D0E8A9E41D97EB6D007EC517 /* ImageSlideshow_framework.h */, - D0E8A9EB1D97EB94007EC517 /* ImageSlideshow.swift */, - D0E8A9EC1D97EB94007EC517 /* ImageSlideshowItem.swift */, - D0E8A9E51D97EB6D007EC517 /* Info.plist */, - D0E8A9ED1D97EB94007EC517 /* InputSource.swift */, - D0B974AF202738F6006217CF /* PageIndicator.swift */, - F802998E20CE9EA7009D64DD /* PageIndicatorPosition.swift */, - D00C7A2420B4C05C00E5725B /* Resources */, - F539204B210F03600057EFB3 /* SwiftSupport.swift */, - D0E8A9EE1D97EB94007EC517 /* UIImage+AspectFit.swift */, - D0E8A9EF1D97EB94007EC517 /* UIImageView+Tools.swift */, - D0E8A9F01D97EB94007EC517 /* ZoomAnimatedTransitioning.swift */, - ); - path = ImageSlideshow_framework; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - D0E8A9DF1D97EB6D007EC517 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - D0E8A9E61D97EB6D007EC517 /* ImageSlideshow_framework.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Example" */; - buildPhases = ( - C32710DF8FE75A6D757948CC /* [CP] Check Pods Manifest.lock */, - 607FACCC1AFB9204008FA782 /* Sources */, - 607FACCD1AFB9204008FA782 /* Frameworks */, - 607FACCE1AFB9204008FA782 /* Resources */, - 7EF425DA4D49DBDDA22C665D /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ImageSlideshow_Example; - productName = ImageSlideshow; - productReference = 607FACD01AFB9204008FA782 /* ImageSlideshow_Example.app */; - productType = "com.apple.product-type.application"; - }; - 607FACE41AFB9204008FA782 /* ImageSlideshow_Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Tests" */; - buildPhases = ( - 607FACE11AFB9204008FA782 /* Sources */, - 607FACE21AFB9204008FA782 /* Frameworks */, - 607FACE31AFB9204008FA782 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 607FACE71AFB9204008FA782 /* PBXTargetDependency */, - ); - name = ImageSlideshow_Tests; - productName = Tests; - productReference = 607FACE51AFB9204008FA782 /* ImageSlideshow_Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - D0E8A9E11D97EB6D007EC517 /* ImageSlideshow_framework */ = { - isa = PBXNativeTarget; - buildConfigurationList = D0E8A9E91D97EB6D007EC517 /* Build configuration list for PBXNativeTarget "ImageSlideshow_framework" */; - buildPhases = ( - D0E8A9DD1D97EB6D007EC517 /* Sources */, - D0E8A9DE1D97EB6D007EC517 /* Frameworks */, - D0E8A9DF1D97EB6D007EC517 /* Headers */, - D0E8A9E01D97EB6D007EC517 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ImageSlideshow_framework; - productName = ImageSlideshow_framework; - productReference = D0E8A9E21D97EB6D007EC517 /* ImageSlideshow.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 607FACC81AFB9204008FA782 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftMigration = 0700; - LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0940; - ORGANIZATIONNAME = CocoaPods; - TargetAttributes = { - 607FACCF1AFB9204008FA782 = { - CreatedOnToolsVersion = 6.3.1; - DevelopmentTeam = 5VWB99DS38; - LastSwiftMigration = 1020; - }; - 607FACE41AFB9204008FA782 = { - CreatedOnToolsVersion = 6.3.1; - DevelopmentTeam = 5VWB99DS38; - LastSwiftMigration = 1020; - TestTargetID = 607FACCF1AFB9204008FA782; - }; - D0E8A9E11D97EB6D007EC517 = { - CreatedOnToolsVersion = 8.0; - DevelopmentTeam = 9X4J83EL7M; - LastSwiftMigration = 0800; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageSlideshow" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - English, - en, - Base, - ); - mainGroup = 607FACC71AFB9204008FA782; - productRefGroup = 607FACD11AFB9204008FA782 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */, - 607FACE41AFB9204008FA782 /* ImageSlideshow_Tests */, - D0E8A9E11D97EB6D007EC517 /* ImageSlideshow_framework */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 607FACCE1AFB9204008FA782 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, - 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, - 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 607FACE31AFB9204008FA782 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0E8A9E01D97EB6D007EC517 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D00C7A2720B4C0A100E5725B /* ic_cross_white@3x.png in Resources */, - D00C7A2820B4C0A100E5725B /* ic_cross_white@2x.png in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 7EF425DA4D49DBDDA22C665D /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework", - "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", - "${BUILT_PRODUCTS_DIR}/AlamofireImage/AlamofireImage.framework", - "${BUILT_PRODUCTS_DIR}/ImageSlideshow/ImageSlideshow.framework", - "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework", - "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AlamofireImage.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ImageSlideshow.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ImageSlideshow_Example/Pods-ImageSlideshow_Example-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C32710DF8FE75A6D757948CC /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ImageSlideshow_Example-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - 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"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 607FACCC1AFB9204008FA782 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, - F802998F20CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */, - D007954F2043262C0053E25F /* TableViewController.swift in Sources */, - 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 607FACE11AFB9204008FA782 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, - F802999020CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - D0E8A9DD1D97EB6D007EC517 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D0E8A9F21D97EB94007EC517 /* ImageSlideshow.swift in Sources */, - D0E8A9F41D97EB94007EC517 /* InputSource.swift in Sources */, - D0E8A9F61D97EB94007EC517 /* UIImageView+Tools.swift in Sources */, - D0E8A9F71D97EB94007EC517 /* ZoomAnimatedTransitioning.swift in Sources */, - D0083DCE1EB739E700126B21 /* ActivityIndicator.swift in Sources */, - 43F0FAD42568BAEB008FFD39 /* Bundle+Module.swift in Sources */, - F802999120CE9EA7009D64DD /* PageIndicatorPosition.swift in Sources */, - D0E8A9F51D97EB94007EC517 /* UIImage+AspectFit.swift in Sources */, - D0E8A9F11D97EB94007EC517 /* FullScreenSlideshowViewController.swift in Sources */, - F539204C210F03610057EFB3 /* SwiftSupport.swift in Sources */, - D0B974B0202738F6006217CF /* PageIndicator.swift in Sources */, - D0E8A9F31D97EB94007EC517 /* ImageSlideshowItem.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 607FACCF1AFB9204008FA782 /* ImageSlideshow_Example */; - targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 607FACD91AFB9204008FA782 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 607FACDA1AFB9204008FA782 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 607FACDF1AFB9204008FA782 /* Base */, - ); - name = LaunchScreen.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 607FACED1AFB9204008FA782 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; - }; - name = Debug; - }; - 607FACEE1AFB9204008FA782 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.2; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 607FACF01AFB9204008FA782 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = FD45C56C18E7B8EC08371B86 /* Pods-ImageSlideshow_Example.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 5VWB99DS38; - INFOPLIST_FILE = ImageSlideshow/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MODULE_NAME = ExampleApp; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 607FACF11AFB9204008FA782 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 09417F1351C21E0DCE8667BE /* Pods-ImageSlideshow_Example.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 5VWB99DS38; - INFOPLIST_FILE = ImageSlideshow/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MODULE_NAME = ExampleApp; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 607FACF31AFB9204008FA782 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = 5VWB99DS38; - FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageSlideshow_Example.app/ImageSlideshow_Example"; - }; - name = Debug; - }; - 607FACF41AFB9204008FA782 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = 5VWB99DS38; - FRAMEWORK_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageSlideshow_Example.app/ImageSlideshow_Example"; - }; - name = Release; - }; - D0E8A9E71D97EB6D007EC517 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 9X4J83EL7M; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = ImageSlideshow_framework/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "io.zvo.ImageSlideshow-framework"; - PRODUCT_NAME = ImageSlideshow; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - D0E8A9E81D97EB6D007EC517 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ENABLE_MODULES = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_SUSPICIOUS_MOVES = YES; - CODE_SIGN_IDENTITY = ""; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 9X4J83EL7M; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = ImageSlideshow_framework/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "io.zvo.ImageSlideshow-framework"; - PRODUCT_NAME = ImageSlideshow; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ImageSlideshow" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 607FACED1AFB9204008FA782 /* Debug */, - 607FACEE1AFB9204008FA782 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 607FACF01AFB9204008FA782 /* Debug */, - 607FACF11AFB9204008FA782 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ImageSlideshow_Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 607FACF31AFB9204008FA782 /* Debug */, - 607FACF41AFB9204008FA782 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - D0E8A9E91D97EB6D007EC517 /* Build configuration list for PBXNativeTarget "ImageSlideshow_framework" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D0E8A9E71D97EB6D007EC517 /* Debug */, - D0E8A9E81D97EB6D007EC517 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 607FACC81AFB9204008FA782 /* Project object */; -} diff --git a/Example/ImageSlideshow.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/ImageSlideshow.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index c5192855..00000000 --- a/Example/ImageSlideshow.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow-Example.xcscheme b/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow-Example.xcscheme deleted file mode 100644 index 4218952b..00000000 --- a/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow-Example.xcscheme +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow_framework.xcscheme b/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow_framework.xcscheme deleted file mode 100644 index f1b33021..00000000 --- a/Example/ImageSlideshow.xcodeproj/xcshareddata/xcschemes/ImageSlideshow_framework.xcscheme +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Example/ImageSlideshow/ViewController.swift b/Example/ImageSlideshow/ViewController.swift index f4cb7aef..bb1aedf8 100644 --- a/Example/ImageSlideshow/ViewController.swift +++ b/Example/ImageSlideshow/ViewController.swift @@ -18,7 +18,6 @@ class ViewController: UIViewController { @IBOutlet var slideshow: ImageSlideshow! let localSource = [BundleImageSource(imageString: "img1"), BundleImageSource(imageString: "img2"), BundleImageSource(imageString: "img3"), BundleImageSource(imageString: "img4")] - 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")!] 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")!] 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")!] 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")!] @@ -28,7 +27,7 @@ class ViewController: UIViewController { slideshow.slideshowInterval = 5.0 slideshow.pageIndicatorPosition = .init(horizontal: .center, vertical: .under) - slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill + slideshow.contentScaleMode = .scaleAspectFill slideshow.pageIndicator = UIPageControl.withSlideshowColors() @@ -36,7 +35,7 @@ class ViewController: UIViewController { slideshow.activityIndicator = DefaultActivityIndicator() slideshow.delegate = self - // can be used with other sample sources as `afNetworkingSource`, `alamofireSource` or `sdWebImageSource` or `kingfisherSource` + // can be used with other sample sources as `alamofireSource`, `sdWebImageSource` or `kingfisherSource` slideshow.setImageInputs(localSource) let recognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap)) @@ -46,7 +45,7 @@ class ViewController: UIViewController { @objc func didTap() { let fullScreenController = slideshow.presentFullScreenController(from: self) // set the activity indicator for full screen controller (skipping the line will show no activity indicator) - fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil) + fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .medium, color: nil) } } diff --git a/Example/Podfile b/Example/Podfile index f36da01c..957106bd 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,10 +1,13 @@ -platform :ios, '10.0' +platform :ios, '15.0' use_frameworks! target 'ImageSlideshow_Example' do pod "ImageSlideshow", :path => "../" - pod "ImageSlideshow/AFURL", :path => "../" pod "ImageSlideshow/Alamofire", :path => "../" pod "ImageSlideshow/SDWebImage", :path => "../" pod "ImageSlideshow/Kingfisher", :path => "../" + + target 'ImageSlideshow_Tests' do + inherit! :search_paths + end end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index f652f1a9..25aa1c37 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,54 +1,32 @@ PODS: - - AFNetworking (3.2.1): - - AFNetworking/NSURLSession (= 3.2.1) - - AFNetworking/Reachability (= 3.2.1) - - AFNetworking/Security (= 3.2.1) - - AFNetworking/Serialization (= 3.2.1) - - AFNetworking/UIKit (= 3.2.1) - - AFNetworking/NSURLSession (3.2.1): - - AFNetworking/Reachability - - AFNetworking/Security - - AFNetworking/Serialization - - AFNetworking/Reachability (3.2.1) - - AFNetworking/Security (3.2.1) - - AFNetworking/Serialization (3.2.1) - - AFNetworking/UIKit (3.2.1): - - AFNetworking/NSURLSession - - Alamofire (5.2.1) - - AlamofireImage (4.1.0): - - Alamofire (~> 5.1) - - ImageSlideshow (1.9.0): - - ImageSlideshow/Core (= 1.9.0) - - ImageSlideshow/AFURL (1.9.0): - - AFNetworking (~> 3.0) - - ImageSlideshow/Core - - ImageSlideshow/Alamofire (1.9.0): + - Alamofire (5.11.2) + - AlamofireImage (4.4.0): + - Alamofire (~> 5.11) + - ImageSlideshow (1.9.4): + - ImageSlideshow/Core (= 1.9.4) + - ImageSlideshow/Alamofire (1.9.4): - AlamofireImage (~> 4.0) - ImageSlideshow/Core - - ImageSlideshow/Core (1.9.0) - - ImageSlideshow/Kingfisher (1.9.0): + - ImageSlideshow/Core (1.9.4) + - ImageSlideshow/Kingfisher (1.9.4): - ImageSlideshow/Core - - Kingfisher (> 3.0) - - ImageSlideshow/SDWebImage (1.9.0): + - Kingfisher (>= 8.3.2) + - ImageSlideshow/SDWebImage (1.9.4): - ImageSlideshow/Core - - SDWebImage (>= 3.7) - - Kingfisher (5.14.0): - - Kingfisher/Core (= 5.14.0) - - Kingfisher/Core (5.14.0) - - SDWebImage (5.8.1): - - SDWebImage/Core (= 5.8.1) - - SDWebImage/Core (5.8.1) + - SDWebImage (>= 5.1) + - Kingfisher (8.9.0) + - SDWebImage (5.21.7): + - SDWebImage/Core (= 5.21.7) + - SDWebImage/Core (5.21.7) DEPENDENCIES: - ImageSlideshow (from `../`) - - ImageSlideshow/AFURL (from `../`) - ImageSlideshow/Alamofire (from `../`) - ImageSlideshow/Kingfisher (from `../`) - ImageSlideshow/SDWebImage (from `../`) SPEC REPOS: trunk: - - AFNetworking - Alamofire - AlamofireImage - Kingfisher @@ -59,13 +37,12 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 - Alamofire: e911732990610fe89af59ac0077f923d72dc3dfd - AlamofireImage: c4a2ba349885fb3064feb74d2e547bd42ce9be10 - ImageSlideshow: a90cc3568a325cdf1bd543732a2cfcf6361d0dc1 - Kingfisher: 7b64389a43139c903ec434788344c288217c792d - SDWebImage: e3eae2eda88578db0685a0c88597fdadd9433f05 + Alamofire: 400275dd1c6d87d95a041861d2a59da4b520a203 + AlamofireImage: 19ec9c0991f52cc7f9ba6a9d4ec782e29bdf6210 + ImageSlideshow: 9778cb0ce8b1f8d14145d4c2934495e632249141 + Kingfisher: 90f3a3c2871a71e01d5493c1c196fc2d3ff132cf + SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf -PODFILE CHECKSUM: 46015634277c8c8bfd78d1b363e41ea2dfd179d9 +PODFILE CHECKSUM: 958bd53553061c42535e4a018e4bf567b60f6526 -COCOAPODS: 1.9.3 +COCOAPODS: 1.16.2 diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 66bf1af1..c50577e1 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -1,28 +1,193 @@ import UIKit import XCTest +@testable import ImageSlideshow -class Tests: XCTestCase { +/// Input source stub that synchronously reports the given image (nil = failure) +private class StubSource: NSObject, @preconcurrency InputSource { + let image: UIImage? - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. + init(image: UIImage?) { + self.image = image + super.init() } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() + @MainActor func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + imageView.image = image + callback(image) } +} + +private func solidImage() -> UIImage { + UIGraphicsBeginImageContext(CGSize(width: 1, height: 1)) + defer { UIGraphicsEndImageContext() } + return UIGraphicsGetImageFromCurrentImageContext()! +} + +@MainActor +class PagingTests: XCTestCase { + + private let width: CGFloat = 300 + + private func makeSlideshow(circular: Bool, count: Int) -> ImageSlideshow { + let slideshow = ImageSlideshow(frame: CGRect(x: 0, y: 0, width: width, height: 200)) + slideshow.circular = circular + slideshow.setImageInputs((0.. "https://github.com/zvonicek/ImageSlideshow.git", :tag => s.version.to_s } s.social_media_url = 'https://twitter.com/zvonicek' - s.swift_versions = ['4.0', '4.1', '4.2', '5', '5.1', '5.2'] - s.platform = :ios, '8.0' + s.swift_versions = ['5', '5.1', '5.2', '5.10', '6.0', '6.1'] + s.platform = :ios, '15.0' s.requires_arc = true s.subspec 'Core' do |core| @@ -38,7 +38,7 @@ Image slideshow is a Swift library providing customizable image slideshow with c s.subspec 'AFURL' do |subspec| subspec.dependency 'ImageSlideshow/Core' - subspec.dependency 'AFNetworking', '~> 3.0' + subspec.dependency 'AFNetworking', '~> 4.0' subspec.source_files = 'ImageSlideshow/Classes/InputSources/AFURLSource.swift' end @@ -51,29 +51,21 @@ Image slideshow is a Swift library providing customizable image slideshow with c s.subspec 'Alamofire' do |subspec| subspec.dependency 'ImageSlideshow/Core' subspec.dependency 'AlamofireImage', '~> 4.0' - subspec.platform = :ios, '10.0' subspec.source_files = 'ImageSlideshow/Classes/InputSources/AlamofireSource.swift' end s.subspec 'SDWebImage' do |subspec| subspec.dependency 'ImageSlideshow/Core' - subspec.dependency 'SDWebImage', '>= 3.7' + subspec.dependency 'SDWebImage', '>= 5.1' subspec.source_files = 'ImageSlideshow/Classes/InputSources/SDWebImageSource.swift' end s.subspec 'Kingfisher' do |subspec| subspec.dependency 'ImageSlideshow/Core' - subspec.dependency 'Kingfisher', '> 3.0' - subspec.platform = :ios, '10.0' + subspec.dependency 'Kingfisher', '>= 8.3.2' subspec.source_files = 'ImageSlideshow/Classes/InputSources/KingfisherSource.swift' end - s.subspec 'Parse' do |subspec| - subspec.dependency 'ImageSlideshow/Core' - subspec.dependency 'Parse', '~> 1.14' - subspec.source_files = 'ImageSlideshow/Classes/InputSources/ParseSource.swift' - end - s.default_subspec = 'Core' end diff --git a/ImageSlideshow/Classes/Core/ActivityIndicator.swift b/ImageSlideshow/Classes/Core/ActivityIndicator.swift index a0af2009..b6bcff33 100644 --- a/ImageSlideshow/Classes/Core/ActivityIndicator.swift +++ b/ImageSlideshow/Classes/Core/ActivityIndicator.swift @@ -25,7 +25,7 @@ public protocol ActivityIndicatorFactory { } /// Default ActivityIndicatorView implementation for UIActivityIndicatorView -extension UIActivityIndicatorView: ActivityIndicatorView { +extension UIActivityIndicatorView: @preconcurrency ActivityIndicatorView { public var view: UIView { return self } @@ -41,9 +41,9 @@ extension UIActivityIndicatorView: ActivityIndicatorView { /// Default activity indicator factory creating UIActivityIndicatorView instances @objcMembers -open class DefaultActivityIndicator: ActivityIndicatorFactory { +open class DefaultActivityIndicator: @preconcurrency ActivityIndicatorFactory { /// activity indicator style - open var style: UIActivityIndicatorViewStyle + open var style: UIActivityIndicatorView.Style /// activity indicator color open var color: UIColor? @@ -52,18 +52,17 @@ open class DefaultActivityIndicator: ActivityIndicatorFactory { /// /// - style: activity indicator style /// - color: activity indicator color - public init(style: UIActivityIndicatorViewStyle = .gray, color: UIColor? = nil) { + public init( + style: UIActivityIndicatorView.Style = .medium, + color: UIColor? = nil + ) { self.style = style self.color = color } /// create ActivityIndicatorView instance - open func create() -> ActivityIndicatorView { - #if swift(>=4.2) + @MainActor open func create() -> ActivityIndicatorView { let activityIndicator = UIActivityIndicatorView(style: style) - #else - let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: style) - #endif activityIndicator.color = color activityIndicator.hidesWhenStopped = true diff --git a/ImageSlideshow/Classes/Core/Bundle+Module.swift b/ImageSlideshow/Classes/Core/Bundle+Module.swift index 4ad0203f..dd47bf88 100644 --- a/ImageSlideshow/Classes/Core/Bundle+Module.swift +++ b/ImageSlideshow/Classes/Core/Bundle+Module.swift @@ -9,8 +9,6 @@ import Foundation #if !SWIFT_PACKAGE extension Bundle { - static var module: Bundle = { - return Bundle(for: ImageSlideshow.self) - }() + static let module = Bundle(for: ImageSlideshow.self) } #endif diff --git a/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift b/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift index 6aac7426..b38734d8 100644 --- a/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift +++ b/ImageSlideshow/Classes/Core/FullScreenSlideshowViewController.swift @@ -13,16 +13,21 @@ open class FullScreenSlideshowViewController: UIViewController { open var slideshow: ImageSlideshow = { let slideshow = ImageSlideshow() slideshow.zoomEnabled = true - slideshow.contentScaleMode = UIViewContentMode.scaleAspectFit - slideshow.pageIndicatorPosition = PageIndicatorPosition(horizontal: .center, vertical: .bottom) + slideshow.contentScaleMode = UIView.ContentMode.scaleAspectFit + slideshow.pageIndicatorPosition = PageIndicatorPosition( + horizontal: .center, + vertical: .bottom + ) // turns off the timer slideshow.slideshowInterval = 0 - slideshow.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight] + slideshow.autoresizingMask = [ + UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight, + ] return slideshow }() - /// Close button + /// Close button open var closeButton = UIButton() /// Close button frame @@ -34,7 +39,7 @@ open class FullScreenSlideshowViewController: UIViewController { /// Index of initial image open var initialPage: Int = 0 - /// Input sources to + /// Input sources to open var inputs: [InputSource]? /// Background color @@ -53,10 +58,7 @@ open class FullScreenSlideshowViewController: UIViewController { self.init(nibName: nil, bundle: nil) self.modalPresentationStyle = .custom - if #available(iOS 13.0, *) { - // Use KVC to set the value to preserve backwards compatiblity with Xcode < 11 - self.setValue(true, forKey: "modalInPresentation") - } + self.isModalInPresentation = true } override open func viewDidLoad() { @@ -72,8 +74,15 @@ open class FullScreenSlideshowViewController: UIViewController { view.addSubview(slideshow) // close button configuration - closeButton.setImage(UIImage(named: "ic_cross_white", in: .module, compatibleWith: nil), for: UIControlState()) - closeButton.addTarget(self, action: #selector(FullScreenSlideshowViewController.close), for: UIControlEvents.touchUpInside) + closeButton.setImage( + UIImage(named: "ic_cross_white", in: .module, compatibleWith: nil), + for: UIControl.State() + ) + closeButton.addTarget( + self, + action: #selector(FullScreenSlideshowViewController.close), + for: UIControl.Event.touchUpInside + ) view.addSubview(closeButton) } @@ -102,13 +111,16 @@ open class FullScreenSlideshowViewController: UIViewController { open override func viewDidLayoutSubviews() { if !isBeingDismissed { let safeAreaInsets: UIEdgeInsets - if #available(iOS 11.0, *) { - safeAreaInsets = view.safeAreaInsets - } else { - safeAreaInsets = UIEdgeInsets.zero - } - - closeButton.frame = closeButtonFrame ?? CGRect(x: max(10, safeAreaInsets.left), y: max(10, safeAreaInsets.top), width: 40, height: 40) + safeAreaInsets = view.safeAreaInsets + + closeButton.frame = + closeButtonFrame + ?? CGRect( + x: max(10, safeAreaInsets.left), + y: max(10, safeAreaInsets.top), + width: 40, + height: 40 + ) } slideshow.frame = view.frame diff --git a/ImageSlideshow/Classes/Core/ImageSlideshow.swift b/ImageSlideshow/Classes/Core/ImageSlideshow.swift index 936a002e..4c7baac8 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshow.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshow.swift @@ -9,32 +9,38 @@ import UIKit @objc /// The delegate protocol informing about image slideshow state changes -public protocol ImageSlideshowDelegate: class { +public protocol ImageSlideshowDelegate: AnyObject { /// Tells the delegate that the current page has changed /// /// - Parameters: /// - imageSlideshow: image slideshow instance /// - page: new page - @objc optional func imageSlideshow(_ imageSlideshow: ImageSlideshow, didChangeCurrentPageTo page: Int) + @objc optional func imageSlideshow( + _ imageSlideshow: ImageSlideshow, + didChangeCurrentPageTo page: Int + ) /// Tells the delegate that the slideshow will begin dragging /// /// - Parameter imageSlideshow: image slideshow instance - @objc optional func imageSlideshowWillBeginDragging(_ imageSlideshow: ImageSlideshow) + @objc optional func imageSlideshowWillBeginDragging( + _ imageSlideshow: ImageSlideshow + ) /// Tells the delegate that the slideshow did end decelerating /// /// - Parameter imageSlideshow: image slideshow instance - @objc optional func imageSlideshowDidEndDecelerating(_ imageSlideshow: ImageSlideshow) + @objc optional func imageSlideshowDidEndDecelerating( + _ imageSlideshow: ImageSlideshow + ) } -/** - Used to represent position of the Page Control - - hidden: Page Control is hidden - - insideScrollView: Page Control is inside image slideshow - - underScrollView: Page Control is under image slideshow - - custom: Custom vertical padding, relative to "insideScrollView" position - */ +/// +/// Used to represent position of the Page Control +/// - hidden: Page Control is hidden +/// - insideScrollView: Page Control is inside image slideshow +/// - underScrollView: Page Control is under image slideshow +/// - custom: Custom vertical padding, relative to "insideScrollView" position public enum PageControlPosition { case hidden case insideScrollView @@ -80,14 +86,20 @@ open class ImageSlideshow: UIView { if let pageIndicator = pageIndicator { addSubview(pageIndicator.view) if let pageIndicator = pageIndicator as? UIControl { - pageIndicator.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged) + pageIndicator.addTarget( + self, + action: #selector(pageControlValueChanged), + for: .valueChanged + ) } } setNeedsLayout() } } - open var pageIndicatorPosition: PageIndicatorPosition = PageIndicatorPosition() { + open var pageIndicatorPosition: PageIndicatorPosition = + PageIndicatorPosition() + { didSet { setNeedsLayout() } @@ -108,7 +120,9 @@ open class ImageSlideshow: UIView { case .underScrollView: pageIndicatorPosition = PageIndicatorPosition(vertical: .under) case .custom(let padding): - pageIndicatorPosition = PageIndicatorPosition(vertical: .customUnder(padding: padding-30)) + pageIndicatorPosition = PageIndicatorPosition( + vertical: .customUnder(padding: padding - 30) + ) } } } @@ -119,7 +133,10 @@ open class ImageSlideshow: UIView { if oldValue != currentPage { pageIndicator?.page = currentPage currentPageChanged?(currentPage) - delegate?.imageSlideshow?(self, didChangeCurrentPageTo: currentPage) + delegate?.imageSlideshow?( + self, + didChangeCurrentPageTo: currentPage + ) } } } @@ -199,7 +216,9 @@ open class ImageSlideshow: UIView { open var preload = ImagePreload.all /// Content mode of each image in the slideshow - open var contentScaleMode: UIViewContentMode = UIViewContentMode.scaleAspectFit { + open var contentScaleMode: UIView.ContentMode = + .scaleAspectFit + { didSet { for view in slideshowItems { view.imageView.contentMode = contentScaleMode @@ -212,10 +231,13 @@ open class ImageSlideshow: UIView { fileprivate var isAnimating: Bool = false /// Transitioning delegate to manage the transition to full screen controller - open fileprivate(set) var slideshowTransitioningDelegate: ZoomAnimatedTransitioningDelegate? // swiftlint:disable:this weak_delegate + open fileprivate(set) var slideshowTransitioningDelegate: + ZoomAnimatedTransitioningDelegate? // swiftlint:disable:this weak_delegate private var primaryVisiblePage: Int { - return scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x + scrollView.frame.size.width / 2) / Int(scrollView.frame.size.width) : 0 + return scrollView.frame.size.width > 0 + ? Int(scrollView.contentOffset.x + scrollView.frame.size.width / 2) + / Int(scrollView.frame.size.width) : 0 } // MARK: - Life cycle @@ -237,12 +259,15 @@ open class ImageSlideshow: UIView { fileprivate func initialize() { autoresizesSubviews = true clipsToBounds = true - if #available(iOS 13.0, *) { - backgroundColor = .systemBackground - } + backgroundColor = .systemBackground // scroll view configuration - scrollView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height - 50.0) + scrollView.frame = CGRect( + x: 0, + y: 0, + width: frame.size.width, + height: frame.size.height - 50.0 + ) scrollView.delegate = self scrollView.isPagingEnabled = true scrollView.bounces = true @@ -250,12 +275,12 @@ open class ImageSlideshow: UIView { scrollView.showsVerticalScrollIndicator = false scrollView.autoresizingMask = autoresizingMask if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft { - scrollView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi)) + scrollView.transform = CGAffineTransform( + rotationAngle: CGFloat(Double.pi) + ) } - if #available(iOS 11.0, *) { - scrollView.contentInsetAdjustmentBehavior = .never - } + scrollView.contentInsetAdjustmentBehavior = .never addSubview(scrollView) if pageIndicator == nil { @@ -286,28 +311,47 @@ open class ImageSlideshow: UIView { pageIndicatorView.isHidden = images.count < 2 var edgeInsets: UIEdgeInsets = UIEdgeInsets.zero - if #available(iOS 11.0, *) { - edgeInsets = safeAreaInsets - } + edgeInsets = safeAreaInsets pageIndicatorView.sizeToFit() - pageIndicatorView.frame = pageIndicatorPosition.indicatorFrame(for: frame, indicatorSize: pageIndicatorView.frame.size, edgeInsets: edgeInsets) + pageIndicatorView.frame = pageIndicatorPosition.indicatorFrame( + for: frame, + indicatorSize: pageIndicatorView.frame.size, + edgeInsets: edgeInsets + ) } } /// updates frame of the scroll view and its inner items func layoutScrollView() { let pageIndicatorViewSize = pageIndicator?.view.frame.size - let scrollViewBottomPadding = pageIndicatorViewSize.flatMap { pageIndicatorPosition.underPadding(for: $0) } ?? 0 - - scrollView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height - scrollViewBottomPadding) - scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(scrollViewImages.count), height: scrollView.frame.size.height) + let scrollViewBottomPadding = + pageIndicatorViewSize.flatMap { + pageIndicatorPosition.underPadding(for: $0) + } ?? 0 + + scrollView.frame = CGRect( + x: 0, + y: 0, + width: frame.size.width, + height: frame.size.height - scrollViewBottomPadding + ) + scrollView.contentSize = CGSize( + width: scrollView.frame.size.width + * CGFloat(scrollViewImages.count), + height: scrollView.frame.size.height + ) for (index, view) in slideshowItems.enumerated() { if !view.zoomInInitially { view.zoomOut() } - view.frame = CGRect(x: scrollView.frame.size.width * CGFloat(index), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height) + view.frame = CGRect( + x: scrollView.frame.size.width * CGFloat(index), + y: 0, + width: scrollView.frame.size.width, + height: scrollView.frame.size.height + ) } setScrollViewPage(scrollViewPage, animated: false) @@ -323,7 +367,12 @@ open class ImageSlideshow: UIView { var i = 0 for image in scrollViewImages { - let item = ImageSlideshowItem(image: image, zoomEnabled: zoomEnabled, activityIndicator: activityIndicator?.create(), maximumScale: maximumScale) + let item = ImageSlideshowItem( + image: image, + zoomEnabled: zoomEnabled, + activityIndicator: activityIndicator?.create(), + maximumScale: maximumScale + ) item.imageView.contentMode = contentScaleMode slideshowItems.append(item) scrollView.addSubview(item) @@ -332,7 +381,15 @@ open class ImageSlideshow: UIView { if circular && (scrollViewImages.count > 1) { scrollViewPage = 1 - scrollView.scrollRectToVisible(CGRect(x: scrollView.frame.size.width, y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height), animated: false) + scrollView.scrollRectToVisible( + CGRect( + x: scrollView.frame.size.width, + y: 0, + width: scrollView.frame.size.width, + height: scrollView.frame.size.height + ), + animated: false + ) } else { scrollViewPage = 0 } @@ -350,10 +407,17 @@ open class ImageSlideshow: UIView { item.loadImage() case .fixed(let offset): // if circular scrolling is enabled and image is on the edge, a helper ("dummy") image on the other side needs to be loaded too - let circularEdgeLoad = circular && ((scrollViewPage == 0 && i == totalCount-3) || (scrollViewPage == 0 && i == totalCount-2) || (scrollViewPage == totalCount-2 && i == 1)) + let circularEdgeLoad = + circular + && ((scrollViewPage == 0 && i == totalCount - 3) + || (scrollViewPage == 0 && i == totalCount - 2) + || (scrollViewPage == totalCount - 2 && i == 1)) // load image if page is in range of loadOffset, else release image - let shouldLoad = abs(scrollViewPage-i) <= offset || abs(scrollViewPage-i) > totalCount-offset || circularEdgeLoad + let shouldLoad = + abs(scrollViewPage - i) <= offset + || abs(scrollViewPage - i) > totalCount - offset + || circularEdgeLoad shouldLoad ? item.loadImage() : item.releaseImage() } } @@ -415,7 +479,15 @@ open class ImageSlideshow: UIView { */ open func setScrollViewPage(_ newScrollViewPage: Int, animated: Bool) { if scrollViewPage < scrollViewImages.count { - scrollView.scrollRectToVisible(CGRect(x: scrollView.frame.size.width * CGFloat(newScrollViewPage), y: 0, width: scrollView.frame.size.width, height: scrollView.frame.size.height), animated: animated) + scrollView.scrollRectToVisible( + CGRect( + x: scrollView.frame.size.width * CGFloat(newScrollViewPage), + y: 0, + width: scrollView.frame.size.width, + height: scrollView.frame.size.height + ), + animated: animated + ) setCurrentPageForScrollViewPage(newScrollViewPage) if animated { isAnimating = true @@ -424,13 +496,23 @@ open class ImageSlideshow: UIView { } fileprivate func setTimerIfNeeded() { - if slideshowInterval > 0 && scrollViewImages.count > 1 && slideshowTimer == nil { - slideshowTimer = Timer.scheduledTimer(timeInterval: slideshowInterval, target: self, selector: #selector(ImageSlideshow.slideshowTick(_:)), userInfo: nil, repeats: true) + if slideshowInterval > 0 && scrollViewImages.count > 1 + && slideshowTimer == nil + { + slideshowTimer = Timer.scheduledTimer( + timeInterval: slideshowInterval, + target: self, + selector: #selector(ImageSlideshow.slideshowTick(_:)), + userInfo: nil, + repeats: true + ) } } func slideshowTick(_ timer: Timer) { - let page = scrollView.frame.size.width > 0 ? Int(scrollView.contentOffset.x / scrollView.frame.size.width) : 0 + let page = + scrollView.frame.size.width > 0 + ? Int(scrollView.contentOffset.x / scrollView.frame.size.width) : 0 var nextPage = page + 1 if !circular && page == scrollViewImages.count - 1 { @@ -529,7 +611,8 @@ open class ImageSlideshow: UIView { return } - let newPage = scrollViewPage > 0 ? scrollViewPage - 1 : scrollViewImages.count - 3 + let newPage = + scrollViewPage > 0 ? scrollViewPage - 1 : scrollViewImages.count - 3 setScrollViewPage(newPage, animated: animated) restartTimer() } @@ -540,15 +623,21 @@ open class ImageSlideshow: UIView { - returns: FullScreenSlideshowViewController instance */ @discardableResult - open func presentFullScreenController(from controller: UIViewController, completion: (() -> Void)? = nil) -> FullScreenSlideshowViewController { + open func presentFullScreenController( + from controller: UIViewController, + completion: (() -> Void)? = nil + ) -> FullScreenSlideshowViewController { let fullscreen = FullScreenSlideshowViewController() - fullscreen.pageSelected = {[weak self] (page: Int) in + fullscreen.pageSelected = { [weak self] (page: Int) in self?.setCurrentPage(page, animated: false) } fullscreen.initialPage = currentPage fullscreen.inputs = images - slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate(slideshowView: self, slideshowController: fullscreen) + slideshowTransitioningDelegate = ZoomAnimatedTransitioningDelegate( + slideshowView: self, + slideshowController: fullscreen + ) fullscreen.transitioningDelegate = slideshowTransitioningDelegate fullscreen.modalPresentationStyle = .custom controller.present(fullscreen, animated: true, completion: completion) @@ -579,19 +668,30 @@ extension ImageSlideshow: UIScrollViewDelegate { open func scrollViewDidScroll(_ scrollView: UIScrollView) { if circular && (scrollViewImages.count > 1) { - let regularContentOffset = scrollView.frame.size.width * CGFloat(images.count) - - if scrollView.contentOffset.x >= scrollView.frame.size.width * CGFloat(images.count + 1) { - scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x - regularContentOffset, y: 0) + let regularContentOffset = + scrollView.frame.size.width * CGFloat(images.count) + + if scrollView.contentOffset.x >= scrollView.frame.size.width + * CGFloat(images.count + 1) + { + scrollView.contentOffset = CGPoint( + x: scrollView.contentOffset.x - regularContentOffset, + y: 0 + ) } else if scrollView.contentOffset.x <= 0 { - scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x + regularContentOffset, y: 0) + scrollView.contentOffset = CGPoint( + x: scrollView.contentOffset.x + regularContentOffset, + y: 0 + ) } } // Updates the page indicator as the user scrolls (#204). Not called when not dragging to prevent flickers // when interacting with PageControl directly (#376). if scrollView.isDragging { - pageIndicator?.page = currentPageForScrollViewPage(primaryVisiblePage) + pageIndicator?.page = currentPageForScrollViewPage( + primaryVisiblePage + ) } } diff --git a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift index 7b2136a8..a7c8d3f4 100644 --- a/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift +++ b/ImageSlideshow/Classes/Core/ImageSlideshowItem.swift @@ -53,7 +53,12 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { - parameter image: Input Source to load the image - parameter zoomEnabled: holds if it should be possible to zoom-in the image */ - init(image: InputSource, zoomEnabled: Bool, activityIndicator: ActivityIndicatorView? = nil, maximumScale: CGFloat = 2.0) { + init( + image: InputSource, + zoomEnabled: Bool, + activityIndicator: ActivityIndicatorView? = nil, + maximumScale: CGFloat = 2.0 + ) { self.zoomEnabled = zoomEnabled self.image = image self.activityIndicator = activityIndicator @@ -65,14 +70,14 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight] imageView.isAccessibilityElement = true imageView.accessibilityTraits = .image - if #available(iOS 11.0, *) { - imageView.accessibilityIgnoresInvertColors = true - } + imageView.accessibilityIgnoresInvertColors = true imageViewWrapper.clipsToBounds = true imageViewWrapper.isUserInteractionEnabled = true if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft { - imageView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi)) + imageView.transform = CGAffineTransform( + rotationAngle: CGFloat(Double.pi) + ) } setPictoCenter() @@ -90,12 +95,18 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { } // tap gesture recognizer - let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ImageSlideshowItem.tapZoom)) + let tapRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(ImageSlideshowItem.tapZoom) + ) tapRecognizer.numberOfTapsRequired = 2 imageViewWrapper.addGestureRecognizer(tapRecognizer) gestureRecognizer = tapRecognizer - singleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(retryLoadImage)) + singleTapGestureRecognizer = UITapGestureRecognizer( + target: self, + action: #selector(retryLoadImage) + ) singleTapGestureRecognizer!.numberOfTapsRequired = 1 singleTapGestureRecognizer!.isEnabled = false imageViewWrapper.addGestureRecognizer(singleTapGestureRecognizer!) @@ -139,7 +150,7 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { isLoading = true imageReleased = false activityIndicator?.show() - image.load(to: self.imageView) {[weak self] image in + image.load(to: self.imageView) { [weak self] image in // set image to nil if there was a release request during the image load if let imageRelease = self?.imageReleased, imageRelease { self?.imageView.image = nil @@ -192,18 +203,28 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { } fileprivate func calculatePictureSize() -> CGSize { - if let image = imageView.image, imageView.contentMode == .scaleAspectFit { + if let image = imageView.image, imageView.contentMode == .scaleAspectFit + { let picSize = image.size let picRatio = picSize.width / picSize.height let screenRatio = screenSize().width / screenSize().height if picRatio > screenRatio { - return CGSize(width: screenSize().width, height: screenSize().width / picSize.width * picSize.height) + return CGSize( + width: screenSize().width, + height: screenSize().width / picSize.width * picSize.height + ) } else { - return CGSize(width: screenSize().height / picSize.height * picSize.width, height: screenSize().height) + return CGSize( + width: screenSize().height / picSize.height * picSize.width, + height: screenSize().height + ) } } else { - return CGSize(width: screenSize().width, height: screenSize().height) + return CGSize( + width: screenSize().width, + height: screenSize().height + ) } } @@ -212,15 +233,23 @@ open class ImageSlideshowItem: UIScrollView, UIScrollViewDelegate { } fileprivate func setPictoCenter() { - var intendHorizon = (screenSize().width - imageViewWrapper.frame.width ) / 2 - var intendVertical = (screenSize().height - imageViewWrapper.frame.height ) / 2 + var intendHorizon = + (screenSize().width - imageViewWrapper.frame.width) / 2 + var intendVertical = + (screenSize().height - imageViewWrapper.frame.height) / 2 intendHorizon = intendHorizon > 0 ? intendHorizon : 0 intendVertical = intendVertical > 0 ? intendVertical : 0 - contentInset = UIEdgeInsets(top: intendVertical, left: intendHorizon, bottom: intendVertical, right: intendHorizon) + contentInset = UIEdgeInsets( + top: intendVertical, + left: intendHorizon, + bottom: intendVertical, + right: intendHorizon + ) } private func isFullScreen() -> Bool { - return imageViewWrapper.frame.width >= screenSize().width && imageViewWrapper.frame.height >= screenSize().height + return imageViewWrapper.frame.width >= screenSize().width + && imageViewWrapper.frame.height >= screenSize().height } func clearContentInsets() { diff --git a/ImageSlideshow/Classes/Core/InputSource.swift b/ImageSlideshow/Classes/Core/InputSource.swift index 3689af2a..2ed64098 100644 --- a/ImageSlideshow/Classes/Core/InputSource.swift +++ b/ImageSlideshow/Classes/Core/InputSource.swift @@ -27,7 +27,7 @@ import UIKit /// Input Source to load plain UIImage @objcMembers -open class ImageSource: NSObject, InputSource { +open class ImageSource: NSObject, @preconcurrency InputSource { var image: UIImage /// Initializes a new Image Source with UIImage @@ -48,7 +48,7 @@ open class ImageSource: NSObject, InputSource { } } - public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + @MainActor public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { imageView.image = image callback(image) } @@ -56,7 +56,7 @@ open class ImageSource: NSObject, InputSource { /// Input Source to load an image from the main bundle @objcMembers -open class BundleImageSource: NSObject, InputSource { +open class BundleImageSource: NSObject, @preconcurrency InputSource { var imageString: String /// Initializes a new Image Source with an image name from the main bundle @@ -66,7 +66,7 @@ open class BundleImageSource: NSObject, InputSource { super.init() } - public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + @MainActor public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { let image = UIImage(named: imageString) imageView.image = image callback(image) @@ -75,7 +75,7 @@ open class BundleImageSource: NSObject, InputSource { /// Input Source to load an image from a local file path @objcMembers -open class FileImageSource: NSObject, InputSource { +open class FileImageSource: NSObject, @preconcurrency InputSource { var path: String /// Initializes a new Image Source with an image name from the main bundle @@ -85,7 +85,7 @@ open class FileImageSource: NSObject, InputSource { super.init() } - public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + @MainActor public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { let image = UIImage(contentsOfFile: path) imageView.image = image callback(image) diff --git a/ImageSlideshow/Classes/Core/PageIndicator.swift b/ImageSlideshow/Classes/Core/PageIndicator.swift index d9863e91..9c49a052 100644 --- a/ImageSlideshow/Classes/Core/PageIndicator.swift +++ b/ImageSlideshow/Classes/Core/PageIndicator.swift @@ -8,7 +8,7 @@ import UIKit /// Cusotm Page Indicator can be used by implementing this protocol -public protocol PageIndicatorView: class { +public protocol PageIndicatorView: AnyObject { /// View of the page indicator var view: UIView { get } @@ -16,10 +16,10 @@ public protocol PageIndicatorView: class { var page: Int { get set } /// Total number of pages of the page indicator - var numberOfPages: Int { get set} + var numberOfPages: Int { get set } } -extension UIPageControl: PageIndicatorView { +extension UIPageControl: @preconcurrency PageIndicatorView { public var view: UIView { return self } @@ -43,20 +43,12 @@ extension UIPageControl: PageIndicatorView { public static func withSlideshowColors() -> UIPageControl { let pageControl = UIPageControl() - if #available(iOS 13.0, *) { - pageControl.currentPageIndicatorTintColor = UIColor { traits in - traits.userInterfaceStyle == .dark ? .white : .lightGray - } - } else { - pageControl.currentPageIndicatorTintColor = .lightGray + pageControl.currentPageIndicatorTintColor = UIColor { traits in + traits.userInterfaceStyle == .dark ? .white : .lightGray } - - if #available(iOS 13.0, *) { - pageControl.pageIndicatorTintColor = UIColor { traits in - traits.userInterfaceStyle == .dark ? .systemGray : .black - } - } else { - pageControl.pageIndicatorTintColor = .black + + pageControl.pageIndicatorTintColor = UIColor { traits in + traits.userInterfaceStyle == .dark ? .systemGray : .black } return pageControl @@ -64,7 +56,7 @@ extension UIPageControl: PageIndicatorView { } /// Page indicator that shows page in numeric style, eg. "5/21" -public class LabelPageIndicator: UILabel, PageIndicatorView { +public class LabelPageIndicator: UILabel, @preconcurrency PageIndicatorView { public var view: UIView { return self } @@ -100,7 +92,10 @@ public class LabelPageIndicator: UILabel, PageIndicatorView { } public override func sizeToFit() { - let maximumString = String(repeating: "8", count: numberOfPages) as NSString - self.frame.size = maximumString.size(withAttributes: [.font: font as Any]) + let maximumString = + String(repeating: "8", count: numberOfPages) as NSString + self.frame.size = maximumString.size(withAttributes: [ + .font: font as Any + ]) } } diff --git a/ImageSlideshow/Classes/Core/SwiftSupport.swift b/ImageSlideshow/Classes/Core/SwiftSupport.swift deleted file mode 100644 index 83f024b8..00000000 --- a/ImageSlideshow/Classes/Core/SwiftSupport.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// SwiftSupport.swift -// ImageSlideshow -// -// Created by Pierluigi Cifani on 30/07/2018. -// - -import UIKit - -#if swift(>=4.2) -public typealias UIViewContentMode = UIView.ContentMode -public typealias UIActivityIndicatorViewStyle = UIActivityIndicatorView.Style -typealias UIControlState = UIControl.State -typealias UIViewAnimationOptions = UIView.AnimationOptions -typealias UIControlEvents = UIControl.Event -typealias UIViewAutoresizing = UIView.AutoresizingMask -#else -#endif diff --git a/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift b/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift index 9b29afae..f0abfe33 100644 --- a/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift +++ b/ImageSlideshow/Classes/Core/ZoomAnimatedTransitioning.swift @@ -9,7 +9,9 @@ import UIKit @objcMembers -open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { +open class ZoomAnimatedTransitioningDelegate: NSObject, + UIViewControllerTransitioningDelegate +{ /// parent image view used for animated transition open var referenceImageView: UIImageView? /// parent slideshow view used for animated transition @@ -30,7 +32,10 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti - parameter slideshowView: ImageSlideshow instance to animate the transition from - parameter slideshowController: FullScreenViewController instance to animate the transition to */ - public init(slideshowView: ImageSlideshow, slideshowController: FullScreenSlideshowViewController) { + public init( + slideshowView: ImageSlideshow, + slideshowController: FullScreenSlideshowViewController + ) { self.referenceSlideshowView = slideshowView self.referenceSlideshowController = slideshowController @@ -44,7 +49,10 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti - parameter imageView: UIImageView instance to animate the transition from - parameter slideshowController: FullScreenViewController instance to animate the transition to */ - public init(imageView: UIImageView, slideshowController: FullScreenSlideshowViewController) { + public init( + imageView: UIImageView, + slideshowController: FullScreenSlideshowViewController + ) { self.referenceImageView = imageView self.referenceSlideshowController = slideshowController @@ -55,36 +63,59 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti func initialize() { // Pan gesture recognizer for interactive dismiss - gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(ZoomAnimatedTransitioningDelegate.handleSwipe(_:))) + gestureRecognizer = UIPanGestureRecognizer( + target: self, + action: #selector(ZoomAnimatedTransitioningDelegate.handleSwipe(_:)) + ) gestureRecognizer.delegate = self + // Append it to a window otherwise it will be canceled during the transition - UIApplication.shared.keyWindow?.addGestureRecognizer(gestureRecognizer) + let keyWindow = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .flatMap { $0.windows } + .first { $0.isKeyWindow } + keyWindow?.addGestureRecognizer(gestureRecognizer) } func handleSwipe(_ gesture: UIPanGestureRecognizer) { - guard let referenceSlideshowController = referenceSlideshowController else { + guard let referenceSlideshowController = referenceSlideshowController + else { return } - let percent = min(max(abs(gesture.translation(in: gesture.view!).y) / 200.0, 0.0), 1.0) + let percent = min( + max(abs(gesture.translation(in: gesture.view!).y) / 200.0, 0.0), + 1.0 + ) if gesture.state == .began { interactionController = UIPercentDrivenInteractiveTransition() - referenceSlideshowController.dismiss(animated: true, completion: nil) + referenceSlideshowController.dismiss( + animated: true, + completion: nil + ) } else if gesture.state == .changed { interactionController?.update(percent) - } else if gesture.state == .ended || gesture.state == .cancelled || gesture.state == .failed { + } else if gesture.state == .ended || gesture.state == .cancelled + || gesture.state == .failed + { let velocity = gesture.velocity(in: referenceSlideshowView) if abs(velocity.y) > 500 { - if let pageSelected = referenceSlideshowController.pageSelected { - pageSelected(referenceSlideshowController.slideshow.currentPage) + if let pageSelected = referenceSlideshowController.pageSelected + { + pageSelected( + referenceSlideshowController.slideshow.currentPage + ) } interactionController?.finish() } else if percent > 0.5 { - if let pageSelected = referenceSlideshowController.pageSelected { - pageSelected(referenceSlideshowController.slideshow.currentPage) + if let pageSelected = referenceSlideshowController.pageSelected + { + pageSelected( + referenceSlideshowController.slideshow.currentPage + ) } interactionController?.finish() @@ -96,9 +127,16 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti } } - open func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + open func animationController( + forPresented presented: UIViewController, + presenting: UIViewController, + source: UIViewController + ) -> UIViewControllerAnimatedTransitioning? { if let reference = referenceSlideshowView { - return ZoomInAnimator(referenceSlideshowView: reference, parent: self) + return ZoomInAnimator( + referenceSlideshowView: reference, + parent: self + ) } else if let reference = referenceImageView { return ZoomInAnimator(referenceImageView: reference, parent: self) } else { @@ -106,9 +144,14 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti } } - open func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + open func animationController(forDismissed dismissed: UIViewController) + -> UIViewControllerAnimatedTransitioning? + { if let reference = referenceSlideshowView { - return ZoomOutAnimator(referenceSlideshowView: reference, parent: self) + return ZoomOutAnimator( + referenceSlideshowView: reference, + parent: self + ) } else if let reference = referenceImageView { return ZoomOutAnimator(referenceImageView: reference, parent: self) } else { @@ -116,16 +159,27 @@ open class ZoomAnimatedTransitioningDelegate: NSObject, UIViewControllerTransiti } } - open func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + open func interactionControllerForPresentation( + using animator: UIViewControllerAnimatedTransitioning + ) -> UIViewControllerInteractiveTransitioning? { return interactionController } - open func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + open func interactionControllerForDismissal( + using animator: UIViewControllerAnimatedTransitioning + ) -> UIViewControllerInteractiveTransitioning? { return interactionController } - public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { - return PresentationController(presentedViewController: presented, presenting: presenting) + public func presentationController( + forPresented presented: UIViewController, + presenting: UIViewController?, + source: UIViewController + ) -> UIPresentationController? { + return PresentationController( + presentedViewController: presented, + presenting: presenting + ) } } @@ -137,8 +191,12 @@ private class PresentationController: UIPresentationController { } extension ZoomAnimatedTransitioningDelegate: UIGestureRecognizerDelegate { - public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { + public func gestureRecognizerShouldBegin( + _ gestureRecognizer: UIGestureRecognizer + ) -> Bool { + guard + let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer + else { return false } @@ -146,7 +204,9 @@ extension ZoomAnimatedTransitioningDelegate: UIGestureRecognizerDelegate { return false } - if let currentItem = referenceSlideshowController?.slideshow.currentSlideshowItem, currentItem.isZoomed() { + if let currentItem = referenceSlideshowController?.slideshow + .currentSlideshowItem, currentItem.isZoomed() + { return false } @@ -166,14 +226,21 @@ class ZoomAnimator: NSObject { var referenceSlideshowView: ImageSlideshow? var parent: ZoomAnimatedTransitioningDelegate - init(referenceSlideshowView: ImageSlideshow, parent: ZoomAnimatedTransitioningDelegate) { + init( + referenceSlideshowView: ImageSlideshow, + parent: ZoomAnimatedTransitioningDelegate + ) { self.referenceSlideshowView = referenceSlideshowView - self.referenceImageView = referenceSlideshowView.currentSlideshowItem?.imageView + self.referenceImageView = + referenceSlideshowView.currentSlideshowItem?.imageView self.parent = parent super.init() } - init(referenceImageView: UIImageView, parent: ZoomAnimatedTransitioningDelegate) { + init( + referenceImageView: UIImageView, + parent: ZoomAnimatedTransitioningDelegate + ) { self.referenceImageView = referenceImageView self.parent = parent super.init() @@ -183,32 +250,41 @@ class ZoomAnimator: NSObject { @objcMembers class ZoomInAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + func transitionDuration( + using transitionContext: UIViewControllerContextTransitioning? + ) -> TimeInterval { return 0.5 } - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + func animateTransition( + using transitionContext: UIViewControllerContextTransitioning + ) { // Pauses slideshow self.referenceSlideshowView?.pauseTimer() let containerView = transitionContext.containerView - let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! - - guard let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as? FullScreenSlideshowViewController else { + let fromViewController = transitionContext.viewController( + forKey: UITransitionContextViewControllerKey.from + )! + + guard + let toViewController = transitionContext.viewController( + forKey: UITransitionContextViewControllerKey.to + ) as? FullScreenSlideshowViewController + else { return } - toViewController.view.frame = transitionContext.finalFrame(for: toViewController) + toViewController.view.frame = transitionContext.finalFrame( + for: toViewController + ) let transitionBackgroundView = UIView(frame: containerView.frame) - transitionBackgroundView.backgroundColor = toViewController.backgroundColor + transitionBackgroundView.backgroundColor = + toViewController.backgroundColor containerView.addSubview(transitionBackgroundView) - #if swift(>=4.2) containerView.sendSubviewToBack(transitionBackgroundView) - #else - containerView.sendSubview(toBack: transitionBackgroundView) - #endif let finalFrame = toViewController.view.frame @@ -216,37 +292,64 @@ class ZoomInAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { var transitionViewFinalFrame = finalFrame if let referenceImageView = referenceImageView { transitionView = UIImageView(image: referenceImageView.image) - transitionView!.contentMode = UIViewContentMode.scaleAspectFill + transitionView!.contentMode = .scaleAspectFill transitionView!.clipsToBounds = true - transitionView!.frame = containerView.convert(referenceImageView.bounds, from: referenceImageView) + transitionView!.frame = containerView.convert( + referenceImageView.bounds, + from: referenceImageView + ) containerView.addSubview(transitionView!) self.parent.referenceSlideshowViewFrame = transitionView!.frame referenceImageView.alpha = 0 if let image = referenceImageView.image { - transitionViewFinalFrame = image.tgr_aspectFitRectForSize(finalFrame.size) + transitionViewFinalFrame = image.tgr_aspectFitRectForSize( + finalFrame.size + ) } } - if let item = toViewController.slideshow.currentSlideshowItem, item.zoomInInitially { - transitionViewFinalFrame.size = CGSize(width: transitionViewFinalFrame.size.width * item.maximumZoomScale, height: transitionViewFinalFrame.size.height * item.maximumZoomScale) + if let item = toViewController.slideshow.currentSlideshowItem, + item.zoomInInitially + { + transitionViewFinalFrame.size = CGSize( + width: transitionViewFinalFrame.size.width + * item.maximumZoomScale, + height: transitionViewFinalFrame.size.height + * item.maximumZoomScale + ) } - let duration: TimeInterval = transitionDuration(using: transitionContext) - - UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: UIViewAnimationOptions.curveLinear, animations: { - fromViewController.view.alpha = 0 - transitionView?.frame = transitionViewFinalFrame - transitionView?.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY) - }, completion: {[ref = self.referenceImageView] _ in - fromViewController.view.alpha = 1 - ref?.alpha = 1 - transitionView?.removeFromSuperview() - transitionBackgroundView.removeFromSuperview() - containerView.addSubview(toViewController.view) - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - }) + let duration: TimeInterval = transitionDuration( + using: transitionContext + ) + + UIView.animate( + withDuration: duration, + delay: 0, + usingSpringWithDamping: 0.7, + initialSpringVelocity: 0, + options: .curveLinear, + animations: { + fromViewController.view.alpha = 0 + transitionView?.frame = transitionViewFinalFrame + transitionView?.center = CGPoint( + x: finalFrame.midX, + y: finalFrame.midY + ) + }, + completion: { [ref = self.referenceImageView] _ in + fromViewController.view.alpha = 1 + ref?.alpha = 1 + transitionView?.removeFromSuperview() + transitionBackgroundView.removeFromSuperview() + containerView.addSubview(toViewController.view) + transitionContext.completeTransition( + !transitionContext.transitionWasCancelled + ) + } + ) } } @@ -254,12 +357,15 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { private var animatorForCurrentTransition: UIViewImplicitlyAnimating? - func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + func transitionDuration( + using transitionContext: UIViewControllerContextTransitioning? + ) -> TimeInterval { return 0.25 } - @available(iOS 10.0, *) - func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { + func interruptibleAnimator( + using transitionContext: UIViewControllerContextTransitioning + ) -> UIViewImplicitlyAnimating { // as per documentation, the same object should be returned for the ongoing transition if let animatorForCurrentSession = animatorForCurrentTransition { return animatorForCurrentSession @@ -267,30 +373,53 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { let params = animationParams(using: transitionContext) - let animator = UIViewPropertyAnimator(duration: params.0, curve: .linear, animations: params.1) + let animator = UIViewPropertyAnimator( + duration: params.0, + curve: .linear, + animations: params.1 + ) animator.addCompletion(params.2) animatorForCurrentTransition = animator return animator } - private func animationParams(using transitionContext: UIViewControllerContextTransitioning) -> (TimeInterval, () -> Void, (Any) -> Void) { - let toViewController: UIViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! - - guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as? FullScreenSlideshowViewController else { - fatalError("Transition not used with FullScreenSlideshowViewController") + private func animationParams( + using transitionContext: UIViewControllerContextTransitioning + ) -> (TimeInterval, () -> Void, (UIViewAnimatingPosition) -> Void) { + let toViewController: UIViewController = + transitionContext.viewController( + forKey: UITransitionContextViewControllerKey.to + )! + + guard + let fromViewController = transitionContext.viewController( + forKey: UITransitionContextViewControllerKey.from + ) as? FullScreenSlideshowViewController + else { + fatalError( + "Transition not used with FullScreenSlideshowViewController" + ) } let containerView = transitionContext.containerView var transitionViewInitialFrame: CGRect - if let currentSlideshowItem = fromViewController.slideshow.currentSlideshowItem { + if let currentSlideshowItem = fromViewController.slideshow + .currentSlideshowItem + { if let image = currentSlideshowItem.imageView.image { - transitionViewInitialFrame = image.tgr_aspectFitRectForSize(currentSlideshowItem.imageView.frame.size) + transitionViewInitialFrame = image.tgr_aspectFitRectForSize( + currentSlideshowItem.imageView.frame.size + ) } else { - transitionViewInitialFrame = currentSlideshowItem.imageView.frame + transitionViewInitialFrame = + currentSlideshowItem.imageView.frame } - transitionViewInitialFrame = containerView.convert(transitionViewInitialFrame, from: currentSlideshowItem) + transitionViewInitialFrame = containerView.convert( + transitionViewInitialFrame, + from: currentSlideshowItem + ) } else { transitionViewInitialFrame = fromViewController.slideshow.frame } @@ -299,33 +428,52 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { if let referenceImageView = referenceImageView { referenceImageView.alpha = 0 - let referenceSlideshowViewFrame = containerView.convert(referenceImageView.bounds, from: referenceImageView) + let referenceSlideshowViewFrame = containerView.convert( + referenceImageView.bounds, + from: referenceImageView + ) transitionViewFinalFrame = referenceSlideshowViewFrame // do a frame scaling when AspectFit content mode enabled - if fromViewController.slideshow.currentSlideshowItem?.imageView.image != nil && referenceImageView.contentMode == UIViewContentMode.scaleAspectFit { - transitionViewFinalFrame = containerView.convert(referenceImageView.aspectToFitFrame(), from: referenceImageView) + if fromViewController.slideshow.currentSlideshowItem?.imageView + .image != nil + && referenceImageView.contentMode + == .scaleAspectFit + { + transitionViewFinalFrame = containerView.convert( + referenceImageView.aspectToFitFrame(), + from: referenceImageView + ) } // fixes the problem when the referenceSlideshowViewFrame was shifted during change of the status bar hidden state - if UIApplication.shared.isStatusBarHidden && !toViewController.prefersStatusBarHidden && referenceSlideshowViewFrame.origin.y != parent.referenceSlideshowViewFrame?.origin.y { - transitionViewFinalFrame = transitionViewFinalFrame.offsetBy(dx: 0, dy: 20) + if UIApplication.shared.isStatusBarHidden + && !toViewController.prefersStatusBarHidden + && referenceSlideshowViewFrame.origin.y + != parent.referenceSlideshowViewFrame?.origin.y + { + transitionViewFinalFrame = transitionViewFinalFrame.offsetBy( + dx: 0, + dy: 20 + ) } } else { - transitionViewFinalFrame = referenceSlideshowView?.frame ?? CGRect.zero + transitionViewFinalFrame = + referenceSlideshowView?.frame ?? CGRect.zero } let transitionBackgroundView = UIView(frame: containerView.frame) - transitionBackgroundView.backgroundColor = fromViewController.backgroundColor + transitionBackgroundView.backgroundColor = + fromViewController.backgroundColor containerView.addSubview(transitionBackgroundView) - #if swift(>=4.2) + containerView.sendSubviewToBack(transitionBackgroundView) - #else - containerView.sendSubview(toBack: transitionBackgroundView) - #endif - let transitionView = UIImageView(image: fromViewController.slideshow.currentSlideshowItem?.imageView.image) - transitionView.contentMode = UIViewContentMode.scaleAspectFill + let transitionView = UIImageView( + image: fromViewController.slideshow.currentSlideshowItem?.imageView + .image + ) + transitionView.contentMode = .scaleAspectFill transitionView.clipsToBounds = true transitionView.frame = transitionViewInitialFrame containerView.addSubview(transitionView) @@ -336,13 +484,17 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { transitionBackgroundView.alpha = 0 transitionView.frame = transitionViewFinalFrame } - let completion = { (_: Any) in + let completion: (UIViewAnimatingPosition) -> Void = { _ in let completed = !transitionContext.transitionWasCancelled self.referenceImageView?.alpha = 1 if completed { fromViewController.view.removeFromSuperview() - UIApplication.shared.keyWindow?.removeGestureRecognizer(self.parent.gestureRecognizer) + // remove the recognizer from the window it was added to, which may no longer be key + self.parent.gestureRecognizer.view?.removeGestureRecognizer( + self.parent.gestureRecognizer + ) + // Unpauses slideshow self.referenceSlideshowView?.unpauseTimer() } else { @@ -360,13 +512,11 @@ class ZoomOutAnimator: ZoomAnimator, UIViewControllerAnimatedTransitioning { return (duration, animations, completion) } - func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + func animateTransition( + using transitionContext: UIViewControllerContextTransitioning + ) { // Working around iOS 10+ breaking change requiring to use UIPropertyAnimator for proper interactive transition instead of UIView.animate - if #available(iOS 10.0, *) { - interruptibleAnimator(using: transitionContext).startAnimation() - } else { - let params = animationParams(using: transitionContext) - UIView.animate(withDuration: params.0, delay: 0, options: UIViewAnimationOptions(), animations: params.1, completion: params.2) - } + + interruptibleAnimator(using: transitionContext).startAnimation() } } diff --git a/ImageSlideshow/Classes/InputSources/AFURLSource.swift b/ImageSlideshow/Classes/InputSources/AFURLSource.swift index 2f48cbd5..31639d89 100644 --- a/ImageSlideshow/Classes/InputSources/AFURLSource.swift +++ b/ImageSlideshow/Classes/InputSources/AFURLSource.swift @@ -10,7 +10,7 @@ import AFNetworking /// Input Source to image using AFNetworking @objcMembers -public class AFURLSource: NSObject, InputSource { +public class AFURLSource: NSObject, @preconcurrency InputSource { /// url to load public var url: URL @@ -39,15 +39,15 @@ public class AFURLSource: NSObject, InputSource { } } - public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + @MainActor public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { imageView.setImageWith(URLRequest(url: url), placeholderImage: self.placeholder, success: { (_, _, image: UIImage) in callback(image) - }, failure: {[placeholder = self.placeholder] _, _, _ in - callback(placeholder) + }, failure: { _, _, _ in + callback(nil) }) } - public func cancelLoad(on imageView: UIImageView) { + @MainActor public func cancelLoad(on imageView: UIImageView) { imageView.cancelImageDownloadTask() } } diff --git a/ImageSlideshow/Classes/InputSources/KingfisherSource.swift b/ImageSlideshow/Classes/InputSources/KingfisherSource.swift index f02394d2..68a5314e 100644 --- a/ImageSlideshow/Classes/InputSources/KingfisherSource.swift +++ b/ImageSlideshow/Classes/InputSources/KingfisherSource.swift @@ -13,7 +13,7 @@ import ImageSlideshow import Kingfisher /// Input Source to image using Kingfisher -public class KingfisherSource: NSObject, InputSource { +public class KingfisherSource: NSObject, @preconcurrency InputSource { /// url to load public var url: URL @@ -54,6 +54,7 @@ public class KingfisherSource: NSObject, InputSource { /// - Parameters: /// - imageView: UIImageView that receives the loaded image /// - callback: Completion callback with an optional image + @MainActor @objc public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { imageView.kf.setImage(with: self.url, placeholder: self.placeholder, options: self.options, progressBlock: nil) { result in @@ -61,7 +62,7 @@ public class KingfisherSource: NSObject, InputSource { case .success(let image): callback(image.image) case .failure: - callback(self.placeholder) + callback(nil) } } } @@ -69,7 +70,7 @@ public class KingfisherSource: NSObject, InputSource { /// Cancel an image download task /// /// - Parameter imageView: UIImage view with the download task that should be canceled - public func cancelLoad(on imageView: UIImageView) { + @MainActor public func cancelLoad(on imageView: UIImageView) { imageView.kf.cancelDownloadTask() } } diff --git a/ImageSlideshow/Classes/InputSources/ParseSource.swift b/ImageSlideshow/Classes/InputSources/ParseSource.swift deleted file mode 100644 index 8e7acc22..00000000 --- a/ImageSlideshow/Classes/InputSources/ParseSource.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// ParseImageSource.swift -// ImageSlideshow -// -// Created by Jaime Agudo Lopez on 14/01/2017. -// -import Parse - -/// Input Source to image using Parse -public class ParseSource: NSObject, InputSource { - var file: PFFileObject - var placeholder: UIImage? - - /// Initializes a new source with URL and optionally a placeholder - /// - parameter url: a url to be loaded - /// - parameter placeholder: a placeholder used before image is loaded - public init(file: PFFileObject, placeholder: UIImage? = nil) { - self.file = file - self.placeholder = placeholder - super.init() - } - - @objc public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { - imageView.image = self.placeholder - - self.file.getDataInBackground {(data: Data?, _: Error?) in - if let data = data, let image = UIImage(data: data) { - imageView.image = image - callback(image) - } else { - callback(nil) - } - } - } -} diff --git a/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift b/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift index b1c189a9..1d24eac0 100644 --- a/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift +++ b/ImageSlideshow/Classes/InputSources/SDWebImageSource.swift @@ -14,7 +14,7 @@ import SDWebImage /// Input Source to image using SDWebImage @objcMembers -public class SDWebImageSource: NSObject, InputSource { +public class SDWebImageSource: NSObject, @preconcurrency InputSource { /// url to load public var url: URL @@ -43,13 +43,13 @@ public class SDWebImageSource: NSObject, InputSource { } } - public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { + @MainActor public func load(to imageView: UIImageView, with callback: @escaping (UIImage?) -> Void) { imageView.sd_setImage(with: self.url, placeholderImage: self.placeholder, options: [], completed: { (image, _, _, _) in callback(image) }) } - public func cancelLoad(on imageView: UIImageView) { + @MainActor public func cancelLoad(on imageView: UIImageView) { imageView.sd_cancelCurrentImageLoad() } } diff --git a/Package.resolved b/Package.resolved index 28277c26..1f5c4102 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/Alamofire/Alamofire.git", "state": { "branch": null, - "revision": "fca036f7aeca07124067cb6e0c12b0ad6359e3d4", - "version": "5.1.0" + "revision": "7595cbcf59809f9977c5f6378500de2ad73b7ddb", + "version": "5.12.0" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/Alamofire/AlamofireImage.git", "state": { "branch": null, - "revision": "3e8edbeb75227f8542aa87f90240cf0424d6362f", - "version": "4.1.0" + "revision": "4cf73d601c482b7d77bae47de3ef1b8bcf328ec1", + "version": "4.4.0" } }, { @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/onevcat/Kingfisher.git", "state": { "branch": null, - "revision": "349ed06467a6f8a4939bcb83db301542bc84eac9", - "version": "5.13.4" + "revision": "7deda23bbdca612076c5c315003d8638a08ed0f1", + "version": "8.3.2" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "74ae21337c415bb542eb901803f3e84799265c34", - "version": "5.7.2" + "revision": "2de3a496eaf6df9a1312862adcfd54acd73c39c0", + "version": "5.21.7" } } ] diff --git a/Package.swift b/Package.swift index 3e4f65c9..ac07f821 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,13 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.5 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "ImageSlideshow", platforms: [ - .iOS(.v10), + .iOS(.v15), + // iOS-only library; declared to satisfy Kingfisher's macOS floor during resolution + .macOS(.v10_15), ], products: [ .library( @@ -22,9 +24,9 @@ let package = Package( targets: ["ImageSlideshowKingfisher"]) ], dependencies: [ - .package(url: "https://github.com/onevcat/Kingfisher.git", from: "5.8.0"), - .package(url: "https://github.com/Alamofire/AlamofireImage.git", from: "4.0.0"), - .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.1.0") + .package(url: "https://github.com/onevcat/Kingfisher.git", from: "8.3.2"), + .package(url: "https://github.com/Alamofire/AlamofireImage.git", from: "4.3.0"), + .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.21.0") ], targets: [ .target( @@ -39,7 +41,6 @@ let package = Package( "Classes/Core/InputSource.swift", "Classes/Core/PageIndicator.swift", "Classes/Core/PageIndicatorPosition.swift", - "Classes/Core/SwiftSupport.swift", "Classes/Core/UIImage+AspectFit.swift", "Classes/Core/UIImageView+Tools.swift", "Classes/Core/ZoomAnimatedTransitioning.swift", diff --git a/README.md b/README.md index 3364d073..6ab094a6 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ ImageSlideshow is available through [CocoaPods](http://cocoapods.org). To instal it, simply add the following line to your Podfile: ```ruby -pod 'ImageSlideshow', '~> 1.9.0' +pod 'ImageSlideshow', '~> 1.9.3' ``` ### Carthage To integrate ImageSlideshow into your Xcode project using Carthage, specify it in your Cartfile: ```ruby -github "zvonicek/ImageSlideshow" ~> 1.9.0 +github "zvonicek/ImageSlideshow" ~> 1.9.3 ``` 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.