Update UIProgressView in UICollectionViewCell - ios

I'm trying to build an app that will display a collection of magazines that can be downloaded and then read. What I want to happen is if the magazine isn't downloaded, a download will commence when a cell is tapped and a progressview will appear in the corresponding cell showing the download progress. I have subclassed UICollectionViewCelland added a UIProgressView property. So far, I've been able to check whether the file is downloaded and if it isn't, download it, and if it is, just open it using a PDF library called VFR Library. I can also update a progress view placed outside of the UICollectionViewCell but I can't update one placed within the UICollectionViewCell. Unfortunately, most of my logic for downloading and updating the progressview is within didSelectItemAtIndexPath:. I know this isn't elegant but my experience level isn't high enough that I can effectively develop my own classes to work together seamlessly. But once I figure this out I'll work on refining the code and abstracting things away a bit. Anyway, here's what I'm doing in didSelectItemAtIndexPath:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
IssueCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
self.navBarLabel.hidden = YES;
self.mainProgressView.hidden = NO;
self.view.userInteractionEnabled = NO;
//self.activityIndicator.hidden = NO;
[self.activityIndicator startAnimating];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"issue%ld", (long)indexPath.row]]stringByAppendingPathExtension:#"pdf"];
if ([[NSFileManager defaultManager]fileExistsAtPath:path])
{
ReaderDocument *document = [ReaderDocument withDocumentFilePath:path password:nil];
if (document != nil)
{
//opens PDF for viewing
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self;
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:nil];
self.mainProgressView.hidden = YES;
self.navBarLabel.hidden = NO;
self.view.userInteractionEnabled = YES;
//self.activityIndicator.hidden = YES;
[self.activityIndicator stopAnimating];
}
}
else
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlArray[indexPath.row]]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
ReaderDocument *document = [ReaderDocument withDocumentFilePath:path password:nil];
if (document != nil)
{
//opens PDF for viewing
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self;
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:nil];
self.mainProgressView.hidden = YES;
self.navBarLabel.hidden = NO;
//self.activityIndicator.hidden = YES;
self.view.userInteractionEnabled = YES;
[self.activityIndicator stopAnimating];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Download Failed" message:#"There was a problem downloading this issue" delegate:self cancelButtonTitle:#"OK" otherButtonTitles: nil];
[alertView show];
self.mainProgressView.hidden = YES;
self.navBarLabel.hidden = NO;
[self.activityIndicator stopAnimating];
//self.activityIndicator.hidden = YES;
self.view.userInteractionEnabled = YES;
NSLog(#"Error: %#", error);
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float percentDone =((float)((int)totalBytesWritten) / (float)((int)totalBytesExpectedToWrite));
self.mainProgressView.progress = percentDone;
[cell.progressView setProgress:percentDone];
[self.collectionView reloadData];
// [cell.progressView performSelectorOnMainThread:#selector(loadingProgress:) withObject:[NSNumber numberWithFloat:percentDone] waitUntilDone:NO];
NSLog(#"Sent %lld of %lld bytes, %#", totalBytesWritten, totalBytesExpectedToWrite, path);
}];
[operation start];
}
}
So is there any way that I can update the progress view within a corresponding cell within this method or is there something else I need to do to get it to work? Thanks for any help you can give.

Well, I've finally solved my own problem. Apparently the problem was due to not fully understanding how to reference a particular item in a UICollectionView. All I had to do was create a method that actually did this and then call it in the "setDownloadProgressBlock:" method above. The method I wrote for this is:
- (void)setProgressAtIndex:(NSInteger)index withProgress:(float)progress
{
IssueCell *cell = (IssueCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
[cell.progressView setProgress:0.0];
[cell.progressView setProgress:progress];
}
The important line in this method is the first one where I grab a reference to the desired cell. This one actually grabs the cell that was tapped, whereas before I guess I was just referring to all the cells in general? I'm not too sure, but I would definitely appreciate it if someone had a better explanation.
This method was then called like so:
[self setProgressAtIndex:indexPath.row withProgress:percentDone];
Again, I did this inside the block in "setDownloadProgressBlock:".
Hopefully this helps someone else in the future!

Update your progress view on your main thread. Try using dispatch_sync(dispatch_get_main_queue()).

Related

iOS - NSURLSession not working the second time

I have a UIView that contains a progress bar. What I want to do is simple, I have a button, user clicks that button, app downloads file and show progress in progress bar. I am able to do this when the user clicks the download button the first time. But when the user clicks the second time to download again, NSURLSession delegates are not called.
My UIView .m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self configure];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self configure];
}
return self;
}
-(void)configure
{
[self createSpinner];
[self createProgressBar];
NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
self.docDirectoryURL = [URLs objectAtIndex:0];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.tinkytickles"];
sessionConfiguration.HTTPMaximumConnectionsPerHost = 1;
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
}
-(void)createSpinner
{
[self setBackgroundColor:[UIColor colorWithWhite:1.0f alpha:0.5f]];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self addSubview:spinner];
[spinner setColor:original_new_dark_grey];
[spinner setUserInteractionEnabled:NO];
[spinner setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
[spinner setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
[spinner startAnimating];
}
-(void)createProgressBar
{
self.progressBar = [[TYMProgressBarView alloc] initWithFrame:CGRectMake(0, 0, 280, 15)];
[self.progressBar setBarBackgroundColor:[UIColor whiteColor]];
[self.progressBar setBarBorderColor:original_new_dark_grey];
[self.progressBar setBarFillColor:original_new_dark_grey];
[self.progressBar setBarBorderWidth:1.0f];
[self addSubview:self.progressBar];
[self.progressBar setCenter:CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2)];
[self.progressBar setHidden:YES];
self.label = [[UILabel alloc] initWithFrame:CGRectMake(self.progressBar.frame.origin.x, self.progressBar.frame.origin.y - 30, self.progressBar.frame.size.width, 25)];
[self.label setText:NSLocalizedString(locDownloading, nil)];
[self.label setTextAlignment:NSTextAlignmentCenter];
[self.label setTextColor:original_new_dark_grey];
[self.label setFont:quicksand_14];
[self addSubview:self.label];
[self.label setHidden:YES];
}
-(void)showProgressBarWithProgress:(CGFloat)progress withText:(NSString *)text
{
[spinner setHidden:YES];
[self.label setText:[NSString stringWithFormat:NSLocalizedString(locDownloadingAt, nil), text]];
[self.label setHidden:NO];
[self.progressBar setHidden:NO];
[self.progressBar setProgress:progress];
}
-(void)stopAnimating
{
[spinner stopAnimating];
}
-(void)startDownloadingURL:(PromoterDownloadInfo *)downloadInfo
{
info = downloadInfo;
if (!info.isDownloading)
{
if (info.taskIdentifier == -1)
{
info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
info.taskIdentifier = info.downloadTask.taskIdentifier;
[info.downloadTask resume];
}
else
{
info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
[info.downloadTask resume];
info.taskIdentifier = info.downloadTask.taskIdentifier;
}
}
else
{
[info.downloadTask cancelByProducingResumeData:^(NSData *resumeData) {
if (resumeData != nil) {
info.taskResumeData = [[NSData alloc] initWithData:resumeData];
}
}];
}
info.isDownloading = !info.isDownloading;
}
-(void)stopDownload:(PromoterDownloadInfo *)downloadInfo
{
if (!info.isDownloading)
{
if (info.taskIdentifier == -1)
{
info.downloadTask = [self.session downloadTaskWithURL:[NSURL URLWithString:info.downloadSource]];
}
else
{
info.downloadTask = [self.session downloadTaskWithResumeData:info.taskResumeData];
}
info.taskIdentifier = info.downloadTask.taskIdentifier;
[info.downloadTask resume];
info.isDownloading = YES;
}
[self stopAnimating];
[self removeFromSuperview];
}
#pragma mark - NSURLSession Delegate method implementation
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSError *error;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *destinationFilename = downloadTask.originalRequest.URL.lastPathComponent;
NSURL *destinationURL = [self.docDirectoryURL URLByAppendingPathComponent:destinationFilename];
if ([fileManager fileExistsAtPath:[destinationURL path]]) {
[fileManager removeItemAtURL:destinationURL error:nil];
}
BOOL success = [fileManager copyItemAtURL:location
toURL:destinationURL
error:&error];
if (success) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self stopAnimating];
[self removeFromSuperview];
}];
}
else
{
NSLog(#"Unable to copy temp file. Error: %#", [error localizedDescription]);
}
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if (error != nil) {
NSLog(#"Download completed with error: %#", [error localizedDescription]);
}
else{
NSLog(#"Download finished successfully.");
}
}
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) {
NSLog(#"Unknown transfer size");
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
info.downloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
[self showProgressBarWithProgress:info.downloadProgress withText:info.fileTitle];
});
}
}
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
// Check if all download tasks have been finished.
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([downloadTasks count] == 0) {
if (appDelegate.backgroundTransferCompletionHandler != nil) {
// Copy locally the completion handler.
void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
// Make nil the backgroundTransferCompletionHandler.
appDelegate.backgroundTransferCompletionHandler = nil;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// Call the completion handler to tell the system that there are no other background transfers.
completionHandler();
// Show a local notification when all downloads are over.
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertBody = NSLocalizedString(locDownloadComplete, nil);
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}];
}
}
}];
}
I use this UIView like this:
PromoterDownloadInfo *info = [[PromoterDownloadInfo alloc] initWithFileTitle:self.title andDownloadSource:#"https://www.mywebsite.com/file.zip"];
PromotersDownloadView *downloadView = [[PromotersDownloadView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[self.navigationController.view addSubview:downloadView];
[downloadView startDownloadingURL:info];
The first time I clicked the download button it works great. The second time NSURLSession only didCompleteWithError method gets called. Here is what I get from log the second time:
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!
2016-05-12 00:50:50.614 APP[32990:1230386] Download finished successfully.
What am I doing wrong? I tried to create NSURLSessionConfiguration only once but this way no delegate method gets called. What should I do?
You said:
The first time I clicked the download button it works great. ... Here is what I get from log the second time:
2016-05-12 00:50:47.440 APP[32990:1230071] A background URLSession with identifier com.app already exists!<br />
That error is pointing out that you want to instantiate only one background NSURLSession for a given identifier (and you generally only need/want a single background session). If you were going to instantiate multiple ones, you'd give them unique identifiers, but handling background sessions is complicated enough without unnecessarily having multiple sessions. I'd suggest that you only want a single background session.
You said:
I tried to create NSURLSessionConfiguration only once but this way no delegate method gets called.
Yes, you should have one session configuration. And, just as importantly, only one background session object.
I suspect that there's an issue with your delegate object not being able to keep track of which view it should be updating. Or perhaps you lost reference to your session object and your reference was nil. It could be a couple of different things, and it's hard to know without seeing how you did this.
I'd suggest moving this session configuration code out of the view, and have some shared instance that you can reference anywhere (e.g. a singleton works well, so you can instantiate it from wherever it's first needed, whether from a view or from the app delegate's handleEventsForBackgroundURLSession method).
The only challenge then is how to keep track of which views are keeping track of which network requests. Do you want to have a single view that will keep track of all incomplete requests, regardless of when this view is instantiated? If so, you can use NSNotificationCenter notifications (that way, any view that wants to be notified of progress updates can just observe your custom notification). Or does a given view only care about requests that you initiated from that view?In that case, you might maintain dictionary that maps taskIdentifier values to which view or object needs to know about the status updates (what way you can have your session object keep track of which views care about which tasks). It just depends upon your app's requirements.

Video saving and display in the iOS app.

I'm developing an app where user can record video and save it in the library/or custom album. I'm able to create the custom album and record the video and save it in the default photo library i.e., camera roll.. But i'm unable to save it to the custom album. and other thing is the saved videos in the album must be showed in the collection view of the app i.e., like the gallery view.. so that user can able to click on the videos for the play.. it might be in the grid view/ collection view or might be in the table view where the UIImage should should display the videos saved in the album..
Here's code section which i used in the app development for creating the custom album.
ALAssetsLibrary* libraryFolder = [[ALAssetsLibrary alloc] init];
[libraryFolder addAssetsGroupAlbumWithName:#"HEP" resultBlock:^(ALAssetsGroup *group)
{
NSLog(#"Adding Folder:'My Album', success: %s", group.editable ? "Success" : "Already created: Not Success");
} failureBlock:^(NSError *error)
{
NSLog(#"Error: Adding on Folder");
}];
For recording the video in the app.
- (IBAction)StartRecord:(id)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = [[NSArray alloc] initWithObjects:(NSString *)kUTTypeMovie, nil];
[self presentViewController:picker animated:YES completion:NULL];
}
For saving it in the album i.e., default photo album not the custom album i created.
-(BOOL)startMediaBrowserFromViewController:(UIViewController*)controller usingDelegate:(id )delegate {
// 1 - Validations
if (([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum] == NO)
|| (delegate == nil)
|| (controller == nil)) {
return NO;
}
// 2 - Get image picker
UIImagePickerController *mediaUI = [[UIImagePickerController alloc] init];
mediaUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
mediaUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
mediaUI.allowsEditing = YES;
mediaUI.delegate = delegate;
// 3 - Display image picker
[controller presentModalViewController:mediaUI animated:YES];
return YES;
}
Since i'm beginner in iOS still learning.. I'm searching since for a month now. So please help me.
First create your album folder with below code & make sure this code paste in AppDelegate.m file didFinishLaunchingWithOptions method :
ALAssetsLibrary* libraryFolderSozialConnectVideo = [[ALAssetsLibrary alloc] init];
[libraryFolderSozialConnectVideo addAssetsGroupAlbumWithName:#"HEP" resultBlock:^(ALAssetsGroup *group)
{
NSLog(#"HEP Folder Created");
} failureBlock:^(NSError *error) {
NSLog(#"Error: Adding on Folder");
}];
Now, use below function for download your video in HEP folder :
- (void)downloadVideo:(NSURL *)videoURL {
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"Downloading Started");
NSData *urlData = [NSData dataWithContentsOfURL:videoURL];
if (urlData) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory,#"thefile.mp4"];
[urlData writeToFile:filePath atomically:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[self.library saveVideo:[NSURL fileURLWithPath:filePath] toAlbum:#"HEP" completion:^(NSURL *assetURL, NSError *error)
{
NSLog(#"Success downloaded");
} failure:^(NSError *error) {
NSLog(#"Error : %#", [error localizedDescription]);
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[self.navigationController popViewControllerAnimated:YES];
}];
});
}
}); }
for videoURL in your didFinishPickingMediaWithInfo method call above function with below code :
self.library = [[ALAssetsLibrary alloc] init];
[self downloadVideo:[info valueForKey:UIImagePickerControllerMediaURL]];
I hope this works . . .

Initialize UITableview with Contents from XML but can't show progress with UIAlertview

I am writing an app, which is getting data from the net using XML. It is a master-detail-app which is fetching data for the master-table and after selecting one item from the master-Table it fills the data for the detailview(s) using another network-access.
I would like to present an alert in order to show the user that the app is busy accessing the net or busy calculating. So I would present the view from the UIAlertController before the calculation / network access starts and dismiss the view when the activity has completed
Problem is: I don't know where to put this call to show / dismiss the UIAlertcontroller view.
Putting the activity code into ViewWillAppear shows and dismisses the alertview BEFORE the network-access... Putting everything into ViewDidLoad seems not the way to go.
- (void) viewWillAppear:(BOOL)animated
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Working"
message:#"Working on it"
preferredStyle:UIAlertControllerStyleAlert];
self.objects = [[NSMutableArray alloc] init];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"Accounts"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
NSURL *url;
if ([self getSession])
{
NSMutableString *URLstring = [NSMutableString stringWithString:#"https://XXXXXXXXXXXXXXX.xml?session="];
[self presentViewController:alert animated:YES completion:nil];
[URLstring appendString:[[DataStore getData]SessionString]];
url = [NSURL URLWithString:URLstring];
self.myXXXXXXParserAlleKontenXMLDelegate = [[KontenParserDelegate alloc] init];
self.xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
self.xmlParser.delegate = self.myfxbookParserAlleKontenXMLDelegate;
if ([self.xmlParser parse])
{
[self.objects removeAllObjects];
for(int i=0; i< [self.XXXXXXXXXrAlleKontenXMLDelegate.allAccounts count];i++)
{
[self.objects addObject : [self.myfxbookParserAlleKontenXMLDelegate.allAccounts objectAtIndex:i]];
}
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey: #"name" ascending: YES];
[self.objects sortUsingDescriptors:#[sort]];
[self.tableView reloadData];
[self dismissViewControllerAnimated:YES completion:nil];
// Do any additional setup after loading the view, typically from a nib.
}
}
}
Added After some discussions I added some new framework "SVProgressHUD" and entered the following code into "ViewWillAppear" where [self parserstuff] contains all the XML parsing..
[SVProgressHUD show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self parserstuff];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
});
This leaves me with a HUD-Display showing up while parsing XML but the resulting Tableview does not show data but is empty... After switching to DISPATCH_SYNC it works.
Solution would be
1) Put code in ViewDidLoad
2) encapsulate Code between "SVProgressHUD show" and "dismiss" with dispatch_async
as in
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD show];
[self parserstuff];
[self fillComparatorParameters:nil];
[self.tableView reloadData];
[SVProgressHUD dismiss];
// }];
});

Terminating App - NSInvalidArgumentException

Crashing when running Parse Anypic code.
-(void)sendCommentButton:(id) sender {
NSString *trimmedComment = [commentTextView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (trimmedComment.length != 0 && [self.photo objectForKey:kPAPPhotoUserKey]) {
PFObject *comment = [PFObject objectWithClassName:kPAPActivityClassKey];
[comment setObject:trimmedComment forKey:kPAPActivityContentKey]; // Set comment text
[comment setObject:[self.photo objectForKey:kPAPPhotoUserKey] forKey:kPAPActivityToUserKey]; // Set toUser
[comment setObject:[PFUser currentUser] forKey:kPAPActivityFromUserKey]; // Set fromUser
[comment setObject:kPAPActivityTypeComment forKey:kPAPActivityTypeKey];
[comment setObject:self.photo forKey:kPAPActivityPhotoKey];
[comment setObject:self.photoFile forKey:#"attachmentFile"];
PFACL *ACL = [PFACL ACLWithUser:[PFUser currentUser]];
[ACL setPublicReadAccess:YES];
[ACL setWriteAccess:YES forUser:[self.photo objectForKey:kPAPPhotoUserKey]];
comment.ACL = ACL;
[[PAPCache sharedCache] incrementCommentCountForPhoto:self.photo];
// Show HUD view
[MBProgressHUD showHUDAddedTo:self.view.superview animated:YES];
// If more than 5 seconds pass since we post a comment, stop waiting for the server to respond
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(handleCommentTimeout:) userInfo:#{#"comment": comment} repeats:NO];
[comment saveEventually:^(BOOL succeeded, NSError *error) {
[timer invalidate];
if (error && error.code == kPFErrorObjectNotFound) {
[[PAPCache sharedCache] decrementCommentCountForPhoto:self.photo];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Could not post comment", nil) message:NSLocalizedString(#"This photo is no longer available", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
[self.navigationController popViewControllerAnimated:YES];
}
[[NSNotificationCenter defaultCenter] postNotificationName:PAPPhotoDetailsViewControllerUserCommentedOnPhotoNotification object:self.photo userInfo:#{#"comments": #(self.objects.count + 1)}];
[MBProgressHUD hideHUDForView:self.view.superview animated:YES];
[self loadObjects];
}];
}
[self.commentTextView setText:#""];
[self.commentTextView resignFirstResponder];
if (self.photoFile != nil) {
self.photoFile = nil;
}
}
Picking an Image
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
NSLog(#"Hello");
UIImage *img = [info objectForKey:UIImagePickerControllerOriginalImage];
// JPEG to decrease file size and enable faster uploads & downloads
NSData *imageData = UIImageJPEGRepresentation(img, 0.8f);
self.photoFile = [PFFile fileWithData:imageData];
// Request a background execution task to allow us to finish uploading the photo even if the app is backgrounded
self.fileUploadBackgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.fileUploadBackgroundTaskId];
}];
NSLog(#"Requested background expiration task with id %lu for Anypic photo upload", (unsigned long)self.fileUploadBackgroundTaskId);
[self.photoFile saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"Photo uploaded successfully");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Photo Uploaded"
message:#"successfully"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
[[UIApplication sharedApplication] endBackgroundTask:self.fileUploadBackgroundTaskId];
}
}];
[self dismissViewControllerAnimated:YES completion:nil];
}
Question: Why is it crashing? I believe this is the code that is crashing it. What I did to crash it was not add an attachmentFile and just put a comment.
If you need more code or need any clarifications please comment down below
PFObject *comment = [PFObject objectWithClassName:kPAPActivityClassKey];
[comment setObject:trimmedComment forKey:kPAPActivityContentKey]; // Set comment text
[comment setObject:[self.photo objectForKey:kPAPPhotoUserKey] forKey:kPAPActivityToUserKey]; // Set toUser
[comment setObject:[PFUser currentUser] forKey:kPAPActivityFromUserKey]; // Set fromUser
[comment setObject:kPAPActivityTypeComment forKey:kPAPActivityTypeKey];
[comment setObject:self.photo forKey:kPAPActivityPhotoKey];
[comment setObject:self.photoFile forKey:#"attachmentFile"];
In one of these lines, you're passing nil to the first parameter of setObject:forKey. Add an Exception Breakpoint (in the breakpoints tab of left sidebar) and check which line it breaks on.

Console warning: Attempt to dismiss from view controller while a presentation or dismiss is in progress

"Warning: Attempt to dismiss from view controller while a presentation or dismiss is in progress!"
I'm trying to have my app go to a loading screen upon selecting a picture to upload. This works by, upon selecting a picture, removing the UIImagePickerController scene, adding the Loading scene, and once the upload is complete, removing the Loading scene.
-(void)uploadMessage{
[self dismissViewControllerAnimated:NO completion:nil];
LoadingViewController *loadView = [[LoadingViewController alloc]initWithNibName:#"LoadView" bundle:nil];
[self presentViewController:loadView animated:NO completion:^{
NSData *fileData;
NSString *fileName;
NSLog(#"Image");
fileData = UIImagePNGRepresentation(self.image);
fileName = #"image.png";
PFFile *file = [PFFile fileWithName:fileName data:fileData];
[self.game setObject:file forKey:#"picture"];
[self.game saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(succeeded){
[self removeAndChangeButtons];
[self dismissViewControllerAnimated:NO completion:nil];
}
}];
}];
}
You can add a function using MBProgressHUD.
- (void) showMessage:(NSString*)message withTitle:(NSString*)title onView:(UIView*)view removeAfter:(NSTimeInterval)delay{
dispatch_async(dispatch_get_main_queue(), ^{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
// Configure for text only and offset down
hud.mode = MBProgressHUDModeText;
hud.labelText = title;
hud.detailsLabelText = message;
hud.margin = 10.f;
hud.yOffset = 0.0f;
hud.removeFromSuperViewOnHide = YES;
[hud hide:YES afterDelay:delay];
});
}
Then in your code when you start the background task, call showMessage and then the upload is done, remove it.
You are dismissing the ViewController twice in your method if it is successful. Obviously, this can't be done. I don't know what exactly what you're trying to achieve, but I think removing the first [self dismissViewControllerAnimated:NO completion:nil] would do the trick.

Resources