diff --git a/CHANGELOG.md b/CHANGELOG.md index 17bf98fc..ae2c2fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Changes for users of the library currently on `develop`: +- Fixed some tests, added tests for NYTPhotoViewerSinglePhotoDataSource +- Fixed a strict warning and a subscripting bug in NYTPhotoViewerArrayDataSource.m +- Added support for interstitial views + +## [2.0.0](https://github.com/NYTimes/NYTPhotoViewer/releases/tag/2.0.0) + +Changes for users of the library in 2.0.0: + - Expose a data-source-oriented API for PhotosViewController ([#226](https://github.com/NYTimes/NYTPhotoViewer/pull/226)) - A data source no longer has to handle out-of-bounds indexes ([#227](https://github.com/NYTimes/NYTPhotoViewer/pull/227)) - The data source is not retained ([#227](https://github.com/NYTimes/NYTPhotoViewer/pull/227)) diff --git a/Example-Swift/Photo.swift b/Example-Swift/Photo.swift deleted file mode 100644 index 630b1025..00000000 --- a/Example-Swift/Photo.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Photo.swift -// NYTPhotoViewer -// -// Created by Chris Dzombak on 2/2/17. -// Copyright © 2017 NYTimes. All rights reserved. -// - -import UIKit - -/// A photo may often be represented in a Swift application as a value type. -struct Photo { - // This would usually be a URL, but for this demo we load images from the bundle. - let name: String - let summary: String - let credit: String - - let identifier: Int -} diff --git a/Example-Swift/PhotoBox.swift b/Example-Swift/PhotoBox.swift index 48c9d46c..58e3046e 100644 --- a/Example-Swift/PhotoBox.swift +++ b/Example-Swift/PhotoBox.swift @@ -12,10 +12,10 @@ import NYTPhotoViewer /// A box allowing NYTPhotoViewer to consume Swift value types from our codebase. final class NYTPhotoBox: NSObject, NYTPhoto { - let value: Photo + let value: PhotoItem - init(_ photo: Photo) { - value = photo + init(_ photoItem: PhotoItem) { + value = photoItem } // MARK: NYTPhoto @@ -24,6 +24,14 @@ final class NYTPhotoBox: NSObject, NYTPhoto { var imageData: Data? var placeholderImage: UIImage? + var isPhoto: Bool { + return value.itemType == .image + } + + var isView: Bool { + return value.itemType == .view + } + var attributedCaptionTitle: NSAttributedString? var attributedCaptionSummary: NSAttributedString? { diff --git a/Example-Swift/PhotoItem.swift b/Example-Swift/PhotoItem.swift new file mode 100644 index 00000000..a1169c81 --- /dev/null +++ b/Example-Swift/PhotoItem.swift @@ -0,0 +1,34 @@ +// +// PhotoItem.swift +// NYTPhotoViewer +// +// Created by Chris Dzombak on 2/2/17. +// Copyright © 2017 NYTimes. All rights reserved. +// + +import UIKit + +/// A photo item can either be an image or a view +enum PhotoItemType { + case image + case view +} + +/// A photo may often be represented in a Swift application as a value type. +struct PhotoItem { + // This would usually be a URL, but for this demo we load images from the bundle. + let name: String + let summary: String + let credit: String + let itemType: PhotoItemType + let identifier: Int + + init(name: String = "", summary: String = "", credit: String = "", itemType: PhotoItemType, identifier: Int) { + self.name = name + self.summary = summary + self.credit = credit + self.itemType = itemType + self.identifier = identifier + } + +} diff --git a/Example-Swift/PhotoViewerCoordinator.swift b/Example-Swift/PhotoViewerCoordinator.swift index ae25b91a..2883f4e8 100644 --- a/Example-Swift/PhotoViewerCoordinator.swift +++ b/Example-Swift/PhotoViewerCoordinator.swift @@ -9,7 +9,7 @@ import NYTPhotoViewer /// Coordinates interaction between the application's data layer and the photo viewer component. -final class PhotoViewerCoordinator: NYTPhotoViewerDataSource { +final class PhotoViewerCoordinator: NSObject, NYTPhotoViewerDataSource { let slideshow: [NYTPhotoBox] let provider: PhotosProvider @@ -20,15 +20,18 @@ final class PhotoViewerCoordinator: NYTPhotoViewerDataSource { init(provider: PhotosProvider) { self.provider = provider self.slideshow = provider.fetchDemoSlideshow().map { NYTPhotoBox($0) } + super.init() self.fetchPhotos() } func fetchPhotos() { for box in slideshow { - provider.fetchPhoto(named: box.value.name, then: { [weak self] (result) in - box.image = result - self?.photoViewer.update(box) - }) + if box.isPhoto { + provider.fetchPhoto(named: box.value.name, then: { [weak self] (result) in + box.image = result + self?.photoViewer.update(box) + }) + } } } @@ -36,7 +39,24 @@ final class PhotoViewerCoordinator: NYTPhotoViewerDataSource { @objc var numberOfPhotos: NSNumber? { - return NSNumber(integerLiteral: slideshow.count) + return NSNumber(integerLiteral: slideshow.filter({ $0.isPhoto }).count) + } + + @objc + func numberOfInterstitialViews() -> NSNumber { + return NSNumber(integerLiteral: slideshow.filter({ $0.isView }).count) + } + + @objc + func isPhoto(at idx: Int) -> Bool { + guard idx < slideshow.count else { return false } + return slideshow[idx].isPhoto + } + + @objc + func isInterstitialView(at idx: Int) -> Bool { + guard idx < slideshow.count else { return false } + return slideshow[idx].isView } @objc @@ -48,6 +68,7 @@ final class PhotoViewerCoordinator: NYTPhotoViewerDataSource { @objc func photo(at index: Int) -> NYTPhoto? { guard index < slideshow.count else { return nil } + guard slideshow[index].isPhoto else { return nil } return slideshow[index] } } diff --git a/Example-Swift/PhotosProvider.swift b/Example-Swift/PhotosProvider.swift index e5dc437b..416c5367 100755 --- a/Example-Swift/PhotosProvider.swift +++ b/Example-Swift/PhotosProvider.swift @@ -10,11 +10,11 @@ import UIKit /// A component of your data layer, which might load photos from the cache or network. final class PhotosProvider { - typealias Slideshow = [Photo] + typealias Slideshow = [PhotoItem] /// Simulate a synchronous fetch of a slideshow, perhaps from a local database. func fetchDemoSlideshow() -> Slideshow { - return (0...4).map { demoPhoto(identifier: $0) } + return (0...7).map { [2,5].contains($0) ? demoView(identifier: $0) : demoPhoto(identifier: $0) } } /// Simulate fetching a photo from the network. @@ -29,7 +29,7 @@ final class PhotosProvider { } extension PhotosProvider { - fileprivate func demoPhoto(identifier: Int) -> Photo { + fileprivate func demoPhoto(identifier: Int) -> PhotoItem { let photoName: String let photoSummary: String let photoCredit: String @@ -44,6 +44,10 @@ extension PhotosProvider { photoCredit = "Photo: Nic Lehoux" } - return Photo(name: photoName, summary: photoSummary, credit: photoCredit, identifier: identifier) + return PhotoItem(name: photoName, summary: photoSummary, credit: photoCredit, itemType: .image, identifier: identifier) + } + + fileprivate func demoView(identifier: Int) -> PhotoItem { + return PhotoItem(itemType: .view, identifier: identifier) } } diff --git a/Example-Swift/ViewController.swift b/Example-Swift/ViewController.swift index 85a8f1cf..9152146f 100755 --- a/Example-Swift/ViewController.swift +++ b/Example-Swift/ViewController.swift @@ -68,4 +68,15 @@ extension ViewController: NYTPhotosViewControllerDelegate { func photosViewControllerDidDismiss(_ photosViewController: NYTPhotosViewController) { photoViewerCoordinator = nil } + + func photosViewController(_ photosViewController: NYTPhotosViewController, interstitialViewAt index: UInt) -> UIView { + let redView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: photosViewController.view.frame.width, height: 200))) + redView.backgroundColor = .red + redView.autoresizingMask = [.flexibleWidth] + return redView + } + + func photosViewController(_ photosViewController: NYTPhotosViewController, didNavigateToInterstialView view: UIView, at index: UInt) { + view.backgroundColor = .blue + } } diff --git a/NYTPhotoViewer.podspec b/NYTPhotoViewer.podspec index 6fe70c84..9a8f962c 100644 --- a/NYTPhotoViewer.podspec +++ b/NYTPhotoViewer.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "NYTPhotoViewer" - s.version = "2.0.0" + s.version = "3.0.0" s.description = <<-DESC NYTPhotoViewer is a slideshow and image viewer that includes double tap to zoom, captions, support for multiple images, interactive flick to dismiss, animated zooming presentation, and more. diff --git a/NYTPhotoViewer.xcodeproj/project.pbxproj b/NYTPhotoViewer.xcodeproj/project.pbxproj index da309f1c..f5d56032 100644 --- a/NYTPhotoViewer.xcodeproj/project.pbxproj +++ b/NYTPhotoViewer.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 1110840A1C875B5000699670 /* NYTPhotoViewer.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084091C875B5000699670 /* NYTPhotoViewer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 111084111C875B5000699670 /* NYTPhotoViewer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 111084061C875B5000699670 /* NYTPhotoViewer.framework */; }; 1110842D1C8767BA00699670 /* NYTPhotoCaptionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084261C8767BA00699670 /* NYTPhotoCaptionViewTests.m */; }; - 1110842E1C8767BA00699670 /* NYTPhotosDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084271C8767BA00699670 /* NYTPhotosDataSourceTests.m */; }; 1110842F1C8767BA00699670 /* NYTPhotosOverlayViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084281C8767BA00699670 /* NYTPhotosOverlayViewTests.m */; }; 111084301C8767BA00699670 /* NYTPhotosViewControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084291C8767BA00699670 /* NYTPhotosViewControllerTests.m */; }; 111084311C8767BA00699670 /* NYTPhotoTransitionAnimatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1110842A1C8767BA00699670 /* NYTPhotoTransitionAnimatorTests.m */; }; @@ -35,7 +34,7 @@ 1110845D1C87682300699670 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084441C87682300699670 /* NYTScalingImageView.m */; }; 1110845E1C87682300699670 /* NYTPhoto.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084461C87682300699670 /* NYTPhoto.h */; settings = {ATTRIBUTES = (Public, ); }; }; 1110845F1C87682300699670 /* NYTPhotoCaptionViewLayoutWidthHinting.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084471C87682300699670 /* NYTPhotoCaptionViewLayoutWidthHinting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 111084601C87682300699670 /* NYTPhotoContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084481C87682300699670 /* NYTPhotoContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 111084601C87682300699670 /* NYTPhotoViewerContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084481C87682300699670 /* NYTPhotoViewerContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 111084611C87682300699670 /* NYTPhotoViewerDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084491C87682300699670 /* NYTPhotoViewerDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 111084621C87682300699670 /* NSBundle+NYTPhotoViewer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1110844B1C87682300699670 /* NSBundle+NYTPhotoViewer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 111084631C87682300699670 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1110844C1C87682300699670 /* NSBundle+NYTPhotoViewer.m */; }; @@ -79,7 +78,7 @@ 11FBDA9D1C87753200018169 /* NYTScalingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084441C87682300699670 /* NYTScalingImageView.m */; }; 11FBDA9E1C87753200018169 /* NYTPhoto.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084461C87682300699670 /* NYTPhoto.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11FBDA9F1C87753200018169 /* NYTPhotoCaptionViewLayoutWidthHinting.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084471C87682300699670 /* NYTPhotoCaptionViewLayoutWidthHinting.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 11FBDAA01C87753200018169 /* NYTPhotoContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084481C87682300699670 /* NYTPhotoContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 11FBDAA01C87753200018169 /* NYTPhotoViewerContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084481C87682300699670 /* NYTPhotoViewerContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11FBDAA11C87753200018169 /* NYTPhotoViewerDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 111084491C87682300699670 /* NYTPhotoViewerDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11FBDAA21C87753200018169 /* NSBundle+NYTPhotoViewer.h in Headers */ = {isa = PBXBuildFile; fileRef = 1110844B1C87682300699670 /* NSBundle+NYTPhotoViewer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11FBDAA31C87753200018169 /* NSBundle+NYTPhotoViewer.m in Sources */ = {isa = PBXBuildFile; fileRef = 1110844C1C87682300699670 /* NSBundle+NYTPhotoViewer.m */; }; @@ -94,7 +93,10 @@ 11FBDAFD1C877D9A00018169 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 11FBDAFB1C877D9A00018169 /* LaunchScreen.xib */; }; 11FBDAFE1C877F5500018169 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 111084781C876A2B00699670 /* Assets.xcassets */; }; 11FBDAFF1C877F8000018169 /* giphy.gif in Resources */ = {isa = PBXBuildFile; fileRef = 111084851C876B0400699670 /* giphy.gif */; }; - 9371A73C1E438B9C00A8F2EF /* Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9371A73B1E438B9C00A8F2EF /* Photo.swift */; }; + 1AFC4D301FE01B9000FDF949 /* NYTPhotoViewerArrayDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 111084271C8767BA00699670 /* NYTPhotoViewerArrayDataSourceTests.m */; }; + 1AFC4D321FE01EFA00FDF949 /* NYTPhotoViewerSinglePhotoDataSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AFC4D311FE01EFA00FDF949 /* NYTPhotoViewerSinglePhotoDataSourceTests.m */; }; + 2A4D6135208683990092ED17 /* NYTInterstitialViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A4D6134208683990092ED17 /* NYTInterstitialViewController.m */; }; + 9371A73C1E438B9C00A8F2EF /* PhotoItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9371A73B1E438B9C00A8F2EF /* PhotoItem.swift */; }; 9371A7441E438BCE00A8F2EF /* PhotoBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9371A7431E438BCE00A8F2EF /* PhotoBox.swift */; }; 9371A7461E43D61E00A8F2EF /* PhotoViewerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9371A7451E43D61E00A8F2EF /* PhotoViewerCoordinator.swift */; }; 93F45B181E3BB5610093DB93 /* NYTPhotoViewerSinglePhotoDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 93F45B161E3BB5610093DB93 /* NYTPhotoViewerSinglePhotoDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -238,7 +240,7 @@ 111084101C875B5000699670 /* NYTPhotoViewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NYTPhotoViewerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 111084171C875B5000699670 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 111084261C8767BA00699670 /* NYTPhotoCaptionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoCaptionViewTests.m; sourceTree = ""; }; - 111084271C8767BA00699670 /* NYTPhotosDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosDataSourceTests.m; sourceTree = ""; }; + 111084271C8767BA00699670 /* NYTPhotoViewerArrayDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoViewerArrayDataSourceTests.m; sourceTree = ""; }; 111084281C8767BA00699670 /* NYTPhotosOverlayViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosOverlayViewTests.m; sourceTree = ""; }; 111084291C8767BA00699670 /* NYTPhotosViewControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotosViewControllerTests.m; sourceTree = ""; }; 1110842A1C8767BA00699670 /* NYTPhotoTransitionAnimatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoTransitionAnimatorTests.m; sourceTree = ""; }; @@ -263,7 +265,7 @@ 111084441C87682300699670 /* NYTScalingImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTScalingImageView.m; sourceTree = ""; }; 111084461C87682300699670 /* NYTPhoto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhoto.h; sourceTree = ""; }; 111084471C87682300699670 /* NYTPhotoCaptionViewLayoutWidthHinting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoCaptionViewLayoutWidthHinting.h; sourceTree = ""; }; - 111084481C87682300699670 /* NYTPhotoContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoContainer.h; sourceTree = ""; }; + 111084481C87682300699670 /* NYTPhotoViewerContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewerContainer.h; sourceTree = ""; }; 111084491C87682300699670 /* NYTPhotoViewerDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewerDataSource.h; sourceTree = ""; }; 1110844B1C87682300699670 /* NSBundle+NYTPhotoViewer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSBundle+NYTPhotoViewer.h"; sourceTree = ""; }; 1110844C1C87682300699670 /* NSBundle+NYTPhotoViewer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSBundle+NYTPhotoViewer.m"; sourceTree = ""; }; @@ -293,7 +295,10 @@ 11FBDAEA1C877BD600018169 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 11FBDAF01C877C8B00018169 /* PhotosProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosProvider.swift; sourceTree = ""; }; 11FBDAFC1C877D9A00018169 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 9371A73B1E438B9C00A8F2EF /* Photo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Photo.swift; sourceTree = ""; }; + 1AFC4D311FE01EFA00FDF949 /* NYTPhotoViewerSinglePhotoDataSourceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYTPhotoViewerSinglePhotoDataSourceTests.m; sourceTree = ""; }; + 2A4D6134208683990092ED17 /* NYTInterstitialViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NYTInterstitialViewController.m; sourceTree = ""; }; + 2A4D613C208683B80092ED17 /* NYTInterstitialViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NYTInterstitialViewController.h; sourceTree = ""; }; + 9371A73B1E438B9C00A8F2EF /* PhotoItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoItem.swift; sourceTree = ""; }; 9371A7431E438BCE00A8F2EF /* PhotoBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoBox.swift; sourceTree = ""; }; 9371A7451E43D61E00A8F2EF /* PhotoViewerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoViewerCoordinator.swift; sourceTree = ""; }; 93F45B161E3BB5610093DB93 /* NYTPhotoViewerSinglePhotoDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYTPhotoViewerSinglePhotoDataSource.h; sourceTree = ""; }; @@ -389,6 +394,8 @@ 111084401C87682300699670 /* NYTPhotoTransitionController.m */, 111084411C87682300699670 /* NYTPhotoViewController.h */, 111084421C87682300699670 /* NYTPhotoViewController.m */, + 2A4D613C208683B80092ED17 /* NYTInterstitialViewController.h */, + 2A4D6134208683990092ED17 /* NYTInterstitialViewController.m */, 111084371C87682300699670 /* NYTPhotoViewerArrayDataSource.h */, 111084381C87682300699670 /* NYTPhotoViewerArrayDataSource.m */, 93F45B161E3BB5610093DB93 /* NYTPhotoViewerSinglePhotoDataSource.h */, @@ -406,7 +413,8 @@ isa = PBXGroup; children = ( 111084261C8767BA00699670 /* NYTPhotoCaptionViewTests.m */, - 111084271C8767BA00699670 /* NYTPhotosDataSourceTests.m */, + 111084271C8767BA00699670 /* NYTPhotoViewerArrayDataSourceTests.m */, + 1AFC4D311FE01EFA00FDF949 /* NYTPhotoViewerSinglePhotoDataSourceTests.m */, 111084281C8767BA00699670 /* NYTPhotosOverlayViewTests.m */, 111084291C8767BA00699670 /* NYTPhotosViewControllerTests.m */, 1110842A1C8767BA00699670 /* NYTPhotoTransitionAnimatorTests.m */, @@ -422,7 +430,7 @@ children = ( 111084461C87682300699670 /* NYTPhoto.h */, 111084471C87682300699670 /* NYTPhotoCaptionViewLayoutWidthHinting.h */, - 111084481C87682300699670 /* NYTPhotoContainer.h */, + 111084481C87682300699670 /* NYTPhotoViewerContainer.h */, 111084491C87682300699670 /* NYTPhotoViewerDataSource.h */, ); path = Protocols; @@ -503,7 +511,7 @@ 9371A7451E43D61E00A8F2EF /* PhotoViewerCoordinator.swift */, 11FBDAF01C877C8B00018169 /* PhotosProvider.swift */, 9371A7431E438BCE00A8F2EF /* PhotoBox.swift */, - 9371A73B1E438B9C00A8F2EF /* Photo.swift */, + 9371A73B1E438B9C00A8F2EF /* PhotoItem.swift */, 11FBDAE21C877BD600018169 /* Main.storyboard */, 11FBDAFB1C877D9A00018169 /* LaunchScreen.xib */, 11FBDAEA1C877BD600018169 /* Info.plist */, @@ -520,7 +528,7 @@ files = ( 111084621C87682300699670 /* NSBundle+NYTPhotoViewer.h in Headers */, 111084541C87682300699670 /* NYTPhotosViewController.h in Headers */, - 111084601C87682300699670 /* NYTPhotoContainer.h in Headers */, + 111084601C87682300699670 /* NYTPhotoViewerContainer.h in Headers */, 1110844E1C87682300699670 /* NYTPhotoDismissalInteractionController.h in Headers */, 111084611C87682300699670 /* NYTPhotoViewerDataSource.h in Headers */, 1110845A1C87682300699670 /* NYTPhotoViewController.h in Headers */, @@ -542,7 +550,7 @@ buildActionMask = 2147483647; files = ( 11FBDA981C87753200018169 /* NYTPhotoTransitionController.h in Headers */, - 11FBDAA01C87753200018169 /* NYTPhotoContainer.h in Headers */, + 11FBDAA01C87753200018169 /* NYTPhotoViewerContainer.h in Headers */, 11FBDA9C1C87753200018169 /* NYTScalingImageView.h in Headers */, 11FBDA921C87753200018169 /* NYTPhotosOverlayView.h in Headers */, 11FBDA8E1C87753200018169 /* NYTPhotoDismissalInteractionController.h in Headers */, @@ -825,6 +833,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2A4D6135208683990092ED17 /* NYTInterstitialViewController.m in Sources */, 111084591C87682300699670 /* NYTPhotoTransitionController.m in Sources */, 111084631C87682300699670 /* NSBundle+NYTPhotoViewer.m in Sources */, 111084551C87682300699670 /* NYTPhotosViewController.m in Sources */, @@ -843,14 +852,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1AFC4D301FE01B9000FDF949 /* NYTPhotoViewerArrayDataSourceTests.m in Sources */, 1110842D1C8767BA00699670 /* NYTPhotoCaptionViewTests.m in Sources */, 1110842F1C8767BA00699670 /* NYTPhotosOverlayViewTests.m in Sources */, - 1110842E1C8767BA00699670 /* NYTPhotosDataSourceTests.m in Sources */, 111084311C8767BA00699670 /* NYTPhotoTransitionAnimatorTests.m in Sources */, 111084301C8767BA00699670 /* NYTPhotosViewControllerTests.m in Sources */, 111084841C876AAD00699670 /* NYTExamplePhoto.m in Sources */, 111084331C8767BA00699670 /* NYTScalingImageViewTests.m in Sources */, 111084321C8767BA00699670 /* NYTPhotoViewControllerTests.m in Sources */, + 1AFC4D321FE01EFA00FDF949 /* NYTPhotoViewerSinglePhotoDataSourceTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -892,7 +902,7 @@ 11FBDADF1C877BD600018169 /* AppDelegate.swift in Sources */, 9371A7461E43D61E00A8F2EF /* PhotoViewerCoordinator.swift in Sources */, 11FBDAF11C877C8B00018169 /* PhotosProvider.swift in Sources */, - 9371A73C1E438B9C00A8F2EF /* Photo.swift in Sources */, + 9371A73C1E438B9C00A8F2EF /* PhotoItem.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/NYTPhotoViewer/NYTInterstitialViewController.h b/NYTPhotoViewer/NYTInterstitialViewController.h new file mode 100644 index 00000000..b038134b --- /dev/null +++ b/NYTPhotoViewer/NYTInterstitialViewController.h @@ -0,0 +1,31 @@ +// +// NYTInterstitialViewController.h +// NYTPhotoViewer +// +// Created by Howarth, Craig on 4/17/18. +// Copyright © 2018 NYTimes. All rights reserved. +// + +@import UIKit; +#import "NYTPhotoViewerContainer.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + * The view controller controlling the display of a interstitial view. + */ +@interface NYTInterstitialViewController : UIViewController + +/** + * The designated initializer that takes an interstitial view. + * + * @param view The view object that this view controller manages. + * @param itemIndex The index of this view controller in the photo viewer collection. + * + * @return A fully initialized object. + */ +- (instancetype)initWithView:(nullable UIView *)view itemIndex:(NSUInteger)itemIndex NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewer/NYTInterstitialViewController.m b/NYTPhotoViewer/NYTInterstitialViewController.m new file mode 100644 index 00000000..688a045c --- /dev/null +++ b/NYTPhotoViewer/NYTInterstitialViewController.m @@ -0,0 +1,75 @@ +// +// NYTInterstitialViewController.m +// NYTPhotoViewer +// +// Created by Howarth, Craig on 4/17/18. +// Copyright © 2018 NYTimes. All rights reserved. +// + +#import "NYTInterstitialViewController.h" +#import "NYTPhoto.h" + +@interface NYTInterstitialViewController () + +@property (nonatomic, nullable) id photo; +@property (nonatomic, nullable) UIView *interstitialView; +@property (nonatomic) NSUInteger photoViewItemIndex; + +- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; + +@end + +@implementation NYTInterstitialViewController + +#pragma mark - UIViewController + +- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + return [self initWithView:nil itemIndex:0]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + + if (self) { + [self commonInitWithView:nil itemIndex:0]; + } + + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.view addSubview:self.interstitialView]; + self.interstitialView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)); +} + +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + + self.interstitialView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)); +} + +- (BOOL)prefersHomeIndicatorAutoHidden { + return YES; +} + +#pragma mark - NYTPhotoViewController + +- (instancetype)initWithView:(UIView *)interstitialView itemIndex:(NSUInteger)itemIndex { + self = [super initWithNibName:nil bundle:nil]; + + if (self) { + [self commonInitWithView:interstitialView itemIndex:itemIndex]; + } + + return self; +} + +- (void)commonInitWithView:(UIView *)interstitialView itemIndex:(NSUInteger)itemIndex { + _photo = nil; + _photoViewItemIndex = itemIndex; + _interstitialView = interstitialView; +} + +@end diff --git a/NYTPhotoViewer/NYTPhotoCaptionView.m b/NYTPhotoViewer/NYTPhotoCaptionView.m index 7f6c83b4..7c28299c 100644 --- a/NYTPhotoViewer/NYTPhotoCaptionView.m +++ b/NYTPhotoViewer/NYTPhotoCaptionView.m @@ -58,7 +58,7 @@ - (void)didMoveToSuperview { - (void)layoutSubviews { [super layoutSubviews]; - void (^updateGradientFrame)() = ^{ + void (^updateGradientFrame)(void) = ^{ self.gradientLayer.frame = self.layer.bounds; }; diff --git a/NYTPhotoViewer/NYTPhotoDismissalInteractionController.h b/NYTPhotoViewer/NYTPhotoDismissalInteractionController.h index 0029f987..bbaf5a7d 100644 --- a/NYTPhotoViewer/NYTPhotoDismissalInteractionController.h +++ b/NYTPhotoViewer/NYTPhotoDismissalInteractionController.h @@ -41,4 +41,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewer/NYTPhotoTransitionController.h b/NYTPhotoViewer/NYTPhotoTransitionController.h index 68b5d2c8..f7b52669 100644 --- a/NYTPhotoViewer/NYTPhotoTransitionController.h +++ b/NYTPhotoViewer/NYTPhotoTransitionController.h @@ -41,4 +41,4 @@ NS_ASSUME_NONNULL_BEGIN @end -NS_ASSUME_NONNULL_END \ No newline at end of file +NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewer/NYTPhotoViewController.h b/NYTPhotoViewer/NYTPhotoViewController.h index 73906f20..95ef1aa1 100644 --- a/NYTPhotoViewer/NYTPhotoViewController.h +++ b/NYTPhotoViewer/NYTPhotoViewController.h @@ -7,7 +7,7 @@ // @import UIKit; -#import "NYTPhotoContainer.h" +#import "NYTPhotoViewerContainer.h" @class NYTScalingImageView; @@ -24,7 +24,7 @@ extern NSString * const NYTPhotoViewControllerPhotoImageUpdatedNotification; /** * The view controller controlling the display of a single photo object. */ -@interface NYTPhotoViewController : UIViewController +@interface NYTPhotoViewController : UIViewController /** * The internal scaling image view used to display the photo. @@ -50,12 +50,13 @@ extern NSString * const NYTPhotoViewControllerPhotoImageUpdatedNotification; * The designated initializer that takes the photo and activity view. * * @param photo The photo object that this view controller manages. + * @param itemIndex The index of this view controller in the photo viewer collection. * @param loadingView The view to display while the photo's image loads. This view will be hidden when the image loads. * @param notificationCenter The notification center on which to observe the `NYTPhotoViewControllerPhotoImageUpdatedNotification`. * * @return A fully initialized object. */ -- (instancetype)initWithPhoto:(nullable id )photo loadingView:(nullable UIView *)loadingView notificationCenter:(nullable NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithPhoto:(nullable id )photo itemIndex:(NSUInteger)itemIndex loadingView:(nullable UIView *)loadingView notificationCenter:(nullable NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER; @end diff --git a/NYTPhotoViewer/NYTPhotoViewController.m b/NYTPhotoViewer/NYTPhotoViewController.m index 301854c9..50755d32 100644 --- a/NYTPhotoViewer/NYTPhotoViewController.m +++ b/NYTPhotoViewer/NYTPhotoViewController.m @@ -18,7 +18,9 @@ @interface NYTPhotoViewController () -@property (nonatomic) id photo; +@property (nonatomic, nullable) id photo; +@property (nonatomic, nullable) UIView *interstitialView; +@property (nonatomic) NSUInteger photoViewItemIndex; - (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; @@ -43,14 +45,14 @@ - (void)dealloc { #pragma mark - UIViewController - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - return [self initWithPhoto:nil loadingView:nil notificationCenter:nil]; + return [self initWithPhoto:nil itemIndex:0 loadingView:nil notificationCenter:nil]; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { - [self commonInitWithPhoto:nil loadingView:nil notificationCenter:nil]; + [self commonInitWithPhoto:nil itemIndex:0 loadingView:nil notificationCenter:nil]; } return self; @@ -86,18 +88,20 @@ - (BOOL)prefersHomeIndicatorAutoHidden { #pragma mark - NYTPhotoViewController -- (instancetype)initWithPhoto:(id )photo loadingView:(UIView *)loadingView notificationCenter:(NSNotificationCenter *)notificationCenter { +- (instancetype)initWithPhoto:(id )photo itemIndex:(NSUInteger)itemIndex loadingView:(UIView *)loadingView notificationCenter:(NSNotificationCenter *)notificationCenter { self = [super initWithNibName:nil bundle:nil]; if (self) { - [self commonInitWithPhoto:photo loadingView:loadingView notificationCenter:notificationCenter]; + [self commonInitWithPhoto:photo itemIndex:itemIndex loadingView:loadingView notificationCenter:notificationCenter]; } return self; } -- (void)commonInitWithPhoto:(id )photo loadingView:(UIView *)loadingView notificationCenter:(NSNotificationCenter *)notificationCenter { +- (void)commonInitWithPhoto:(id )photo itemIndex:(NSUInteger)itemIndex loadingView:(UIView *)loadingView notificationCenter:(NSNotificationCenter *)notificationCenter { _photo = photo; + _interstitialView = nil; + _photoViewItemIndex = itemIndex; if (photo.imageData) { _scalingImageView = [[NYTScalingImageView alloc] initWithImageData:photo.imageData frame:CGRectZero]; diff --git a/NYTPhotoViewer/NYTPhotoViewer.h b/NYTPhotoViewer/NYTPhotoViewer.h index c28f4983..1d601a51 100644 --- a/NYTPhotoViewer/NYTPhotoViewer.h +++ b/NYTPhotoViewer/NYTPhotoViewer.h @@ -29,7 +29,7 @@ FOUNDATION_EXPORT const unsigned char NYTPhotoViewerVersionString[]; // Protocols #import #import -#import +#import #import // Support diff --git a/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h b/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h index f1a6baf2..1295441e 100644 --- a/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h +++ b/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN /** * A simple concrete implementation of `NYTPhotoViewerDataSource`, for use with an array of images. + * Does not support interstitial views. */ @interface NYTPhotoViewerArrayDataSource : NSObject diff --git a/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.m b/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.m index 37d8ea53..562a34ae 100644 --- a/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.m +++ b/NYTPhotoViewer/NYTPhotoViewerArrayDataSource.m @@ -63,7 +63,7 @@ - (NSInteger)indexOfPhoto:(id )photo { #pragma mark - Subscripting - (id)objectAtIndexedSubscript:(NSUInteger)idx { - return self.photos[idx]; + return [self photoAtIndex:idx]; } @end diff --git a/NYTPhotoViewer/NYTPhotoViewerCore.h b/NYTPhotoViewer/NYTPhotoViewerCore.h index 55dc8a38..e25cfb8a 100644 --- a/NYTPhotoViewer/NYTPhotoViewerCore.h +++ b/NYTPhotoViewer/NYTPhotoViewerCore.h @@ -29,7 +29,7 @@ FOUNDATION_EXPORT const unsigned char NYTPhotoViewerCoreVersionString[]; // Protocols #import #import -#import +#import #import // Support diff --git a/NYTPhotoViewer/NYTPhotosViewController.h b/NYTPhotoViewer/NYTPhotosViewController.h index be8a9845..d1cafcc7 100644 --- a/NYTPhotoViewer/NYTPhotosViewController.h +++ b/NYTPhotoViewer/NYTPhotosViewController.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN */ extern NSString * const NYTPhotosViewControllerDidNavigateToPhotoNotification; +extern NSString * const NYTPhotosViewControllerDidNavigateToInterstitialViewNotification; + /** * Notification name issued when this `NYTPhotosViewController` is about to be dismissed. * @@ -184,6 +186,15 @@ extern NSString * const NYTPhotosViewControllerDidDismissNotification; */ - (void)photosViewController:(NYTPhotosViewController *)photosViewController didNavigateToPhoto:(id )photo atIndex:(NSUInteger)photoIndex; +/** + * Called when a new interstitial view is displayed through a swipe gesture. + * + * @param photosViewController The `NYTPhotosViewController` instance that sent the delegate message. + * @param view The view that was just displayed. + * @param index The index of the view that was just displayed. + */ +- (void)photosViewController:(NYTPhotosViewController *)photosViewController didNavigateToInterstialView:(UIView *)view atIndex:(NSUInteger)index; + /** * Called immediately before the `NYTPhotosViewController` is about to start a user-initiated dismissal. * This will be the beginning of the interactive panning to dismiss, if it is enabled and performed. @@ -298,6 +309,16 @@ extern NSString * const NYTPhotosViewControllerDidDismissNotification; */ - (void)photosViewController:(NYTPhotosViewController *)photosViewController actionCompletedWithActivityType:(NSString * _Nullable)activityType; +/** + * called when an `NYTInterstitialViewController` is created but before it is displayed. Returns the view to display as an interstitial view. + * + * @param photosViewController The `NYTPhotosViewController` instance that sent the delegate message. + * @param index The index in the page view controller where the view will be displayed. + * + * @return A `UIView`. + */ +- (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController interstitialViewAtIndex:(NSUInteger)index; + @end NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewer/NYTPhotosViewController.m b/NYTPhotoViewer/NYTPhotosViewController.m index 79e3281b..e089d081 100644 --- a/NYTPhotoViewer/NYTPhotosViewController.m +++ b/NYTPhotoViewer/NYTPhotosViewController.m @@ -10,6 +10,7 @@ #import "NYTPhotoViewerDataSource.h" #import "NYTPhotoViewerArrayDataSource.h" #import "NYTPhotoViewController.h" +#import "NYTInterstitialViewController.h" #import "NYTPhotoTransitionController.h" #import "NYTScalingImageView.h" #import "NYTPhoto.h" @@ -22,6 +23,7 @@ #endif NSString * const NYTPhotosViewControllerDidNavigateToPhotoNotification = @"NYTPhotosViewControllerDidNavigateToPhotoNotification"; +NSString * const NYTPhotosViewControllerDidNavigateToInterstitialViewNotification = @"NYTPhotosViewControllerDidNavigateToInterstitialViewNotification"; NSString * const NYTPhotosViewControllerWillDismissNotification = @"NYTPhotosViewControllerWillDismissNotification"; NSString * const NYTPhotosViewControllerDidDismissNotification = @"NYTPhotosViewControllerDidDismissNotification"; @@ -214,11 +216,12 @@ - (void)commonInitWithDataSource:(id )dataSource initi - (void)configurePageViewControllerWithInitialPhoto { NYTPhotoViewController *initialPhotoViewController; - if (self.initialPhoto != nil && [self.dataSource indexOfPhoto:self.initialPhoto] != NSNotFound) { - initialPhotoViewController = [self newPhotoViewControllerForPhoto:self.initialPhoto]; + NSInteger initialPhotoIndex = [self.dataSource indexOfPhoto:self.initialPhoto]; + if (self.initialPhoto != nil && initialPhotoIndex != NSNotFound) { + initialPhotoViewController = [self newPhotoViewControllerForPhoto:self.initialPhoto atIndex:initialPhotoIndex]; } else { - initialPhotoViewController = [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:0]]; + initialPhotoViewController = [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:0] atIndex:0]; } [self setCurrentlyDisplayedViewController:initialPhotoViewController animated:NO]; @@ -239,7 +242,7 @@ - (void)addOverlayView { - (void)updateOverlayInformation { NSString *overlayTitle; - NSUInteger photoIndex = [self.dataSource indexOfPhoto:self.currentlyDisplayedPhoto]; + NSUInteger photoIndex = self.currentPhotoViewController.photoViewItemIndex; NSInteger displayIndex = photoIndex + 1; if ([self.delegate respondsToSelector:@selector(photosViewController:titleForPhoto:atIndex:totalPhotoCount:)]) { @@ -249,9 +252,10 @@ - (void)updateOverlayInformation { if (!overlayTitle && self.dataSource.numberOfPhotos == nil) { overlayTitle = [NSString localizedStringWithFormat:@"%lu", (unsigned long)displayIndex]; } - - if (!overlayTitle && self.dataSource.numberOfPhotos.integerValue > 1) { - overlayTitle = [NSString localizedStringWithFormat:NSLocalizedString(@"%lu of %lu", nil), (unsigned long)displayIndex, (unsigned long)self.dataSource.numberOfPhotos.integerValue]; + + NSInteger totalItems = [self totalItemCount]; + if (!overlayTitle && totalItems > 1) { + overlayTitle = [NSString localizedStringWithFormat:NSLocalizedString(@"%lu of %lu", nil), (unsigned long)displayIndex, (unsigned long)totalItems]; } self.overlayView.title = overlayTitle; @@ -343,11 +347,12 @@ - (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems { } - (void)displayPhoto:(id )photo animated:(BOOL)animated { - if ([self.dataSource indexOfPhoto:photo] == NSNotFound) { + NSInteger indexOfPhoto = [self.dataSource indexOfPhoto:photo]; + if (indexOfPhoto == NSNotFound) { return; } - NYTPhotoViewController *photoViewController = [self newPhotoViewControllerForPhoto:photo]; + NYTPhotoViewController *photoViewController = [self newPhotoViewControllerForPhoto:photo atIndex:indexOfPhoto]; [self setCurrentlyDisplayedViewController:photoViewController animated:animated]; [self updateOverlayInformation]; } @@ -458,7 +463,7 @@ - (void)dismissViewControllerAnimated:(BOOL)animated userInitiated:(BOOL)isUserI #pragma mark - Convenience -- (void)setCurrentlyDisplayedViewController:(UIViewController *)viewController animated:(BOOL)animated { +- (void)setCurrentlyDisplayedViewController:(UIViewController *)viewController animated:(BOOL)animated { if (!viewController) { return; } @@ -467,8 +472,8 @@ - (void)setCurrentlyDisplayedViewController:(UIViewController )photo { +- (NYTPhotoViewController *)newPhotoViewControllerForPhoto:(id )photo atIndex:(NSUInteger)index { if (photo) { UIView *loadingView; if ([self.delegate respondsToSelector:@selector(photosViewController:loadingViewForPhoto:)]) { loadingView = [self.delegate photosViewController:self loadingViewForPhoto:photo]; } - NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:photo loadingView:loadingView notificationCenter:self.notificationCenter]; + NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:photo itemIndex:index loadingView:loadingView notificationCenter:self.notificationCenter]; photoViewController.delegate = self; [self.singleTapGestureRecognizer requireGestureRecognizerToFail:photoViewController.doubleTapGestureRecognizer]; @@ -518,14 +523,34 @@ - (NYTPhotoViewController *)newPhotoViewControllerForPhoto:(id )photo return nil; } -- (void)didNavigateToPhoto:(id )photo { +- (UIViewController *)newViewControllerAtIndex:(NSUInteger)index { + if ([self.delegate respondsToSelector:@selector(photosViewController:interstitialViewAtIndex:)]) { + UIView *view = [self.delegate photosViewController:self interstitialViewAtIndex:index]; + + NYTInterstitialViewController *interstitialViewController = [[NYTInterstitialViewController alloc] initWithView:view itemIndex:index]; + + return interstitialViewController; + } + + return nil; +} + +- (void)didNavigateToPhoto:(id )photo atIndex:(NSUInteger)index { if ([self.delegate respondsToSelector:@selector(photosViewController:didNavigateToPhoto:atIndex:)]) { - [self.delegate photosViewController:self didNavigateToPhoto:photo atIndex:[self.dataSource indexOfPhoto:photo]]; + [self.delegate photosViewController:self didNavigateToPhoto:photo atIndex:index]; } [[NSNotificationCenter defaultCenter] postNotificationName:NYTPhotosViewControllerDidNavigateToPhotoNotification object:self]; } +- (void)didNavigateToInterstitialView:(UIView *)view atIndex:(NSUInteger)index { + if ([self.delegate respondsToSelector:@selector(photosViewController:didNavigateToInterstialView:atIndex:)]) { + [self.delegate photosViewController:self didNavigateToInterstialView:view atIndex:index]; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:NYTPhotosViewControllerDidNavigateToInterstitialViewNotification object:self]; +} + - (id )currentlyDisplayedPhoto { return self.currentPhotoViewController.photo; } @@ -546,6 +571,14 @@ - (CGPoint)boundsCenterPoint { return CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)); } +- (NSInteger)totalItemCount { + NSInteger numberOfInterstitialViews = 0; + if ([self.dataSource respondsToSelector:@selector(numberOfInterstitialViews)]) { + numberOfInterstitialViews = self.dataSource.numberOfInterstitialViews.integerValue; + } + return self.dataSource.numberOfPhotos.integerValue + numberOfInterstitialViews; +} + #pragma mark - NYTPhotoViewControllerDelegate - (void)photoViewController:(NYTPhotoViewController *)photoViewController didLongPressWithGestureRecognizer:(UILongPressGestureRecognizer *)longPressGestureRecognizer { @@ -569,22 +602,40 @@ - (void)photoViewController:(NYTPhotoViewController *)photoViewController didLon #pragma mark - UIPageViewControllerDataSource -- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { - NSUInteger photoIndex = [self.dataSource indexOfPhoto:viewController.photo]; - if (photoIndex == 0 || photoIndex == NSNotFound) { +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { + NSUInteger itemIndex = viewController.photoViewItemIndex; + if (itemIndex == 0) { return nil; } - return [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:(photoIndex - 1)]]; + NSUInteger beforeIndex = itemIndex - 1; + BOOL isPhotoAvailableAtIndex = true; + if ([self.dataSource respondsToSelector:@selector(isPhotoAtIndex:)]) { + isPhotoAvailableAtIndex = [self.dataSource isPhotoAtIndex:beforeIndex]; + } + if (isPhotoAvailableAtIndex) { + return [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:beforeIndex] atIndex:beforeIndex]; + } else { + return [self newViewControllerAtIndex:beforeIndex]; + } } -- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { - NSUInteger photoIndex = [self.dataSource indexOfPhoto:viewController.photo]; - if (photoIndex == NSNotFound) { +- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { + NSUInteger itemIndex = viewController.photoViewItemIndex; + NSUInteger afterIndex = itemIndex + 1; + if (afterIndex >= [self totalItemCount]) { return nil; } - return [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:(photoIndex + 1)]]; + BOOL isPhotoAvailableAtIndex = true; + if ([self.dataSource respondsToSelector:@selector(isPhotoAtIndex:)]) { + isPhotoAvailableAtIndex = [self.dataSource isPhotoAtIndex:afterIndex]; + } + if (isPhotoAvailableAtIndex) { + return [self newPhotoViewControllerForPhoto:[self.dataSource photoAtIndex:afterIndex] atIndex:afterIndex]; + } else { + return [self newViewControllerAtIndex:afterIndex]; + } } #pragma mark - UIPageViewControllerDelegate @@ -592,9 +643,13 @@ - (UIViewController *)pageViewController:(UIPageViewController *)pageViewControl - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { if (completed) { [self updateOverlayInformation]; - - UIViewController *photoViewController = pageViewController.viewControllers.firstObject; - [self didNavigateToPhoto:photoViewController.photo]; + + UIViewController *viewController = pageViewController.viewControllers.firstObject; + if (viewController.photo == nil) { + [self didNavigateToInterstitialView:viewController.interstitialView atIndex:viewController.photoViewItemIndex]; + } else { + [self didNavigateToPhoto:viewController.photo atIndex:viewController.photoViewItemIndex]; + } } } diff --git a/NYTPhotoViewer/Protocols/NYTPhotoContainer.h b/NYTPhotoViewer/Protocols/NYTPhotoContainer.h deleted file mode 100644 index ca9bd1f7..00000000 --- a/NYTPhotoViewer/Protocols/NYTPhotoContainer.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// NYTPhotoContainer.h -// NYTPhotoViewer -// -// Created by Brian Capps on 2/11/15. -// -// - -@protocol NYTPhoto; - -/** - * A protocol that defines that an object contains a photo property. - */ -@protocol NYTPhotoContainer - -/** - * An object conforming to the `NYTPhoto` protocol. - */ -@property (nonatomic, readonly) id photo; - -@end diff --git a/NYTPhotoViewer/Protocols/NYTPhotoViewerContainer.h b/NYTPhotoViewer/Protocols/NYTPhotoViewerContainer.h new file mode 100644 index 00000000..907070f7 --- /dev/null +++ b/NYTPhotoViewer/Protocols/NYTPhotoViewerContainer.h @@ -0,0 +1,35 @@ +// +// NYTPhotoViewerContainer.h +// NYTPhotoViewer +// +// Created by Brian Capps on 2/11/15. +// +// + +@protocol NYTPhoto; + +/** + * A protocol that defines that an object contains a photo or interstitial view property + * and the index of the item in the collection. + */ +@protocol NYTPhotoViewerContainer + +/** + * An object conforming to the `NYTPhoto` protocol. + * Will be nil if the container has a view. + */ +@property (nonatomic, readonly, nullable) id photo; + +/** + * A view to be displayed instead of a photo. + * Will be nil if the container has a photo. + */ +@property (nonatomic, readonly, nullable) UIView *interstitialView; + +/** + * The index of this item in the collection. + */ + +@property (nonatomic, readonly) NSUInteger photoViewItemIndex; + +@end diff --git a/NYTPhotoViewer/Protocols/NYTPhotoViewerDataSource.h b/NYTPhotoViewer/Protocols/NYTPhotoViewerDataSource.h index 6f6ad5b5..4d1d4d9b 100644 --- a/NYTPhotoViewer/Protocols/NYTPhotoViewerDataSource.h +++ b/NYTPhotoViewer/Protocols/NYTPhotoViewerDataSource.h @@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN * * Alternatively, `NYTPhotoViewerArrayDataSource` and `NYTPhotoViewerSinglePhotoDataSource` are concrete classes which conveniently handle the most common use cases for NYTPhotoViewer. */ -@protocol NYTPhotoViewerDataSource +@protocol NYTPhotoViewerDataSource /** * The total number of photos in the data source, or `nil` if the number is not known. @@ -46,6 +46,31 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable id )photoAtIndex:(NSInteger)photoIndex; +@optional + +/** + * The total number of interstitial views in the data source. + * + * The number returned should package an `NSInteger` value. + * + * @return The number of interstitial views or `nil` if the number is not known. + */ +- (NSNumber *)numberOfInterstitialViews; + +/** + * Indicates if the item at the specified index is a photo. + * + * @return `true` if the item at the specified index is a photo, `false` otherwise. + */ +- (BOOL)isPhotoAtIndex:(NSInteger)idx; + +/** + * Indicates if the item at the specified index is an interstitial view. + * + * @return `true` if the item at the specified index is an interstitial view, `false` otherwise. + */ +- (BOOL)isInterstitialViewAtIndex:(NSInteger)idx; + @end NS_ASSUME_NONNULL_END diff --git a/NYTPhotoViewerTests/NYTPhotoViewControllerTests.m b/NYTPhotoViewerTests/NYTPhotoViewControllerTests.m index c5fa61a1..9373f717 100644 --- a/NYTPhotoViewerTests/NYTPhotoViewControllerTests.m +++ b/NYTPhotoViewerTests/NYTPhotoViewControllerTests.m @@ -19,34 +19,34 @@ @interface NYTPhotoViewControllerTests : XCTestCase @implementation NYTPhotoViewControllerTests - (void)testScalingImageViewExistsAferInitialization { - NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] loadingView:nil notificationCenter:nil]; + NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] itemIndex:0 loadingView:nil notificationCenter:nil]; XCTAssertNotNil(photoViewController.scalingImageView); } - (void)testDoubleTapGestureRecognizerExistsAferInitialization { - NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] loadingView:nil notificationCenter:nil]; + NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] itemIndex:0 loadingView:nil notificationCenter:nil]; XCTAssertNotNil(photoViewController.doubleTapGestureRecognizer); } - (void)testLoadingViewExistsAferNilInitialization { - NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] loadingView:nil notificationCenter:nil]; + NYTPhotoViewController *photoViewController = [[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] itemIndex:0 loadingView:nil notificationCenter:nil]; XCTAssertNotNil(photoViewController.loadingView); } - (void)testDesignatedInitializerAcceptsNilForPhotoArgument { - XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:nil loadingView:[[UIView alloc] init] notificationCenter:[NSNotificationCenter defaultCenter]]); + XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:nil itemIndex:0 loadingView:[[UIView alloc] init] notificationCenter:[NSNotificationCenter defaultCenter]]); } - (void)testDesignatedInitializerAcceptsNilForLoadingViewArgument { - XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] loadingView:nil notificationCenter:[NSNotificationCenter defaultCenter]]); + XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] itemIndex:0 loadingView:nil notificationCenter:[NSNotificationCenter defaultCenter]]); } - (void)testDesignatedInitializerAcceptsNilForNotificationCenterArgument { - XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] loadingView:[[UIView alloc] init] notificationCenter:nil]); + XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:[self newTestPhoto] itemIndex:0 loadingView:[[UIView alloc] init] notificationCenter:nil]); } - (void)testDesignatedInitializerAcceptsNilForAllArguments { - XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:nil loadingView:nil notificationCenter:nil]); + XCTAssertNoThrow([[NYTPhotoViewController alloc] initWithPhoto:nil itemIndex:0 loadingView:nil notificationCenter:nil]); } - (NYTExamplePhoto *)newTestPhoto { diff --git a/NYTPhotoViewerTests/NYTPhotosDataSourceTests.m b/NYTPhotoViewerTests/NYTPhotoViewerArrayDataSourceTests.m similarity index 56% rename from NYTPhotoViewerTests/NYTPhotosDataSourceTests.m rename to NYTPhotoViewerTests/NYTPhotoViewerArrayDataSourceTests.m index af688cb9..f6f91593 100644 --- a/NYTPhotoViewerTests/NYTPhotosDataSourceTests.m +++ b/NYTPhotoViewerTests/NYTPhotoViewerArrayDataSourceTests.m @@ -9,37 +9,37 @@ @import UIKit; @import XCTest; -#import #import "NYTExamplePhoto.h" +#import "NYTPhotoViewerArrayDataSource.h" -@interface NYTPhotosDataSourceTests : XCTestCase +@interface NYTPhotoViewerArrayDataSourceTests : XCTestCase @end -@implementation NYTPhotosDataSourceTests +@implementation NYTPhotoViewerArrayDataSourceTests - (void)testInitializerAcceptsNil { - XCTAssertNoThrow([[NYTPhotosDataSource alloc] initWithPhotos:nil]); + XCTAssertNoThrow([[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:nil]); } - (void)testOutOfBoundsDoesNotCrash { - NYTPhotosDataSource *dataSource = [[NYTPhotosDataSource alloc] initWithPhotos:nil]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:nil]; XCTAssertNoThrow(dataSource[1]); } - (void)testOutOfBoundsReturnsNil { - NYTPhotosDataSource *dataSource = [[NYTPhotosDataSource alloc] initWithPhotos:nil]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:nil]; XCTAssertNil(dataSource[1]); } - (void)testValidIndexReturnsPhoto { - NYTPhotosDataSource *dataSource = [[NYTPhotosDataSource alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:[self newTestPhotos]]; XCTAssertNotNil(dataSource[1]); } - (void)testValidIndexReturnsCorrectPhoto { NSArray *photos = [self newTestPhotos]; - NYTPhotosDataSource *dataSource = [[NYTPhotosDataSource alloc] initWithPhotos:photos]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:photos]; XCTAssertEqualObjects(photos.firstObject, dataSource[0]); } diff --git a/NYTPhotoViewerTests/NYTPhotoViewerSinglePhotoDataSourceTests.m b/NYTPhotoViewerTests/NYTPhotoViewerSinglePhotoDataSourceTests.m new file mode 100644 index 00000000..cf00330b --- /dev/null +++ b/NYTPhotoViewerTests/NYTPhotoViewerSinglePhotoDataSourceTests.m @@ -0,0 +1,64 @@ +// +// NYTPhotosDataSourceTests.m +// NYTPhotoViewer +// +// Created by Brian Capps on 2/26/15. +// Copyright (c) 2015 NYTimes. All rights reserved. +// + +@import UIKit; +@import XCTest; + +#import "NYTExamplePhoto.h" +#import "NYTPhotoViewerSinglePhotoDataSource.h" + +@interface NYTPhotoViewerSinglePhotoDataSourceTests : XCTestCase + +@end + +@implementation NYTPhotoViewerSinglePhotoDataSourceTests + +- (void)testInitializerAcceptsNil { + XCTAssertNoThrow([[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:nil]); +} + +- (void)testOutOfBoundsDoesNotCrash { + NYTPhotoViewerSinglePhotoDataSource *dataSource = [[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:nil]; + XCTAssertNoThrow([dataSource photoAtIndex:1]); +} + +- (void)testOutOfBoundsReturnsNil { + NYTPhotoViewerSinglePhotoDataSource *dataSource = [[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:nil]; + XCTAssertNil([dataSource photoAtIndex:1]); +} + +- (void)testValidIndexReturnsPhotoAtIndex { + NYTExamplePhoto *photo = [[NYTExamplePhoto alloc] init]; + NYTPhotoViewerSinglePhotoDataSource *dataSource = [[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:photo]; + XCTAssertEqual(0, [dataSource indexOfPhoto:photo]); +} + +- (void)testValidIndexReturnsPhoto { + NYTExamplePhoto *photo = [[NYTExamplePhoto alloc] init]; + NYTPhotoViewerSinglePhotoDataSource *dataSource = [[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:photo]; + XCTAssertNotNil([dataSource photoAtIndex:1]); +} + +- (void)testValidIndexReturnsCorrectPhoto { + NSArray *photos = [self newTestPhotos]; + NYTPhotoViewerSinglePhotoDataSource *dataSource = [[NYTPhotoViewerSinglePhotoDataSource alloc] initWithPhoto:photos.firstObject]; + XCTAssertEqualObjects(photos.firstObject, [dataSource photoAtIndex:0]); +} + +- (NSArray *)newTestPhotos { + NSMutableArray *photos = [NSMutableArray array]; + + for (int i = 0; i < 5; i++) { + NYTExamplePhoto *photo = [[NYTExamplePhoto alloc] init]; + [photos addObject:photo]; + } + + return photos; +} + +@end diff --git a/NYTPhotoViewerTests/NYTPhotosViewControllerTests.m b/NYTPhotoViewerTests/NYTPhotosViewControllerTests.m index 93d01452..14be0d31 100644 --- a/NYTPhotoViewerTests/NYTPhotosViewControllerTests.m +++ b/NYTPhotoViewerTests/NYTPhotosViewControllerTests.m @@ -13,6 +13,8 @@ #import #import "NYTExamplePhoto.h" +#import "NYTPhotoViewerArrayDataSource.h" + @interface NYTPhotosViewControllerTests : XCTestCase @end @@ -28,56 +30,62 @@ - (void)doneButtonTapped:(id)sender; @implementation NYTPhotosViewControllerTests - (void)testPanGestureRecognizerExistsAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.panGestureRecognizer); } - (void)testPanGestureRecognizerHasAssociatedViewAfterViewDidLoad { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; __unused id view = photosViewController.view; XCTAssertNotNil(photosViewController.panGestureRecognizer.view); } - (void)testSingleTapGestureRecognizerExistsAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.singleTapGestureRecognizer); } - (void)testSingleTapGestureRecognizerHasAssociatedViewAfterViewDidLoad { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; __unused id view = photosViewController.view; XCTAssertNotNil(photosViewController.singleTapGestureRecognizer.view); } - (void)testPageViewControllerExistsAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; [photosViewController viewDidLoad]; - + XCTAssertNotNil(photosViewController.pageViewController); } - (void)testPageViewControllerDoesNotHaveAssociatedSuperviewBeforeViewLoads { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNil(photosViewController.pageViewController.view.superview); } - (void)testPageViewControllerHasAssociatedSuperviewAfterViewLoads { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; photosViewController.view = photosViewController.view; // Referencing the view loads it. XCTAssertNotNil(photosViewController.pageViewController.view.superview); } - (void)testCurrentlyDisplayedPhotoIsFirstAfterConvenienceInitialization { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:photos]; + + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:dataSource]; [photosViewController viewDidLoad]; XCTAssertEqualObjects(photos.firstObject, photosViewController.currentlyDisplayedPhoto); } + - (void)testCurrentlyDisplayedPhotoIsAccurateAfterSettingInitialPhoto { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.lastObject]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:photos]; + + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:dataSource initialPhoto:photos.lastObject delegate:nil]; [photosViewController viewDidLoad]; XCTAssertEqualObjects(photos.lastObject, photosViewController.currentlyDisplayedPhoto); @@ -85,7 +93,9 @@ - (void)testCurrentlyDisplayedPhotoIsAccurateAfterSettingInitialPhoto { - (void)testCurrentlyDisplayedPhotoIsAccurateAfterDisplayPhotoCall { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.lastObject]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:photos]; + + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:dataSource initialPhoto:photos.lastObject delegate:nil]; [photosViewController viewDidLoad]; [photosViewController displayPhoto:photos.firstObject animated:NO]; @@ -93,162 +103,161 @@ - (void)testCurrentlyDisplayedPhotoIsAccurateAfterDisplayPhotoCall { } - (void)testLeftBarButtonItemIsPopulatedAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.leftBarButtonItem); } - (void)testLeftBarButtonItemIsNilAfterSettingToNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; photosViewController.leftBarButtonItem = nil; XCTAssertNil(photosViewController.leftBarButtonItem); } - (void)testLeftBarButtonItemsArePopulatedAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.leftBarButtonItems); } - (void)testLeftBarButtonItemsAreNilAfterSettingToNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; photosViewController.leftBarButtonItems = nil; XCTAssertNil(photosViewController.leftBarButtonItems); } - (void)testRightBarButtonItemIsPopulatedAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.rightBarButtonItem); } - (void)testRightBarButtonItemIsNilAfterSettingToNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; photosViewController.rightBarButtonItem = nil; XCTAssertNil(photosViewController.rightBarButtonItem); } - (void)testRightBarButtonItemsArePopulatedAfterInitialization { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNotNil(photosViewController.rightBarButtonItems); } - (void)testRightBarButtonItemsAreNilAfterSettingToNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; photosViewController.rightBarButtonItems = nil; XCTAssertNil(photosViewController.rightBarButtonItems); } - (void)testOneArgConvenienceInitializerAcceptsNil { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:nil]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:nil]); } - (void)testTwoArgConvenienceInitializerAcceptsNilForPhotosParameter { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:nil initialPhoto:[[NYTExamplePhoto alloc] init]]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:nil initialPhoto:[[NYTExamplePhoto alloc] init] delegate:nil]); } - (void)testTwoArgConvenienceInitializerAcceptsNilForInitialPhotoParameter { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos] initialPhoto:nil]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:nil delegate:nil]); } - (void)testTwoArgConvenienceInitializerAcceptsNilForBothParameters { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:nil initialPhoto:nil]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:nil initialPhoto:nil delegate:nil]); } - (void)testDesignatedInitializerAcceptsNilForPhotosParameter { id delegateMock = OCMProtocolMock(@protocol(NYTPhotosViewControllerDelegate)); - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:nil initialPhoto:[NYTExamplePhoto new] delegate:delegateMock]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:nil initialPhoto:[NYTExamplePhoto new] delegate:delegateMock]); } - (void)testDesignatedInitializerAcceptsNilForInitialPhotoParameter { id delegateMock = OCMProtocolMock(@protocol(NYTPhotosViewControllerDelegate)); - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos] initialPhoto:nil delegate:delegateMock]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:nil delegate:delegateMock]); } - (void)testDesignatedInitializerAcceptsNilForDelegateParameter { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos] initialPhoto:[NYTExamplePhoto new] delegate:nil]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:[NYTExamplePhoto new] delegate:nil]); } - (void)testDesignatedInitializerAcceptsNilForAllParameters { - XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithPhotos:nil initialPhoto:nil delegate:nil]); + XCTAssertNoThrow([[NYTPhotosViewController alloc] initWithDataSource:nil initialPhoto:nil delegate:nil]); } - (void)testDesignatedInitializerSetsDelegate { id delegateMock = OCMProtocolMock(@protocol(NYTPhotosViewControllerDelegate)); - NYTPhotosViewController *sut = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos] initialPhoto:nil delegate:delegateMock]; + NYTPhotosViewController *sut = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:nil delegate:delegateMock]; XCTAssertEqual(sut.delegate, delegateMock); } - (void)testDisplayPhotoAcceptsNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertNoThrow([photosViewController displayPhoto:nil animated:NO]); } - (void)testDisplayPhotoDoesNothingWhenPassedPhotoOutsideDataSource { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.firstObject]; + NYTPhotoViewerArrayDataSource *dataSource = [[NYTPhotoViewerArrayDataSource alloc] initWithPhotos:photos]; + + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:dataSource initialPhoto:photos.firstObject delegate:nil]; [photosViewController viewDidLoad]; - + NYTExamplePhoto *invalidPhoto = [[NYTExamplePhoto alloc] init]; - + [photosViewController displayPhoto:invalidPhoto animated:NO]; XCTAssertEqualObjects(photos.firstObject, photosViewController.currentlyDisplayedPhoto); } - (void)testDisplayPhotoMovesToCorrectPhoto { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.firstObject]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:photos.firstObject delegate:nil]; [photosViewController viewDidLoad]; NYTExamplePhoto *photoToDisplay = photos[2]; - + [photosViewController displayPhoto:photoToDisplay animated:NO]; XCTAssertEqualObjects(photoToDisplay, photosViewController.currentlyDisplayedPhoto); } - (void)testUpdateImageForPhotoAcceptsNil { - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:[self newTestPhotos]]; - XCTAssertNoThrow([photosViewController updateImageForPhoto:nil]); + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; + XCTAssertNoThrow([photosViewController updatePhoto:nil]); } - (void)testUpdateImageForPhotoDoesNothingWhenPassedPhotoOutsideDataSource { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.firstObject]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:photos.firstObject delegate:nil]; NYTExamplePhoto *invalidPhoto = [[NYTExamplePhoto alloc] init]; invalidPhoto.image = [[UIImage alloc] init]; - - [photosViewController updateImageForPhoto:invalidPhoto]; - + + [photosViewController updatePhoto:invalidPhoto]; + XCTAssertEqualObjects(photosViewController.currentlyDisplayedPhoto.image, [photos.firstObject image]); } - (void)testUpdateImageForPhotoUpdatesImage { NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos initialPhoto:photos.firstObject]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource] initialPhoto:photos.firstObject delegate:nil]; NYTExamplePhoto *photoToUpdate = photos.firstObject; photoToUpdate.image = [UIImage imageNamed:@"NYTimesBuilding" inBundle:[NSBundle bundleForClass:[self class]] compatibleWithTraitCollection:nil]; - - [photosViewController updateImageForPhoto:photoToUpdate]; - + + [photosViewController updatePhoto:photoToUpdate]; + XCTAssertEqualObjects(photosViewController.currentlyDisplayedPhoto.image, photoToUpdate.image); } - (void)testViewIsntLoadedAfterInit { - NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertFalse(photosViewController.isViewLoaded); } - (void)testPageViewIsntLoadedAfterInit { - NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; XCTAssertFalse(photosViewController.pageViewController.isViewLoaded); } - (void)testDoneButtonDismissalUserInitiatedFlagIsTrue { - NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; id photosVCMock = OCMPartialMock(photosViewController); @@ -258,8 +267,7 @@ - (void)testDoneButtonDismissalUserInitiatedFlagIsTrue { } - (void)testGestureBasedDismissalUserInitiatedFlagIsTrue { - NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; id photosVCMock = OCMPartialMock(photosViewController); @@ -272,8 +280,7 @@ - (void)testGestureBasedDismissalUserInitiatedFlagIsTrue { } - (void)testProgrammaticDismissalUserInitiatedFlagIsFalse { - NSArray *photos = [self newTestPhotos]; - NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:photos]; + NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:[self newTestPhotosDataSource]]; id photosVCMock = OCMPartialMock(photosViewController); @@ -295,4 +302,10 @@ - (NSArray *)newTestPhotos { return photos; } +- (NYTPhotoViewerArrayDataSource *)newTestPhotosDataSource { + + return [NYTPhotoViewerArrayDataSource dataSourceWithPhotos:[self newTestPhotos]]; + +} + @end diff --git a/README.md b/README.md index 3e8a4116..dcf4727e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] ## Running the Example -The Example project uses [Carthage](https://github.com/Carthage/Carthage) to integrate its dependencies. If you don’t have Carthge installed, you can install it via [Homebrew](http://brew.sh) with `brew install carthage`. +The Example project uses [Carthage](https://github.com/Carthage/Carthage) to integrate its dependencies. If you don’t have Carthage installed, you can install it via [Homebrew](http://brew.sh) with `brew install carthage`. Then, in your checkout of the `NYTPhotoViewer` repo, run `carthage checkout --use-submodules`.