-
Notifications
You must be signed in to change notification settings - Fork 378
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
Changes from 33 commits
1acd496
66784db
f5e3dc8
4c180d9
d1394c6
e615a09
2652474
577aa89
ae015e2
620540d
207a448
3e0dba9
de002c1
ed8f246
b0a9525
762483b
587c82b
4750578
d3d2323
290ba8c
8f6011a
3dcdbf0
f8ccb16
62d54be
21c3852
bb38825
2f45ab9
b5e3c07
f71d8f4
4f81acb
cf9cfbc
a57f6d2
db4ccb5
bbacf0c
7dc1e0d
d7aa268
c67c605
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
|
||
#import "NYTViewController.h" | ||
#import <NYTPhotoViewer/NYTPhotosViewController.h> | ||
#import <NYTPhotoViewer/NYTPhotoViewerArrayDataSource.h> | ||
#import "NYTExamplePhoto.h" | ||
|
||
typedef NS_ENUM(NSUInteger, NYTViewControllerPhotoIndex) { | ||
|
@@ -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]; | ||
} | ||
|
||
// 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]}]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]; | ||
|
@@ -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]; | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
@@ -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 |
There was a problem hiding this comment.
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:
There was a problem hiding this comment.
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.