Synchronize operations inside a for loop - objective-c

I'm using the QBImagePickerControllerlibrary to select more than one image in the provided Image Picker view.
I want to let the user upload more than a single image to the "main" server, with a UIImageView displaying what is uploaded at the moment and a UIProgressView.
Since
- (void)imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets
give me an array of ALAssets containig all the images the user selected, I iterate through it this way:
NSMutableArray *IDs = [[NSMutableArray alloc] init];
if([assets count] == 1) {
// single image, no prob.
} else {
for(ALAsset *a in assets) {
//set the image to the one which is uploaded right now
//upload it through a library and tell about status with NSProgress
//write the result into IDs
}
}
My problem is that if the user select two images, the operations appears to be running at the same time, so the UIProgressView is uploaded in what seems a "first come, first served" scenario, while the UIImageViewshows only the last image selected and IDs seems empty.
Is this behaviour due to the method I use to iterate through the array?
What can I do to make each operation mutually exclusive?

Related

Removing a SKNode content and recreating it cause EXC_BAD_ACCESS

'Cause of the difficulties I found combining UITableView and SpriteKit Framework I decided to develop a custom SKTableView (provided with full scrolling system). Everything works really fine but I have serious problem understanding an error I get during the update process.
My SKTableView shows the user a list of items in alphabetical order. Due to the tricky algorithm I should have create in order to update this list (add a cell in a defined position, refresh the headers and scroll down all of the items), I decided to simply update the database and reload the list (by removing existing content and recreate it) while showing asynchrony a notification to the user.
What is happen is the application to success reload the list once, once again, then after some random updates ... here comes the chaos
Issues
The application crashes giving me EXC_BAD_ACCESS during the removeAllChildren methods.
The application crashes with the same error during a for statement (???) or during other statements
The application crashes giving me jet_context::set_fragment_texture... error.
Debug:
The console window doesn't show any kind of information.
I can't monitoring the application using 'Allocation' or 'Zombies' instruments because it critically slow down the application. It takes more than 2 minutes to even load the SKTableView during the init method.
Code
Interface
/** Container for all the table items. */
#property (nonatomic, readonly) SKNode *nodeContainer;
Initialization
- (instancetype)init
{
if (self = [super init]) {
...
Create the style of the SKNode
...
// Container initialization
_nodeContainer = [[SKNode alloc] init];
_nodeContainer.position = CGPointZero;
_nodeContainer.zPosition = 0;
[self addChild:_nodeContainer];
// Generate the content
[self generateContent];
}
return self;
}
Populate the table
- (void)generateContent
{
// Checks if the node exists, if it does, remove all of its children
// Note: used for the recreation of the list
if (_nodeContainer != nil)
[_nodeContainer removeAllChildren];
...
Initializes several SKSpriteNode and SKLabelNode
...
[_nodeContainer addChild:imgItem];
[_nodeContainer addChild:labelItem];
}
Reload the content
...
Update the database
...
[self showProgressNotification];
// Reload the content showing a notification
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Reload the content
[self generateContent];
dispatch_async(dispatch_get_main_queue(), ^{
[self hideProgressNotification];
});
});
I really cannot figure it out what's happening. The code crashes overtime in a different line without a logic. I mean, is this the right way to remove and recreate items? Any advise?

UICollectionViewLayout supplementary view duplicate on delete

I have been tinkering with a UICollectionViewLayout for a few days now, and seem to be stuck on getting a Supplementary view to delete with animation using the finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:
I have finally made some headway and discovered this issue. Initially it looked as if it was not animating, because i was not getting a fading animation. so i added a transform on it to get it to rotate... and discovered a supplementary view is in fact being animated, but another one just sits there. until its finished and the unceremoniously disappears not being affected by being told to disappear at all. So to sum up it looks like an exact copy is made and that is animated out for finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath: and the original just sits there unmoving and animating, then both disappear.
This copy or whatever it is does not disappear either until i scroll the view.
Does anyone have any insight into this issue?
Update
So after some tweaking, experimentation and many hypothesis later as to what was causing the issue, it turns out that the culprit was this method
- (NSArray *)indexPathsToDeleteForSupplementaryViewOfKind:(NSString *)kind
{
NSMutableArray *deletedIndexPaths = [NSMutableArray new];
[self.updatedItems enumerateObjectsUsingBlock:^(UICollectionViewUpdateItem *updateItem, NSUInteger idx, BOOL * _Nonnull stop)
{
switch (updateItem.updateAction)
{
case UICollectionUpdateActionInsert:
{
if (_unReadHeaderIndexPath != nil && [self.layoutInformation[ChatroomLayoutElementKindUnreadHeader] count] == 0)
{
[deletedIndexPaths addObject:_unReadHeaderIndexPath];
_unReadHeaderIndexPath = nil;
}
}
break;
case UICollectionUpdateActionDelete:
{
[deletedIndexPaths addObject:updateItem.indexPathBeforeUpdate];
}
break;
default:
break;
}
}];
return deletedIndexPaths;
}
The part to focus on is here
if (_unReadHeaderIndexPath != nil && [self.layoutInformation[ChatroomLayoutElementKindUnreadHeader] count] == 0)
{
[deletedIndexPaths addObject:_unReadHeaderIndexPath];
_unReadHeaderIndexPath = nil;
}
Particularly the _unReadHeaderIndexPath = nil;
What this is doing is making a supplementary view disappear which indicates unread messages when the user inserts any text and then setting it to nil so it is not added multiple times, the logic is sound however the method in which is employed seems to be the issue.
indexPathsToDeleteForSupplementaryViewOfKind: is called multiple times and one can assume it needs multiple passes through it to work properly but since we nil _unReadHeaderIndexPath; it seems like it isn't added to the returned array at the appropriate time.
It also seems like the incorrect method in as I'm checking for insertion action in a method name indexPathsToDeleteForSupplementaryViewOfKind: so the solution was to move all of that logic to the method
prepareForCollectionViewUpdates:
Where we originally set the values for self.updatedItems in the first place. Which means i can get rid of that member variable altogether. And it animates and disappears without any odd copy sticking around.

Need a view that acts like a log view

I need to implement a view that acts as a log view, so that when you push a message into it, the message would push other messages upwards.
Is there anything like that for iOS?
You can easily implement that using standard UITableView:
Each cell will be responsible for displaying 1 log message
Add new cell to the end of the table when new message arrive
Scroll table to the bottom after cell is added (using scrollToRowAtIndexPath:atScrollPosition:animated: method with UITableViewScrollPositionBottom position parameter)
That means you'll need to store your log messages in array, but if you're going to display them you need to store messages anyway
#Vladimir's answer is probably the way to go, but just for the sake of seeing some additional options, here's an example using a UITextView:
- (IBAction)addNewLog:(UIButton *)sender {
NSString *myInputText = #"some new text from string";
NSString *temp = myTextView.text;
[myTextView setText:[temp stringByAppendingString:[NSString stringWithFormat:#"\n%#: %#",[NSDate date],myInputText]]];
[myTextView setContentOffset:CGPointMake(0, myTextView.contentSize.height - myTextView.frame.size.height) animated:NO];
}
Then if you wanted to separate the text in the text view into objects in an array:
NSArray *myAwesomeArray = [myTextView.text componentsSeparatedByString:#"\n"];
Mind you, the above would break if the "myInputText" string ever contained a line break.

Use two nested dispatch_async in parallel?

Could someone explain to me why I get second NSLog(images count) BEFORE first NSLog(text count) in debug console?
My purpose of having two parallel dispatch_async (each one is nested) is to separately download text and image into a table view using core data. Note that image data are much bigger than text.
I firstly try downloading both text and images in one nesteddispatch_async (jump back to main queue to do mapping and commit of both text and images of course). However, I notice significant delay in displaying cells on screen. I figure there must be some better ways to do it. Any suggestion is appreciate.
-(void)doSometing
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
// download text data
[document.managedObjectContext performBlock:^{
NSLog(#"Text count is %d \n\n",[text count]);
// insert text data to context
// commit text data
}];
});
// see if I can shorten wait time by download image separately
//
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
// download image
[document.managedObjectContext performBlock:^{
NSLog(#"Images count is %d \n\n",[images count]);
// insert images into context
// commit images
}];
});
}

mimic animated iPhone recent calls list on OS X with NSTableView

I'm trying to pretty closely mimic the functionality of the iPhone phone app recent calls TableView, using an NSTableView on OS X. The desired functionality is that you click a button and the TableView switches from showing you all the recent calls to showing the subset of those calls that were missed, AND, I want to have the animation that you see on the iPhone where the appearance and disappearance of the rows is animated.
I'm using a view based NSTableView and I have it wired up very simply with a manual data source and delegate in my view controller (not using cocoa bindings).
Everything is working except when trying to delete rows to show the subset of "missed" calls. If I just reconstruct the NSMutableArray that is the data source for the TableView and then call reloadData, it works perfectly. But that gives me no animation. Here is the unanimated version which results in the TableView and the data source NSMutableArray in perfect sync, the correct content in the TableView, but no animation:
- (IBAction)showMissed:(id)sender {
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
Call *call;
for (call in callsArray) {
if (!call.missed) {
[indices addIndex:[callsArray indexOfObject:call]];
}
}
if ([indices count] > 0) {
[callsArray removeObjectsAtIndexes:indices];
[self.recentsTableView reloadData];
}
}
When I try to use the NSTableView removeRowsAtIndexes:indices withAnimation: method to get the animation I want, I am getting some very strange behavior. Here is the animated showMissed method:
- (IBAction)showMissed:(id)sender {
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
Call *call;
for (call in callsArray) {
if (!call.missed) {
[indices addIndex:[callsArray indexOfObject:call]];
}
}
if ([indices count] > 0) {
[callsArray removeObjectsAtIndexes:indices];
[self.recentsTableView removeRowsAtIndexes:indices withAnimation:NSTableViewAnimationSlideUp];
}
}
The logic I am using in the animated version of the method is pulled out of Apple's TableViewPlaygound sample code.
When I run this code, I get two strange results. First, the TableView does not show only "missed" calls -- it shows the right number of rows (i.e. the number of rows in the TableView equals the number of Call objects marked as missed), but the actual objects showing in the rows are some missed and some not missed.
Even weirder though is that after the animated version runs, the callsArray has ALL of the Call objects in it, as though the [callsArray removeObjectsAtIndexes:indices]; statement was not there at all. It seems like somehow the removeRowsAtIndexes:indices withAnimation: method is messing with the contents of my data source array and throwing the whole thing out of whack.
FWIW, my animated showAll method which is basically the reverse of the above using the insertRowsAtIndexes:indices withAnimation: seems to work perfectly.
What am I doing wrong?
There must have been some corruption somewhere in the sync between the data source array and the tableView. I fixed it by adding a reloadData call at the top of the method. Here is the fixed showMissed method:
- (IBAction)showMissed:(id)sender {
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
Call *call;
[self.recentsTableView reloadData];
for (call in callsArray) {
if (!call.missed) {
[indices addIndex:[callsArray indexOfObject:call]];
}
}
if ([indices count] > 0) {
[callsArray removeObjectsAtIndexes:indices];
[self.recentsTableView removeRowsAtIndexes:indices withAnimation:NSTableViewAnimationSlideUp];
}
}