completion

This commit is contained in:
2025-08-06 13:49:11 +08:00
commit c2d7317897
684 changed files with 92987 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
//
// ContainerViewController.h
// PhotoShareExtension
//
// Created by Russell on 27/5/15.
//
//
#import <UIKit/UIKit.h>
#import <NixNetwork/NixUpload.h>
#import <SDWebImage/SDImageCache.h>
@interface ContainerViewController : UIViewController <NixUploadDelegate>
@property (nonatomic, strong) NixAPI *api;
@property (nonatomic, strong) NixUpload *uploadHandler;
@property (nonatomic, strong) NSArray *imageErrors;
@property (nonatomic, strong) NSArray *imageUrls;
@property (nonatomic, strong) NSArray *captions;
@property (nonatomic, strong) NSArray *playlistIds;
@property (nonatomic, strong) NSArray *friendUsernames;
@property (nonatomic, strong) NSArray *playlistData;
@property (nonatomic, strong) NSArray *friendData;
@property (nonatomic, strong) NSArray *selectedReceiverIndexPaths;
- (void)verifyServiceAvailable;
- (void)verifyServiceAvailableWithCompletionHandler:(void (^)(BOOL))completionHandler;
- (void)pingWithCompletionHandler:(void (^)(NSError *))completionHandler;
- (void)send;
- (void)cancel;
@end

View File

@@ -0,0 +1,368 @@
//
// ContainerViewController.m
// PhotoShareExtension
//
// Created by Russell on 27/5/15.
//
//
#import "ContainerViewController.h"
#import "ImageCaptionViewController.h"
#import <ImageIO/ImageIO.h>
@interface ContainerViewController ()
@property (assign) ImageCaptionViewController *svc;
- (BOOL)tokenExists;
- (void)handleUploadError:(NSError *)error;
@end
@implementation ContainerViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self performSegueWithIdentifier:@"InitialSegue" sender:nil];
self.api = [[NixAPI alloc] init];
self.uploadHandler = [[NixUpload alloc] initWithAPI:self.api];
self.uploadHandler.delegate = self;
self.uploadHandler.uploadSessionIdentifier = @"com.creedon.Nixplay.uploadsession.appext";
[self.uploadHandler createSession]; // rejoin the session
[self.uploadHandler loadUploadState];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
self.svc = ((UINavigationController *)segue.destinationViewController)
.childViewControllers[0];
self.svc.containerViewController = self;
}
#pragma mark - Actions
- (void)verifyServiceAvailable {
[self verifyServiceAvailableWithCompletionHandler:nil];
}
- (void)verifyServiceAvailableWithCompletionHandler:(void (^)(BOOL))completionHandler {
[self.uploadHandler getCurrentTasks:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
NSLog(@"uploadTasks: %@", uploadTasks);
}];
if (!self.api.apiURL.length) {
[self showAlertViewWithTitle:NSLocalizedString(@"app_never_launched_title", nil)
message:NSLocalizedString(@"app_never_launched_message", nil)
actionWithTitle:NSLocalizedString(@"close", nil)
handler:^(UIAlertAction *action) {
[self cancel];
}];
completionHandler(NO);
return;
}
if (![self tokenExists]) {
[self showAlertViewWithTitle:NSLocalizedString(@"no_account_title", nil)
message:NSLocalizedString(@"no_account_message", nil)
actionWithTitle:NSLocalizedString(@"close", nil)
handler:^(UIAlertAction *action) {
[self cancel];
}];
completionHandler(NO);
} else {
[self pingWithCompletionHandler:^(NSError *error) {
if (error) {
if (error.code == 401) {
[self showAlertViewWithTitle:NSLocalizedString(@"signin_again_title", nil)
message:NSLocalizedString(@"signin_again_message", nil)
actionWithTitle:NSLocalizedString(@"close", nil)
handler:^(UIAlertAction *action) {
[self cancel];
}];
} else {
[self showAlertViewWithTitle:NSLocalizedString(@"server_connection_error_title", nil)
message:NSLocalizedString(@"server_connection_error_message", nil)
actionWithTitle:NSLocalizedString(@"close", nil)
handler:^(UIAlertAction *action) {
[self cancel];
}];
}
completionHandler(NO);
} else {
completionHandler(YES);
}
}];
}
}
- (void)pingWithCompletionHandler:(void (^)(NSError *))completionHandler {
[self.api ping:^(id data, NSURLResponse *response, NSError *error) {
NSLog(@"pingError: %@", error);
completionHandler(error);
}];
}
- (void)send {
NSMutableIndexSet *imageErrorIndexes = [NSMutableIndexSet indexSet];
for (NSUInteger i = 0; i < self.imageErrors.count; i++) {
NSError *error = self.imageErrors[i];
if ([error isKindOfClass:[NSError class]]) {
[imageErrorIndexes addIndex:i];
}
}
NSMutableArray *mutableImageUrls = [NSMutableArray arrayWithArray:self.imageUrls];
NSMutableArray *mutableCaptions = [NSMutableArray arrayWithArray:self.captions];
[mutableImageUrls removeObjectsAtIndexes:imageErrorIndexes];
[mutableCaptions removeObjectsAtIndexes:imageErrorIndexes];
if (mutableImageUrls.count == 0) {
NSError *error = [NSError errorWithDomain:[[NSBundle mainBundle] bundleIdentifier]
code:0
userInfo:@{
NSLocalizedDescriptionKey: NSLocalizedString(@"upload_error_no_files_selected", nil)
}];
[self handleUploadError:error];
return;
}
//process image before upload
//if user choose optimize photo size
if( [self isOptimizeResolution ]){
//optimize photo size
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
SDImageCache *imageCache = [SDImageCache sharedImageCache];
//store images to /tmp
NSString *defaultCachePath = NSTemporaryDirectory();
//nix opt. size
float optimizedDimension = 1820.0f;
NSNumber *maxWidth = [NSNumber numberWithFloat:optimizedDimension];
for (NSUInteger i = 0; i < mutableImageUrls.count; i++) {
@autoreleasepool {
NSURL *imageUrl = mutableImageUrls[i];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil);
// load the image at the desired size
NSDictionary* options = @{
(id)kCGImageSourceShouldAllowFloat: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
(id)kCGImageSourceThumbnailMaxPixelSize: maxWidth
};
CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
//80% jpg quality
NSData * data = UIImageJPEGRepresentation([UIImage imageWithCGImage:imageRef], 0.8f);
//retrieve cache path from Table view SDImageCache
NSURL* cachePathURL = [[NSURL alloc] initWithString:[imageCache cachePathForKey:[imageUrl absoluteString]]] ;
NSURL* newPathURL = [[NSURL alloc] initWithString:
[NSString stringWithFormat:@"%@/%@.JPG",defaultCachePath, [cachePathURL lastPathComponent]]];
NSDictionary *metaoptions = @{(NSString *)kCGImageSourceShouldCache : [NSNumber numberWithBool:NO]};
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)metaoptions);
if (imageProperties) {
NSDictionary *metadata = (__bridge NSDictionary *)imageProperties;
NSMutableDictionary *mutableMetadata = [NSMutableDictionary dictionaryWithDictionary:metadata];
[mutableMetadata setValue:[NSNumber numberWithInt:1] forKey:@"Orientation"];
[self writeData: [self writeMetadataIntoImageData:data metadata:mutableMetadata ] toPath: [newPathURL absoluteString]];
}
else{
//wirte to file
[self writeData: data toPath: [newPathURL absoluteString]];
}
//replace imageURLs object
[mutableImageUrls replaceObjectAtIndex:i withObject:[newPathURL absoluteString] ];
}
}
[self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy]
captions:[mutableCaptions copy]
playlists:self.playlistIds
friends:self.friendUsernames
camera:NO];
});
}
else{
[mutableImageUrls enumerateObjectsUsingBlock:^(NSURL* url, NSUInteger idx, BOOL * _Nonnull stop) {
[mutableImageUrls replaceObjectAtIndex:idx withObject:[url absoluteString]];
}];
[self.uploadHandler uploadWithImageUrls:[mutableImageUrls copy]
captions:[mutableCaptions copy]
playlists:self.playlistIds
friends:self.friendUsernames
camera:NO];
}
}
- (void)cancel {
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:^{
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was cancelled.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Operation was cancelled.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Operation was cancelled.", nil)
};
NSError *error = [NSError errorWithDomain:NSURLErrorDomain
code:-57
userInfo:userInfo];
[self.extensionContext cancelRequestWithError:error];
}];
});
}
#pragma mark - Private
- (BOOL)tokenExists {
return [[self.api getAccessToken] length];
}
- (void)handleUploadError:(NSError *)error {
if (error) {
UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
ReceiverTableViewController *receiverTableVC = (ReceiverTableViewController *)navigationController.topViewController;
if ([receiverTableVC isKindOfClass:[ReceiverTableViewController class]]) {
[receiverTableVC hideActivityIndicator];
receiverTableVC.navigationItem.rightBarButtonItem.enabled = YES;
}
NSString *errorMessage = [error localizedDescription];
switch (error.code) {
case kNixIoFileTooLargeError:
errorMessage = NSLocalizedString(@"upload_error_file_too_large", nil);
break;
}
[self showAlertViewWithTitle:NSLocalizedString(@"upload_error_title", nil)
message:errorMessage
actionWithTitle:NSLocalizedString(@"close", nil)
handler:^(UIAlertAction *action) {
[self cancel];
}];
}
//delete cache image
[self deleteCache];
}
-(BOOL) isOptimizeResolution {
NSUserDefaults *preferences = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.creedon.Nixplay"] ;
NSString *optimizeResolutionKey = @"nixSettings.settings.resolution";
if ([preferences objectForKey:optimizeResolutionKey] == nil)
{
NSLog(@"%@ not exist",optimizeResolutionKey);
return false;
}
else
{
// Get current level
const Boolean optimizeResolution = [preferences boolForKey:optimizeResolutionKey];
NSLog(@"currentLevel %i",optimizeResolution);
return optimizeResolution;
}
return false;
}
- (BOOL)writeData:(NSData*) data toPath:(NSString *)path
{
NSError* errorWrite = nil;
BOOL write = [data writeToFile:path atomically:YES];
if(errorWrite != NULL){
NSLog(@"Failed to write to '%@': %@", path , [errorWrite localizedDescription]);
}
return write;
}
-(void)deleteCache{
NSArray* tmpDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL];
for (NSString *file in tmpDirectory) {
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), file] error:NULL];
}
}
//http://stackoverflow.com/questions/9006759/how-to-write-exif-metadata-to-an-image-not-the-camera-roll-just-a-uiimage-or-j
-(NSData *)writeMetadataIntoImageData:(NSData *)imageData metadata:(NSMutableDictionary *)metadata {
// create an imagesourceref
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) imageData, NULL);
// this is the type of image (e.g., public.jpeg)
CFStringRef UTI = CGImageSourceGetType(source);
// create a new data object and write the new image into it
NSMutableData *dest_data = [NSMutableData data];
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)dest_data, UTI, 1, NULL);
if (!destination) {
NSLog(@"Error: Could not create image destination");
}
// add the image contained in the image source to the destination, overidding the old metadata with our modified metadata
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef) metadata);
BOOL success = NO;
success = CGImageDestinationFinalize(destination);
if (!success) {
NSLog(@"Error: Could not create data from image destination");
}
CFRelease(destination);
CFRelease(source);
return dest_data;
}
#pragma mark - NixUploadDelegate
- (void)didStartUploadWithTasks:(NSArray *)tasks {
[self dismissViewControllerAnimated:YES completion:^{
[self.extensionContext completeRequestReturningItems:@[]
completionHandler:nil];
}];
}
- (void)didUploadImageAtIndex:(NSUInteger)index error:(NSError *)error {
[self handleUploadError:error];
}
- (void)didFinishUploadingWithError:(NSError *)error {
[self handleUploadError:error];
}
-(void) showAlertViewWithTitle:(NSString*)title message:(NSString*)message actionWithTitle:actiontitle handler:(void (^ __nullable)(UIAlertAction *action))handler{
UIAlertController* alert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self cancel];
}];
[alert addAction:defaultAction];
[self.svc presentViewController:alert animated:YES completion:nil];
}
@end

View File

@@ -0,0 +1,32 @@
//
// ShareViewController.h
// PhotoShareExtension
//
// Created by Russell on 28/4/15.
//
//
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "ReceiverTableViewController.h"
#import "ContainerViewController.h"
FOUNDATION_EXPORT NSString *const kNixAppUrlScheme;
FOUNDATION_EXPORT NSString *const kNixAppUrlActionPhotoShare;
typedef NSString *kNixAppUrlAction;
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyImageUrl;
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyCaption;
FOUNDATION_EXPORT NSString *const kNixAppUrlQueryKeyFrameId;
typedef NSString *kNixAppUrlQueryKey;
FOUNDATION_EXPORT NSUInteger const kNixCaptionMaxLength;
FOUNDATION_EXPORT NSUInteger const kNixInputFieldCells;
@interface ImageCaptionViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UITextViewDelegate>
@property (nonatomic, strong) ContainerViewController *containerViewController;
- (IBAction)cancel:(id)sender;
@end

View File

@@ -0,0 +1,399 @@
//
// ShareViewController.m
// PhotoShareExtension
//
// Created by Russell on 27/4/15.
//
//
#import "ImageCaptionViewController.h"
#import <ImageIO/ImageIO.h>
#import <SDWebImage/UIImageView+WebCache.h>
NSString *const kNixAppUrlScheme = @"nixplay";
NSString *const kNixAppUrlActionPhotoShare = @"photo_share";
NSString *const kNixAppUrlQueryKeyImageUrl = @"image_url";
NSString *const kNixAppUrlQueryKeyCaption = @"caption";
NSString *const kNixAppUrlQueryKeyFrameId = @"frame_id";
NSUInteger const kNixCaptionMaxLength = 160;
NSUInteger const kNixInputFieldCells = 0;
@interface ImageCaptionViewController ()
@property (nonatomic, strong) NSMutableArray *imageErrors;
@property (nonatomic, strong) NSArray *imageUrls;
@property (nonatomic, strong) NSMutableArray *captions;
@property (nonatomic, strong) NSNumber *selectedFrameId;
@property (nonatomic, strong) NSString *selectedFrameName;
@property (nonatomic, strong) IBOutlet UITableView *tableView;
@property (nonatomic, strong) NSIndexPath *activeCellIndexPath;
@property (nonatomic, assign) BOOL keyboardShown;
@property (nonatomic, assign) CGFloat keyboardOverlap;
- (void)loadExtensionItems;
- (void)validateImages;
- (void)loadImagesIntoCache;
@end
@interface FrameTableViewCell : UITableViewCell
@end
@interface ImageCaptionTableViewCell : UITableViewCell
@property (nonatomic, strong) NSError *error;
@property (nonatomic, strong) IBOutlet UIImageView *thumbnailView;
//@property (nonatomic, strong) IBOutlet UITextView *textView;
@property (nonatomic, strong) IBOutlet UITextView *textView;
@property (nonatomic, strong) IBOutlet UILabel *errorLabel;
@property (nonatomic, strong) IBOutlet UIActivityIndicatorView *activityIndicatorView;
@end
@implementation ImageCaptionViewController
- (ContainerViewController*)containerViewController {
if (_containerViewController == nil) {
_containerViewController = [[ContainerViewController alloc] init];
}
return _containerViewController;
}
#pragma mark - IB
- (IBAction)cancel:(id)sender {
[self.containerViewController cancel];
}
#pragma mark - UIViewController
- (void)loadView {
[super loadView];
[self loadExtensionItems];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationItem.rightBarButtonItem.enabled = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
[self verifyUser];
}
-(void)appWillBecomeActive:(NSNotification*)note {
NSLog(@"appWillBecomeActive: %@", note);
// re-validate again if the user is active logged-in
[self verifyUser];
}
-(void)appWillResignActive:(NSNotification*)note {
NSLog(@"appWillResignActive: %@", note);
}
-(void)appWillTerminate:(NSNotification*)note {
NSLog(@"appWillTerminate: %@", note);
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
/*
* This works around a quirk on iOS version < 8.3 where the contents of the table do not load (after the VC has been
* loaded once) on subsequent loads until the user scrolls the tableview.
*/
[self.tableView reloadData];
[self loadImagesIntoCache];
// [self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
// dispatch_async(dispatch_get_main_queue(), ^{
// self.navigationItem.rightBarButtonItem.enabled = success;
// });
// }]; // must be in viewWillAppear (doesn't work in viewDidAppear)
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"pushToPlaylistsTableView"]) {
[self.view endEditing:YES];
ReceiverTableViewController *playlistTableVC = (ReceiverTableViewController *)segue.destinationViewController;
playlistTableVC.containerViewController = self.containerViewController;
self.containerViewController.imageErrors = [self.imageErrors copy];
self.containerViewController.imageUrls = [self.imageUrls copy];
self.containerViewController.captions = [self.captions copy];
}
}
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:@"\n"])
{
[textView resignFirstResponder];
return NO;
}
if ([text isEqualToString:@""])
return YES;
return textView.text.length < kNixCaptionMaxLength;
}
- (void)textViewDidEndEditing:(UITextView *)textView {
NSUInteger index = textView.tag;
self.captions[index] = textView.text;
self.activeCellIndexPath = nil;
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
self.activeCellIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.imageUrls count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
NSUInteger imageIndex = indexPath.row;
cell = [tableView dequeueReusableCellWithIdentifier:@"ImageCaptionCell"
forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
NSURL *imageUrl = self.imageUrls[indexPath.row];
ImageCaptionTableViewCell *imageCaptionCell = (ImageCaptionTableViewCell *)cell;
imageCaptionCell.thumbnailView.contentMode = UIViewContentModeScaleAspectFit;
imageCaptionCell.thumbnailView.image = [[SDImageCache sharedImageCache]
imageFromMemoryCacheForKey:[imageUrl absoluteString]];
if (imageCaptionCell.thumbnailView.image) {
[imageCaptionCell.activityIndicatorView stopAnimating];
}
imageCaptionCell.textView.tag = imageIndex;
imageCaptionCell.textView.delegate = self;
NSError *imageError;
if (imageIndex < self.imageErrors.count) {
imageError = self.imageErrors[imageIndex];
}
imageCaptionCell.errorLabel.hidden = YES;
imageCaptionCell.textView.hidden = YES;
if (![imageError isKindOfClass:[NSNull class]]) {
imageCaptionCell.errorLabel.hidden = NO;
imageCaptionCell.errorLabel.text = [imageError localizedDescription];
} else {
imageCaptionCell.textView.hidden = NO;
imageCaptionCell.textView.layer.borderWidth = 0.5f;
imageCaptionCell.textView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
imageCaptionCell.textView.layer.cornerRadius = 2.0f;
imageCaptionCell.textView.textContainer.lineBreakMode = NSLineBreakByWordWrapping;
}
NSString *caption = [self.captions objectAtIndex:imageIndex];
if ([caption isKindOfClass:[NSString class]])
imageCaptionCell.textView.text = caption;
else
imageCaptionCell.textView.text = @"";
return cell;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath{
// @try{
// UIImage *image = [[SDImageCache sharedImageCache]
// imageFromMemoryCacheForKey:[self.imageUrls[indexPath.row] absoluteString]];
// CGFloat height = image.size.height > image.size.width ? image.size.height : 256;
// return height > 0 ? height : 256;
// }
// @catch(NSException * exception){
// NSLog(@"estimatedHeightForRowAtIndexPath Exception %@",exception);
// }
return 300;// UITableViewAutomaticDimension;
}
#pragma mark - Private
#pragma mark App flow
- (void)loadExtensionItems {
NSExtensionItem *extensionItem = self.containerViewController.extensionContext.inputItems.firstObject;
NSArray *inputItems = extensionItem.attachments;
NSMutableArray *mutableImageUrls = [[NSMutableArray alloc] init];
dispatch_group_t group = dispatch_group_create();
// Load images from selection
[inputItems enumerateObjectsUsingBlock:^(NSItemProvider *itemProvider, NSUInteger idx, BOOL *stop) {
dispatch_group_enter(group);
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage
options:nil
completionHandler: ^(NSURL *imageUrl, NSError *error) {
[mutableImageUrls addObject:imageUrl];
dispatch_group_leave(group);
}];
} else {
dispatch_group_leave(group);
}
}];
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
self.imageUrls = [NSArray arrayWithArray:mutableImageUrls];
NSUInteger imagesCount = [self.imageUrls count];
self.imageErrors = [NSMutableArray arrayWithCapacity:imagesCount];
self.captions = [[NSMutableArray alloc] initWithCapacity:imagesCount];
for (NSUInteger i = 0; i < imagesCount; i++) {
[self.captions addObject:[NSNull null]];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self validateImages];
[self.tableView reloadData];
});
});
}
-(BOOL) isOptimizeResolution {
NSUserDefaults *preferences = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.creedon.Nixplay"] ;
NSString *optimizeResolutionKey = @"nixSettings.settings.resolution";
if ([preferences objectForKey:optimizeResolutionKey] == nil)
{
NSLog(@"%@ not exist",optimizeResolutionKey);
return false;
}
else
{
// Get current level
const Boolean optimizeResolution = [preferences boolForKey:optimizeResolutionKey];
NSLog(@"currentLevel %i",optimizeResolution);
return optimizeResolution;
}
return false;
}
- (void)validateImages {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
for (NSUInteger i = 0; i < self.imageUrls.count; i++) {
@autoreleasepool {
NSURL *imageUrl = self.imageUrls[i];
NSDictionary *fileAttrs = [fm attributesOfItemAtPath:[imageUrl path] error:nil];
NSNumber *fileSizeLimit = self.containerViewController.uploadHandler.fileSizeLimit;
NSNumber *fileSize = [fileAttrs objectForKey:NSFileSize];
NSError *fileError;
//https://www.wrike.com/open.htm?id=190326881
//allow proess photo when isOptimizeResolution
if(![self isOptimizeResolution]){
if ([fileSize longLongValue] > [fileSizeLimit longLongValue]) {
fileError = [NSError errorWithDomain:bundleIdentifier
code:kNixIoFileTooLargeError
userInfo:@{
NSLocalizedDescriptionKey: NSLocalizedString(@"selection_error_file_too_large", nil)
}];
}
}
if (fileError) {
[self.imageErrors addObject:fileError];
} else {
[self.imageErrors addObject:[NSNull null]];
}
}
}
}
- (void)loadImagesIntoCache {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
SDImageCache *imageCache = [SDImageCache sharedImageCache];
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGFloat screenScale = [[UIScreen mainScreen] scale];
NSNumber *maxWidth = [NSNumber numberWithFloat:screenBounds.size.width * screenScale * 0.75];
// Create thumbnail and save to cache
for (NSUInteger i = 0; i < self.imageUrls.count; i++) {
@autoreleasepool {
NSURL *imageUrl = self.imageUrls[i];
if (![imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]]) {
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)imageUrl, nil);
NSDictionary *options = @{
(id)kCGImageSourceCreateThumbnailWithTransform: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailFromImageIfAbsent: (id)kCFBooleanTrue,
(id)kCGImageSourceCreateThumbnailFromImageAlways: (id)kCFBooleanTrue,
(id)kCGImageSourceThumbnailMaxPixelSize: (id)maxWidth
};
CGImageRef thumbnailRef = CGImageSourceCreateThumbnailAtIndex(imageSource,
0,
(__bridge CFDictionaryRef)options);
UIImage *scaledImage = [UIImage imageWithCGImage:thumbnailRef];
[imageCache storeImage:scaledImage forKey:[imageUrl absoluteString] completion:^{
}];
}
}
}
// Update visible cells with images
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *visibleIndexPath in visibleIndexPaths) {
@autoreleasepool {
NSUInteger i = visibleIndexPath.row;
NSURL *imageUrl = self.imageUrls[i];
// Set up NSError to insert into cell
dispatch_async(dispatch_get_main_queue(), ^{
ImageCaptionTableViewCell *cell = (ImageCaptionTableViewCell *)[self.tableView
cellForRowAtIndexPath:visibleIndexPath];
if (!cell.thumbnailView.image) {
cell.thumbnailView.alpha = 0.f;
cell.thumbnailView.image = [imageCache imageFromMemoryCacheForKey:[imageUrl absoluteString]];
[UIView animateWithDuration:0.35f animations:^{
cell.thumbnailView.alpha = 1.f;
}];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[visibleIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
[cell.activityIndicatorView stopAnimating];
});
}
}
});
});
}
#pragma mark - User
- (void) verifyUser {
[self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
dispatch_async(dispatch_get_main_queue(), ^{
self.navigationItem.rightBarButtonItem.enabled = success;
});
}]; // must be in viewWillAppear (doesn't work in viewDidAppear)
}
@end
#pragma mark -
@implementation FrameTableViewCell
@end
@implementation ImageCaptionTableViewCell
@end

View File

@@ -0,0 +1,16 @@
//
// SLComposeTableViewController.h
// PhotoShareExtension
//
// Created by Russell on 8/5/15.
//
//
#import <UIKit/UIKit.h>
#import "ContainerViewController.h"
@interface ReceiverTableViewController : UITableViewController
@property (nonatomic, strong) ContainerViewController *containerViewController;
- (void)showActivityIndicator;
- (void)hideActivityIndicator;
@end

View File

@@ -0,0 +1,556 @@
//
// SLComposeTableViewController.m
// PhotoShareExtension
//
// Created by Russell on 8/5/15.
//
//
#import "ReceiverTableViewController.h"
typedef NS_ENUM(NSUInteger, NixTableViewSection) {
kPlaylistHeader,
kPlaylist,
kFriendHeader,
kFriend
};
@interface ReceiverTableViewController ()
@property (nonatomic, strong) NSArray *playlistValues;
@property (nonatomic, strong) NSArray *friendValues;
@property (nonatomic, strong) NSMutableArray *selectedPlaylists;
@property (nonatomic, strong) NSMutableArray *selectedFriends;
- (IBAction)send:(id)sender;
- (void)preparePlaylistValues;
- (void)prepareFriendValues;
- (void)reloadData;
- (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends;
- (void)updateHeaderTitleWithCounts;
- (void)loadAllData:(void (^)(NSError *))completionHandler;
- (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler;
- (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler;
@end
@implementation ReceiverTableViewController
#pragma mark - IB
- (IBAction)send:(id)sender {
self.selectedPlaylists = [[NSMutableArray alloc]
initWithCapacity:self.containerViewController.playlistData.count];
self.selectedFriends = [[NSMutableArray alloc]
initWithCapacity:self.containerViewController.friendData.count];
NSArray *selectedIndexPaths = [self.tableView indexPathsForSelectedRows];
[selectedIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
@autoreleasepool {
switch (indexPath.section) {
case kPlaylist:
{
NSString *playlistId = self.containerViewController.playlistData[indexPath.row][@"id"];
[self.selectedPlaylists addObject:playlistId];
break;
}
case kFriend:
{
NSString *friendUsername = self.containerViewController.friendData[indexPath.row][@"username"];
[self.selectedFriends addObject:friendUsername];
break;
}
default:
break;
}
}
}];
self.containerViewController.playlistIds = [self.selectedPlaylists copy];
self.containerViewController.friendUsernames = [self.selectedFriends copy];
[self showActivityIndicator];
self.navigationItem.rightBarButtonItem.enabled = NO;
self.navigationItem.hidesBackButton = YES;
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
[self.containerViewController send];
}
#pragma mark - Navigation
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.tableView.editing = YES;
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged];
if (!self.containerViewController.playlistData || !self.containerViewController.friendData) {
[self showActivityIndicator];
[self loadAllData:^(NSError *error) {
[self hideActivityIndicator];
if (error) {
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"signin_again_title", nil)
message:NSLocalizedString(@"signin_again_message", nil)
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self.containerViewController.extensionContext cancelRequestWithError:error];
}]];
[self presentViewController:alertController animated:YES completion:nil];
} else {
if (self.playlistValues.count == 0 && self.friendValues.count == 0) {
NSError *err;
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"no_receiver_title", nil)
message:NSLocalizedString(@"no_receiver_message", nil)
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self.containerViewController.extensionContext cancelRequestWithError:err];
}]];
[self presentViewController:alertController animated:YES completion:nil];
} else {
[self.tableView reloadData];
}
}
}];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationItem.rightBarButtonItem.enabled = NO;
NSArray *selectedReceiverIndexPaths = self.containerViewController.selectedReceiverIndexPaths;
[self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) {
if ([selectedReceiverIndexPaths count]) {
self.navigationItem.rightBarButtonItem.enabled = YES;
}
}];
if (self.containerViewController.playlistData && self.containerViewController.friendData) {
[self preparePlaylistValues];
[self prepareFriendValues];
[self.tableView reloadData];
if ([selectedReceiverIndexPaths count]) {
[selectedReceiverIndexPaths
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[self.tableView selectRowAtIndexPath:indexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}];
[self updateHeaderTitleWithCounts];
}
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.friendValues = nil;
self.playlistValues = nil;
self.containerViewController.friendData = nil;
self.containerViewController.playlistData = nil;
self.containerViewController.selectedReceiverIndexPaths = nil; //[self.tableView indexPathsForSelectedRows];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Private
- (void)showActivityIndicator {
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.navigationItem.titleView = activityIndicator;
[activityIndicator startAnimating];
}
- (void)hideActivityIndicator {
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)self.navigationItem.titleView;
if ([activityIndicator isKindOfClass:[UIActivityIndicatorView class]]) {
[activityIndicator stopAnimating];
}
self.navigationItem.titleView = nil;
}
- (void)preparePlaylistValues {
NSMutableArray *playlistValues = [NSMutableArray arrayWithCapacity:self.containerViewController.playlistData.count];
for (NSDictionary *record in self.containerViewController.playlistData) {
@autoreleasepool {
NSString *name = record[@"name"];
[playlistValues addObject:name];
}
}
self.playlistValues = playlistValues;
}
- (void)prepareFriendValues {
NSMutableArray *friendValues = [NSMutableArray arrayWithCapacity:self.containerViewController.friendData.count];
for (NSDictionary *record in self.containerViewController.friendData) {
@autoreleasepool {
NSString *name = record[@"fullName"];
[friendValues addObject:name];
}
}
self.friendValues = friendValues;
}
- (void)reloadData {
NSArray *playlistData = [self.containerViewController.playlistData copy];
NSArray *friendData = [self.containerViewController.friendData copy];
NSMutableArray *selectedPlaylistIds = [[NSMutableArray alloc] initWithCapacity:playlistData.count];
NSMutableArray *selectedFriendUsernames = [[NSMutableArray alloc] initWithCapacity:friendData.count];
[[self.tableView indexPathsForSelectedRows]
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
switch (indexPath.section) {
case kPlaylist:
[selectedPlaylistIds addObject:playlistData[indexPath.row][@"id"]];
break;
case kFriend:
[selectedFriendUsernames addObject:friendData[indexPath.row][@"username"]];
break;
default:
break;
}
}];
[self loadAllData:^(NSError *error) {
[self.refreshControl endRefreshing];
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *freshPlaylistData = [self.containerViewController.playlistData copy];
NSArray *freshFriendData = [self.containerViewController.friendData copy];
[freshPlaylistData enumerateObjectsUsingBlock:^(NSDictionary *playlist, NSUInteger idx, BOOL *stop) {
@autoreleasepool {
if ([selectedPlaylistIds containsObject:playlist[@"id"]]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kPlaylist];
[self.tableView selectRowAtIndexPath:indexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
}
}];
[freshFriendData enumerateObjectsUsingBlock:^(NSDictionary *friend, NSUInteger idx, BOOL *stop) {
@autoreleasepool {
if ([selectedFriendUsernames containsObject:friend[@"username"]]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kFriend];
[self.tableView selectRowAtIndexPath:indexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
}
}];
});
}];
}
- (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends {
NSString *rawString;
if (playlists == 0) {
if (friends == 1) {
rawString = NSLocalizedString(@"navigation_header_n_selected_friend", nil);
} else {
rawString = NSLocalizedString(@"navigation_header_n_selected_friends", nil);
}
}
if (friends == 0) {
if (playlists == 1) {
rawString = NSLocalizedString(@"navigation_header_n_selected_playlist", nil);
} else {
rawString = NSLocalizedString(@"navigation_header_n_selected_playlists", nil);
}
}
if (0 < playlists && 0 < friends) {
rawString = NSLocalizedString(@"navigation_header_n_selected_receivers", nil);
}
if (playlists + friends == 0) {
return NSLocalizedString(@"navigation_header_select_receivers", nil);
}
return [NSString stringWithFormat:rawString, playlists + friends];
}
- (void)updateHeaderTitleWithCounts {
__block NSUInteger playlistCount = 0;
__block NSUInteger friendCount = 0;
[[self.tableView indexPathsForSelectedRows]
enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
switch (indexPath.section) {
case kPlaylist:
playlistCount++;
break;
case kFriend:
friendCount++;
break;
}
}];
NSString *title = [self headerTitleWithPlaylistCounts:playlistCount andFriendCounts:friendCount];
self.navigationItem.title = title;
}
#pragma mark Network
- (void)loadAllData:(void (^)(NSError *))completionHandler {
__block NSError *error = nil;
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_enter(dispatchGroup);
[self loadPlaylistsWithCompletionHandler:^(NSError *playlistsError) {
if (playlistsError)
error = playlistsError;
dispatch_group_leave(dispatchGroup);
}];
dispatch_group_enter(dispatchGroup);
[self loadFriendsWithCompletionHandler:^(NSError *friendsError) {
if (friendsError)
error = friendsError;
dispatch_group_leave(dispatchGroup);
}];
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
completionHandler(error);
});
}
- (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler {
[self.containerViewController.api playlists:^(id data, NSURLResponse *response, NSError *error) {
if (error || !data || [data isKindOfClass:[NSDictionary class]]) {
completionHandler(error);
return;
}
if ([data isKindOfClass:[NSArray class]]) {
NSPredicate *predicateByTitle = [NSPredicate predicateWithFormat:@"SELF.type == %@", @"normal"];
NSArray *playlists = [(NSArray*)data filteredArrayUsingPredicate:predicateByTitle];
self.containerViewController.playlistData = playlists;
[self preparePlaylistValues];
[self.tableView reloadData];
}
completionHandler(nil);
}];
}
- (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler {
[self.containerViewController.api friends:^(NSDictionary *data, NSURLResponse *response, NSError *error) {
if (error || data[@"error"]) {
completionHandler(error);
return;
}
NSString *predString = [NSString stringWithFormat:@"(status='%@')", @"success"];
NSPredicate *pred = [NSPredicate predicateWithFormat:predString];
NSArray *friendsArr = [data[@"friends"] filteredArrayUsingPredicate:pred];
self.containerViewController.friendData = friendsArr;
[self prepareFriendValues];
[self.tableView reloadData];
completionHandler(nil);
}];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 4;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
switch (section) {
case kPlaylistHeader:
return self.playlistValues.count ? NSLocalizedString(@"table_header_playlist", nil) : nil;
case kFriendHeader:
return self.friendValues.count ? NSLocalizedString(@"table_header_friend", nil) : nil;
default:
return nil;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
switch (section) {
case kPlaylist:
return self.playlistValues.count;
case kFriend:
return self.friendValues.count;
case kPlaylistHeader:
return self.playlistValues.count ? 1 : 0;
case kFriendHeader:
return self.friendValues.count ? 1 : 0;
default:
return 0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = @"SLComposeTableViewControllerCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
if ([[tableView indexPathsForSelectedRows] containsObject:indexPath]) {
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
switch (indexPath.section) {
case kPlaylist:
cell.textLabel.text = self.playlistValues[indexPath.row];
break;
case kFriend:
cell.textLabel.text = self.friendValues[indexPath.row];
break;
case kPlaylistHeader:
cell.textLabel.text = NSLocalizedString(@"table_select_all_playlists", nil);
break;
case kFriendHeader:
cell.textLabel.text = NSLocalizedString(@"table_select_all_friends", nil);
break;
default:
break;
}
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case kPlaylistHeader:
{
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) {
NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist];
[tableView selectRowAtIndexPath:playlistIndexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
break;
}
case kFriendHeader:
{
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) {
NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend];
[tableView selectRowAtIndexPath:friendIndexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
break;
}
default:
break;
}
self.navigationItem.rightBarButtonItem.enabled = YES;
[self updateHeaderTitleWithCounts];
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.section) {
case kPlaylistHeader:
{
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) {
NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist];
[tableView deselectRowAtIndexPath:playlistIndexPath
animated:NO];
}
break;
}
case kFriendHeader:
{
for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) {
NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend];
[tableView deselectRowAtIndexPath:friendIndexPath
animated:NO];
}
break;
}
case kPlaylist:
{
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kPlaylistHeader];
[tableView deselectRowAtIndexPath:headerIndexPath
animated:NO];
break;
}
case kFriend:
{
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kFriendHeader];
[tableView deselectRowAtIndexPath:headerIndexPath
animated:NO];
break;
}
default:
break;
}
if (![tableView indexPathsForSelectedRows]) {
self.navigationItem.rightBarButtonItem.enabled = NO;
}
[self updateHeaderTitleWithCounts];
}
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end