Methods of iOS development, used by me in the Pictograph contest

Recently underwent three rounds of the competition Vkontakte on creating a photo app for the iOS platform. The link to the contest: http://vk.com/photo_contest. In the process of application development, the first round I found some interesting solutions to some problems. These decisions, I wanted to share with the public. Hardened iOS developers I hardly tell something new, I don't think the article is also suitable for beginners. I assume that the article will be interesting for iOS developers with experience of 2-5 applications.



the

horizontally Scrollable tape
latest photos from device memory


First, very correctly, that from the beginning of the tape is not exactly 4 or exactly 5 pictures, and 4 and 1/3. This allows the user to instantly understand that the photo list is scrolled horizontally.

Several questions arise:
the

    How many photos to upload to this tape? the

  • How to organize dynamic loading of photos, so they all hung in memory?

First, I've decided to display in the ribbon, all pictures from the device memory in case of problems with download speeds promised myself to come back to this question.
Immediately there was a problem getting all the photos from the device memory in the correct order, as was required in the beginning of the tape to show the new picture. Immediately it turned out that the most recent photos I have are not in the album of saved photos, photo Stream, which with me to this day shared with my brother.
It was decided at the beginning of the tape to display the latest photos from saved photos, and for this album all the rest. Inside each photo album, I began to have since the last. Here is the source that receives an array of ALAssets in the order described below:

the
@implementation ALAssetsLibrary (Extension)
- (void)latestAssetsAndCall:(void (^)(NSMutableArray *))callback
{
__block NSMutableArray * assets = [NSMutableArray arrayWithCapacity:5000];
[self enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group == nil)
{
callback(assets);
return;
}

ALAssetsGroupType groupType = [[group valueForProperty:ALAssetsGroupPropertyType] intValue];
int insertIndex = (groupType == ALAssetsGroupSavedPhotos) ? 0 : assets.count;
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result != nil)
[assets insertObject:result atIndex:insertIndex];
}];
} failureBlock:^(NSError *error) {
if (error)
NSLog(@"%@", error);
}];
}
@end

As for the second question, I have not found anything better than to use a UITableView, because it is simply created for scrolling long lists, dynamic loading of content and reuse of similar cells. The only problem is that the table must be rotated 90° counterclockwise. Given that the transformation is carried out relative to the center of the object located at UITableView in the expected location of the center of the tape and execute:

the
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI_2);

When you create a table cell, you must perform a reverse transformation — rotation 90° clockwise:

the
- (UItableViewCell *)tableView:(UITableView *)tableView 
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BottomRollCell"];
if (cell == nil) 
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"BottomRollCell"];
cell.contentView.transform = CGAffineTransformMakeRotation(M_PI_2);
// then the creation of cell elements
}
// then filling in the elements of cell specific content
}

What we have in the end? Table as you scroll requests the contents of their cells. Having an array of ALAssets, get thumbnail-a the images and fill their table cells. The table scrolls very smoothly, no lag with uploading photos not seen. About the time of receipt of all photos — getting 2500 photos takes less than 1 second of time, but to run the application, it is critical. Do the animation of the shown table from right to left upon receipt of ALAssets. It turns out very cute and the delay in half a second is almost unnoticeable. Especially that not all query assets, and a set of indexes of speed boost doesn't give, it I was even more discouraged. So a quick optimization with the preload first of the photos is not rolled.
the

Animation of opening and closing photos


The decision was made to expand photos from thumbnail from the ribbon when they open them and fold them back when you cancel editing. To get the rectangle coordinates of a cell of the table, I used the class method UITableView:

the
- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath;

Had to spend a little Mannyto determine the exact coordinates of the images based on its rotation, etc., After receiving the coordinates of the thumbnail images in the main view, on top of the thumbnails I had reduced full-sized animated image and changed the size and position of the image. Ideally, I would like to have the image out of the tape with a nonlinear trajectory, but the time never left...

In order to track changes in the photos of the device, you must subscribe to the event ALAssetsLibraryChangedNotificationthat you want to restart the array ALAssets. The tape is not started to be updated when saving pictures to the app itself — you must use an internal flag to suppress redrawing of the ribbon in the next update and manually add a new ALAsset at the beginning of the array of assets.
Save I have is in the left position, I move the table to the right by the width of one image, perform the animation of the fly image in the tape, move the table back without animation, and then manually call reloadData.

To animate the opening and closing images were executed smoothly and as quickly as possible, had to do one interesting thing. If you open the photo, zoom and click undo button — the picture will fly to the tape in exactly the form in which we left it after the zoom. The picture will remain there in this form until, until it is hidden behind the border of the screen and does not restart again. To achieve this effect, I used NSMutableDictionary with URLS assets as keys and NSValue containing CGRect as values. Sorry, I forgot to remove this property in a video review, but it was one of the most interesting problems for me.

the

Smooth scaling and positioning of pictures of the effects


I really wanted to do scaling and positioning of pictures with simultaneous application of the selected effect in the preview too, all moving synchronously and apply the effect. In General, if you try to do to hinder this joy is shamelessly. It was found interesting solution to apply the selected effect to the main photo, and take a photo reduced to five times (or rather 320.0/56.0) and apply other effects to it, and in the process of scaling and positioning to synchronize scrolls thumbnails with the main UIScrollView. This method works quickly, smoothly and no mistakes.



Code that synchronizes scrolls thumbnails with the main scrolls (these are the methods delegate to UIScrollViewDelegate):

the
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (UITableViewCell * cell in [self.filtersTable visible cells])
{
UIScrollView * filterScrollView = (UIScrollView *)[cell.contentView viewWithTag:125];
filterScrollView.contentOffset = CGPointMake(scrollView.contentOffset.x*56/320,
scrollView.contentOffset.y*56/320);
}
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
for (UITableViewCell * cell in [self.filtersTable visible cells])
{
UIScrollView * filterScrollView = (UIScrollView *)[cell.contentView viewWithTag:125];
filterScrollView.zoomScale = self.scrollView.zoomScale;
filterScrollView.contentOffset = CGPointMake(scrollView.contentOffset.x*56/320,
scrollView.contentOffset.y*56/320);
}
}


the

saving the result


As for us, the most important performance and image quality in General, the conditions of competition is not regulated, I decided to keep images of size 640х640 (on the retina). And the easiest way to do it through the rendering of the main view in the context of image offset up:
the
- (UIImage *)renderImageForSaving
{
UIGraphicsBeginImageContextWithOptions(self.scrollView.bounds.size, YES, 0.0);
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, -self.scrollView.frame.origin.y);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * image = UIGraphicsGetImageFromCurrentImagecontext();
UIGraphicsEndImageContext();

return image;
}

It's fast and without additional problems with labels, etc. Yes, the solution would be to store and more — but that's another issue that requires time and patience)

Video from my naughty dog demonstrating the application:



I think the link can be given (OK?). The app is free and ad, respectively.
Link to the app: https://itunes.apple.com/app/pictography/id570470169
Under iOS5 now there are several different bugs, the update with fixes submitted for review and is expected by the end of the week...

PS and finally, a big thanks to VK for the organization and conduct of such competitions. After all, they motivate/encourage the programmers to begin to develop a new promising platform (I somehow think that among the participants a lot of newcomers to the platform). Very pleased with the input data for the contest — all images were as on selection. Not a single superfluous pixel never stuck...
Article based on information from habrahabr.ru

Популярные сообщения из этого блога

Approval of WSUS updates: import, export, copy

The Hilbert curve vs. Z-order

Kaspersky Security Center — the fight for automation