Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose a data-source-oriented API for PhotosViewController #226

Merged
merged 37 commits into from
Jan 31, 2017
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1acd496
clarify when `NYTPhotoCaptionView` is used
Dec 9, 2016
66784db
rename concrete data source to `NYTPhotoViewerArrayDataSource`
Dec 9, 2016
f5e3dc8
[minor] add clarifying comment to umbrella header
Dec 9, 2016
4c180d9
Rename `NYTPhotoViewerDataSource` and clarify it vs. delegate
Dec 9, 2016
d1394c6
[minor] Improve comments in `NYTPhoto.h`
Dec 9, 2016
e615a09
Update library code to work with new `NYTPhotoViewerDataSource`
Dec 10, 2016
2652474
Update Example app to work with new `NYTPhotoViewerDataSource`
Dec 10, 2016
577aa89
Clean up ArrayDataSource interface
Jan 26, 2017
ae015e2
Add factory method for ArrayDataSource
Jan 26, 2017
620540d
Handle null input properly in ArrayDataSource
Jan 26, 2017
207a448
NYTPhotosViewController takes a data source, not an array
Jan 26, 2017
3e0dba9
Allow informing PhotosVC that an individual photo has changed
Jan 26, 2017
de002c1
Better document updatePhotoAtIndex: behavior
Jan 27, 2017
ed8f246
refactor: use a property that’s already been implemented
Jan 27, 2017
b0a9525
Add another example photo
Jan 27, 2017
762483b
Better document updatePhoto: methods
Jan 27, 2017
587c82b
Allow setting `NYTPhotosVC` data source
Jan 27, 2017
4750578
Add reload-all-photos method to `NYTPhotosVC`
Jan 27, 2017
d3d2323
Add an Example codepath which demos replacing the data source
Jan 27, 2017
290ba8c
Improve `NYTPhotosVC` animation logic
Jan 27, 2017
8f6011a
Add a single-photo data source class
Jan 27, 2017
3dcdbf0
docs: tweak ArrayDataSource initializer documentation
Jan 27, 2017
f8ccb16
Modernize PhotosVC code style
Jan 27, 2017
62d54be
Make Xcode (8.2) recommended updates to NYTPhotoViewer’s Xcode project
Jan 27, 2017
21c3852
Avoid using `NYTPhotosVC`’s delegate until after `-init…` returns
Jan 27, 2017
bb38825
Add convenience `NYTPhotosVC` initializer accepting solely a data source
Jan 27, 2017
2f45ab9
Fix incorrect `NYTPhotosVC` convenience initializer
Jan 27, 2017
b5e3c07
style: correct spacing in Example ViewController
Jan 27, 2017
f71d8f4
fix: use proper equality checks
Jan 27, 2017
4f81acb
docs: clarify how NYTPhoto equality is determined
Jan 27, 2017
cf9cfbc
docs: add this feature to CHANGELOG
Jan 27, 2017
a57f6d2
Update LICENSE to specify (c) through 2017
Jan 30, 2017
db4ccb5
docs: add missing copyright in headers
Jan 30, 2017
bbacf0c
example: Use a more obvious demo for custom maximum zoom scale
Jan 31, 2017
7dc1e0d
example: Improve data-source-swap demo
Jan 31, 2017
d7aa268
example: Improve caption for photo with a loading spinner
Jan 31, 2017
c67c605
Use more modern `nullable` instead of `_Nullable`
Jan 31, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Changes for users of the library currently on `develop`:

_This space intentionally left blank._
- Expose a data-source-oriented API for PhotosViewController ([#226](https://github.com/NYTimes/NYTPhotoViewer/pull/226))

## [1.2.0](https://github.com/NYTimes/NYTPhotoViewer/releases/tag/1.2.0)

Expand Down
10 changes: 10 additions & 0 deletions Example/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
Expand Down
23 changes: 23 additions & 0 deletions Example/Assets.xcassets/Chess.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "dzombak-chess.jpg",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "dzombak-chess-1.jpg",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "dzombak-chess-2.jpg",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
194 changes: 127 additions & 67 deletions Example/NYTViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import "NYTViewController.h"
#import <NYTPhotoViewer/NYTPhotosViewController.h>
#import <NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h>
#import "NYTExamplePhoto.h"

typedef NS_ENUM(NSUInteger, NYTViewControllerPhotoIndex) {
Expand All @@ -23,101 +24,67 @@ typedef NS_ENUM(NSUInteger, NYTViewControllerPhotoIndex) {
@interface NYTViewController () <NYTPhotosViewControllerDelegate>

@property (weak, nonatomic) IBOutlet UIButton *imageButton;
@property (nonatomic) NSArray *photos;
@property (nonatomic) NYTPhotoViewerArrayDataSource *dataSource;

@end

@implementation NYTViewController

- (IBAction)imageButtonTapped:(id)sender {
self.photos = [[self class] newTestPhotos];
NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithPhotos:self.photos initialPhoto:nil delegate:self];
self.dataSource = [self.class newTimesBuildingDataSource];

NYTPhotosViewController *photosViewController = [[NYTPhotosViewController alloc] initWithDataSource:self.dataSource initialPhoto:nil delegate:self];

[self presentViewController:photosViewController animated:YES completion:nil];

[self updateImagesOnPhotosViewController:photosViewController afterDelayWithPhotos:self.photos];
[self updateImagesOnPhotosViewController:photosViewController afterDelayWithDataSource:self.dataSource];
// [self switchDataSourceOnPhotosViewController:photosViewController afterDelayWithDataSource:self.dataSource];
Copy link
Contributor

@craighowarth craighowarth Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps provide a little explanation here. Is your intention that only one of these two lines be uncommented or should we simply uncomment line 41 to see the data source switch after a delay. It works both ways, but it would be nice to know your intention. Or maybe use an if statement to make it more clear:

BOOL switchDataSource = false
[self updateImagesOnPhotosViewController:photosViewController afterDelayWithDataSource:self.dataSource];
if (switchDataSource) {
    [self switchDataSourceOnPhotosViewController:photosViewController afterDelayWithDataSource:self.dataSource];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to demonstrate both the possibility of updating a single photo, and the possibility of completely changing out the data source.

The complete-data-source-change is surprising, though, if you're not expecting it. I wanted that to be optional.

I like the BOOL suggestion and will implement it shortly.

}

// This method simulates previously blank photos loading their images after some time.
- (void)updateImagesOnPhotosViewController:(NYTPhotosViewController *)photosViewController afterDelayWithPhotos:(NSArray *)photos {
- (void)updateImagesOnPhotosViewController:(NYTPhotosViewController *)photosViewController afterDelayWithDataSource:(NYTPhotoViewerArrayDataSource *)dataSource {
if (dataSource != self.dataSource) {
return;
}

CGFloat updateImageDelay = 5.0;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(updateImageDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
for (NYTExamplePhoto *photo in photos) {
for (NYTExamplePhoto *photo in dataSource.photos) {
if (!photo.image && !photo.imageData) {
photo.image = [UIImage imageNamed:@"NYTimesBuilding"];
[photosViewController updateImageForPhoto:photo];
photo.attributedCaptionSummary = [[NSAttributedString alloc] initWithString:@"Photo which previously had a loading spinner (reopen the photo viewer and scroll to here to see it)" attributes:@{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]}];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps change the caption to... "Photo which previously had a loading spinner (to see the spinner, reopen the photo viewer and scroll to this photo)"

[photosViewController updatePhoto:photo];
}
}
});
}

+ (NSArray *)newTestPhotos {
NSMutableArray *photos = [NSMutableArray array];

for (NSUInteger i = 0; i < NYTViewControllerPhotoCount; i++) {
NYTExamplePhoto *photo = [[NYTExamplePhoto alloc] init];

if (i == NYTViewControllerPhotoIndexGif) {
photo.imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"giphy" ofType:@"gif"]];
} else if (i == NYTViewControllerPhotoIndexCustomEverything || i == NYTViewControllerPhotoIndexDefaultLoadingSpinner) {
// no-op, left here for clarity:
photo.image = nil;
} else {
photo.image = [UIImage imageNamed:@"NYTimesBuilding"];
}

if (i == NYTViewControllerPhotoIndexCustomEverything) {
photo.placeholderImage = [UIImage imageNamed:@"NYTimesBuildingPlaceholder"];
}

NSString *caption = @"summary";
switch ((NYTViewControllerPhotoIndex)i) {
case NYTViewControllerPhotoIndexCustomEverything:
caption = @"photo with custom everything";
break;
case NYTViewControllerPhotoIndexLongCaption:
caption = @"photo with long caption. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum maximus laoreet vehicula. Maecenas elit quam, pellentesque at tempor vel, tempus non sem. Vestibulum ut aliquam elit. Vivamus rhoncus sapien turpis, at feugiat augue luctus id. Nulla mi urna, viverra sed augue malesuada, bibendum bibendum massa. Cras urna nibh, lacinia vitae feugiat eu, consectetur a tellus. Morbi venenatis nunc sit amet varius pretium. Duis eget sem nec nulla lobortis finibus. Nullam pulvinar gravida est eget tristique. Curabitur faucibus nisl eu diam ullamcorper, at pharetra eros dictum. Suspendisse nibh urna, ultrices a augue a, euismod mattis felis. Ut varius tortor ac efficitur pellentesque. Mauris sit amet rhoncus dolor. Proin vel porttitor mi. Pellentesque lobortis interdum turpis, vitae tincidunt purus vestibulum vel. Phasellus tincidunt vel mi sit amet congue.";
break;
case NYTViewControllerPhotoIndexDefaultLoadingSpinner:
caption = @"photo with loading spinner";
break;
case NYTViewControllerPhotoIndexNoReferenceView:
caption = @"photo without reference view";
break;
case NYTViewControllerPhotoIndexCustomMaxZoomScale:
caption = @"photo with custom maximum zoom scale";
break;
case NYTViewControllerPhotoIndexGif:
caption = @"animated GIF";
break;
case NYTViewControllerPhotoCount:
// this case statement intentionally left blank.
break;
}

photo.attributedCaptionTitle = [[NSAttributedString alloc] initWithString:@(i + 1).stringValue attributes:@{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]}];
photo.attributedCaptionSummary = [[NSAttributedString alloc] initWithString:caption attributes:@{NSForegroundColorAttributeName: [UIColor lightGrayColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]}];
photo.attributedCaptionCredit = [[NSAttributedString alloc] initWithString:@"NYT Building Photo Credit: Nic Lehoux" attributes:@{NSForegroundColorAttributeName: [UIColor grayColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1]}];

[photos addObject:photo];
- (void)switchDataSourceOnPhotosViewController:(NYTPhotosViewController *)photosViewController afterDelayWithDataSource:(NYTPhotoViewerArrayDataSource *)dataSource {
if (dataSource != self.dataSource) {
return;
}

return photos;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NYTExamplePhoto *photoWithLongCaption = (NYTExamplePhoto *)dataSource[NYTViewControllerPhotoIndexLongCaption];
photosViewController.delegate = nil; // delegate methods in this VC are intended for use with the TimesBuildingDataSource
self.dataSource = [self.class newVariedDataSourceIncludingPhoto:photoWithLongCaption];
photosViewController.dataSource = self.dataSource;
[photosViewController reloadPhotosAnimated:YES];
});
}

#pragma mark - NYTPhotosViewControllerDelegate

- (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController referenceViewForPhoto:(id <NYTPhoto>)photo {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexNoReferenceView]]) {
if ([photo isEqual:self.dataSource[NYTViewControllerPhotoIndexNoReferenceView]]) {
return nil;
}

return self.imageButton;
}

- (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController loadingViewForPhoto:(id <NYTPhoto>)photo {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
if ([photo isEqual:self.dataSource.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
UILabel *loadingLabel = [[UILabel alloc] init];
loadingLabel.text = @"Custom Loading...";
loadingLabel.textColor = [UIColor greenColor];
Expand All @@ -128,7 +95,7 @@ - (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController
}

- (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController captionViewForPhoto:(id <NYTPhoto>)photo {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
if ([photo isEqual:self.dataSource.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
UILabel *label = [[UILabel alloc] init];
label.text = @"Custom Caption View";
label.textColor = [UIColor whiteColor];
Expand All @@ -140,24 +107,24 @@ - (UIView *)photosViewController:(NYTPhotosViewController *)photosViewController
}

- (CGFloat)photosViewController:(NYTPhotosViewController *)photosViewController maximumZoomScaleForPhoto:(id <NYTPhoto>)photo {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexCustomMaxZoomScale]]) {
if ([photo isEqual:self.dataSource.photos[NYTViewControllerPhotoIndexCustomMaxZoomScale]]) {
return 10.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps change the custom scale to 0.5f because 10.0f is hard to tell that it is different when you zoom because the original image is so large to begin with. Using 0.5f it is clear that the scaling is different.

}

return 1.0f;
}

- (NSDictionary *)photosViewController:(NYTPhotosViewController *)photosViewController overlayTitleTextAttributesForPhoto:(id <NYTPhoto>)photo {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
if ([photo isEqual:self.dataSource.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
return @{NSForegroundColorAttributeName: [UIColor grayColor]};
}

return nil;
}

- (NSString *)photosViewController:(NYTPhotosViewController *)photosViewController titleForPhoto:(id<NYTPhoto>)photo atIndex:(NSUInteger)photoIndex totalPhotoCount:(NSUInteger)totalPhotoCount {
if ([photo isEqual:self.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
return [NSString stringWithFormat:@"%lu/%lu", (unsigned long)photoIndex+1, (unsigned long)totalPhotoCount];
- (NSString *)photosViewController:(NYTPhotosViewController *)photosViewController titleForPhoto:(id<NYTPhoto>)photo atIndex:(NSInteger)photoIndex totalPhotoCount:(nullable NSNumber *)totalPhotoCount {
if ([photo isEqual:self.dataSource.photos[NYTViewControllerPhotoIndexCustomEverything]]) {
return [NSString stringWithFormat:@"%lu/%lu", (unsigned long)photoIndex+1, (unsigned long)totalPhotoCount.integerValue];
}

return nil;
Expand All @@ -175,4 +142,97 @@ - (void)photosViewControllerDidDismiss:(NYTPhotosViewController *)photosViewCont
NSLog(@"Did Dismiss Photo Viewer: %@", photosViewController);
}

#pragma mark - Sample Data Sources

+ (NYTPhotoViewerArrayDataSource *)newTimesBuildingDataSource {
NSMutableArray *photos = [NSMutableArray array];

for (NSUInteger i = 0; i < NYTViewControllerPhotoCount; i++) {
NYTExamplePhoto *photo = [NYTExamplePhoto new];

if (i == NYTViewControllerPhotoIndexGif) {
photo.imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"giphy" ofType:@"gif"]];
} else if (i == NYTViewControllerPhotoIndexCustomEverything || i == NYTViewControllerPhotoIndexDefaultLoadingSpinner) {
// no-op, left here for clarity:
photo.image = nil;
} else {
photo.image = [UIImage imageNamed:@"NYTimesBuilding"];
}

if (i == NYTViewControllerPhotoIndexCustomEverything) {
photo.placeholderImage = [UIImage imageNamed:@"NYTimesBuildingPlaceholder"];
}

NSString *caption = @"<photo summary>";
switch ((NYTViewControllerPhotoIndex)i) {
case NYTViewControllerPhotoIndexCustomEverything:
caption = @"Photo with custom everything";
break;
case NYTViewControllerPhotoIndexLongCaption:
caption = @"Photo with long caption.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum maximus laoreet vehicula. Maecenas elit quam, pellentesque at tempor vel, tempus non sem. Vestibulum ut aliquam elit. Vivamus rhoncus sapien turpis, at feugiat augue luctus id. Nulla mi urna, viverra sed augue malesuada, bibendum bibendum massa. Cras urna nibh, lacinia vitae feugiat eu, consectetur a tellus. Morbi venenatis nunc sit amet varius pretium. Duis eget sem nec nulla lobortis finibus. Nullam pulvinar gravida est eget tristique. Curabitur faucibus nisl eu diam ullamcorper, at pharetra eros dictum. Suspendisse nibh urna, ultrices a augue a, euismod mattis felis. Ut varius tortor ac efficitur pellentesque. Mauris sit amet rhoncus dolor. Proin vel porttitor mi. Pellentesque lobortis interdum turpis, vitae tincidunt purus vestibulum vel. Phasellus tincidunt vel mi sit amet congue.";
break;
case NYTViewControllerPhotoIndexDefaultLoadingSpinner:
caption = @"Photo with loading spinner";
break;
case NYTViewControllerPhotoIndexNoReferenceView:
caption = @"Photo without reference view";
break;
case NYTViewControllerPhotoIndexCustomMaxZoomScale:
caption = @"Photo with custom maximum zoom scale";
break;
case NYTViewControllerPhotoIndexGif:
caption = @"Animated GIF";
break;
case NYTViewControllerPhotoCount:
// this case statement intentionally left blank.
break;
}

photo.attributedCaptionTitle = [self attributedTitleFromString:@(i + 1).stringValue];
photo.attributedCaptionSummary = [self attributedSummaryFromString:caption];

if (i != NYTViewControllerPhotoIndexGif) {
photo.attributedCaptionCredit = [self attributedCreditFromString:@"Photo: Nic Lehoux"];
}

[photos addObject:photo];
}

return [NYTPhotoViewerArrayDataSource dataSourceWithPhotos:photos];
}

/// A second set of test photos, to demonstrate reloading the entire data source.
+ (NYTPhotoViewerArrayDataSource *)newVariedDataSourceIncludingPhoto:(NYTExamplePhoto *)photo {
NSMutableArray *photos = [NSMutableArray array];

[photos addObject:({
NYTExamplePhoto *p = [NYTExamplePhoto new];
p.image = [UIImage imageNamed:@"Chess"];
p.attributedCaptionTitle = [self attributedTitleFromString:@"Chess"];
p.attributedCaptionCredit = [self attributedCreditFromString:@"Photo: Chris Dzombak"];
p;
})];

[photos addObject:({
NYTExamplePhoto *p = photo;
photo.attributedCaptionTitle = nil;
p.attributedCaptionSummary = [self attributedSummaryFromString:@"This photo’s caption has changed in the data source."];
p;
})];

return [NYTPhotoViewerArrayDataSource dataSourceWithPhotos:photos];
}

+ (NSAttributedString *)attributedTitleFromString:(NSString *)caption {
return [[NSAttributedString alloc] initWithString:caption attributes:@{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]}];
}

+ (NSAttributedString *)attributedSummaryFromString:(NSString *)summary {
return [[NSAttributedString alloc] initWithString:summary attributes:@{NSForegroundColorAttributeName: [UIColor lightGrayColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleBody]}];
}

+ (NSAttributedString *)attributedCreditFromString:(NSString *)credit {
return [[NSAttributedString alloc] initWithString:credit attributes:@{NSForegroundColorAttributeName: [UIColor grayColor], NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1]}];
}

@end
8 changes: 4 additions & 4 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Copyright (c) 2015-2016 The New York Times Company
Copyright (c) 2015-2017 The New York Times Company

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this library except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand Down
Loading