Best way to implement "iOS Photos style" tiled image gallery - objective-c

I have an app that basically can be used to download, upload, and manage photos from various web services.
I want the image list view to look like the iOS Photos app but not quite sure what the best way to do that is.
I was thinking I could use NSMutableArray and subclass UIScrollView to create that functionality from scratch but I'd really like to use NSFetchedResultsController because some of the data related to the images are dynamically/asynchronously updated/inserted/deleted in Core Data.
Right now I've setup something pretty hacky.
I created an separate Core Data entity to hold relationships to 4 photos in each managed object and I made UITableView loop through them. Then cellforrow delegate would loop through the 4 photos in each table cell. This approach sucks because it's hard to delete and insert photos dynamically and I have to reconstruct relationships and reload the table each time an update is made.
I've looked at some other libraries that do this but I really want to understand what the most simple and efficient way to do this is.

If you are referring to the tile view, you can duplicate that using a UITableView or a UIScrollView. If you want it to side scroll then a UIScrollView is the starting point.
If you want it to scroll vertically (which I recommend on iPhone) then use a UITableView with a custom UITableViewCell.
As for the Core Data side, you are probably coming at this incorrect, you are trying to store UI state information (how many photos are in a cell) in core data. Core Data should not care about how many images are in a cell.
UITableViewCell
To use a UITableView you would create an array that contains all of your images. You can then determine how many rows you need by dividing that array by the number of images per row.
From there you should subclass UITableViewCell and pass in the 4 images for that cell to draw. Then it is a simple matter of adding 4 UIImageView instances to the custom cell and setting the images.
UIScrollView
For a scrollview you crate a UIImageView for each image and then add them as subviews of the UIScrollView. You would then set the frame of each UIImageView so that they are displayed in a grid.
If you find you have more images than you can hold in memory at once then you will need to deal with tiling, effectively paying attention to where the user is scrolling and filling in (via moving the offscreen image views) ahead of the edge the user is scrolling towards. This is the same thing you get effectively for free with a UITableView via its queuing of cells.

I just did this with a UITableView - you are over thinking it with the relationships.
For my solution I created a UITableViewCell subclass has four buttons as properties.
In my cellForRow method I figure out which four images belong to that row and then set the appropriate image for the appropriate button.
My images are in an array in my view controller. I populate the array in viewDidLoad. Whenever I add or delete an image from the managedObjectContext I repopulate the array and then reload the tableview. Reloading the tableview will rearrange the images in the cell appropriately.
Scrolling is smooth and the only drawback I have run into is that I would like the images to animate to their new positions when I delete one, but I do not think that I'll be able to pull that off with this setup.

I think a very nice implementation is AQGridView.
It is easy to extends, has nice reordering (see the springboard demo) and it reuses the cell.
If you ever implemented a TableViewDelegate, you should be able to use it.

You should check out the Three20 photo viewer. The project is on github. Also, there's a nice tutorial for the photo viewer here

Related

Strategy for measuring performance of an iOS app

I have a simple case of an iOS application I would like to measure: a UITableView built with custom extended cells. Each cell is a composition of a main views and some subviews. Based on some criteria I am hiding or showing subviews in table cell.
Supposing I have to decide if adding subviews when needed, or built xib with all the outlets and hide/show when needed.
What could be the best approach for deciding between those two ?
Supposing also you didn't read Apple guidelines about composition of cell, what concrete steps you would do either via code (by putting NSLog statement for example), or via Xcode instruments (which one to choose etc...) to confirm the choice.
This is quite a new matter for me, so please be as much specific as you can.
This http://blog.giorgiocalderolla.com/2011/04/16/customizing-uitableviewcells-a-better-way/ is a great resource.
Having many subviews with transparent backgrounds degrades scrolling performance.
Dynamically adding subViews could get difficult to track, and if you don't do it "right" then you'll end up with cells with too many "extra" subviews, via the cell reuse queueing mechanism.
You'll have to add the subViews in the init method of the cell itself and not in the cellForRow method of your data source. (unless you're using some way to track the creation/availability of any given subview, like viewWithTag etc.)

UIPageViewController optimization and possible solution

I am using a UIPageViewController in my app to display number of images (1024x768 size). Is it ok to make array of UIViewControllers each with his picture and in set appropriate for each page? Maybe if you get like 50 pictures it will crash? At the moment I store images in Documents folder so I can remove images from view controllers that are not on screen
This wouldn't be very efficient, you'd be much better off doing the same thing with UIImageViews. Even then you have to be careful. Probably the best way to handle this would be to using image views and deallocating them when they go more than one image width out of the screens bounds. Then of course reallocing when the image in question is the next image in line to be displayed in either direction.
EDIT: It looks like you can use the following UIPageViewController Datasource methods to set which view controller do basically have queued and waiting to the right/left. Using these you should be able to only allocate 3 view controllers at a time.
– pageViewController:viewControllerBeforeViewController:
– pageViewController:viewControllerAfterViewController:
Then you can use this to set the initial controller:
Set the initial view controller using UIPageViewController's
-setViewControllers:direction:animated:completion:
The sentence below is quoted from Apple's documentation and is leading me to believe that the controller may be able to only load the necessary view controllers into memory on its own.
View controllers are either provided one at a time (or two at a time,
depending upon the spine position and double-sided state) via the
setViewControllers:direction:animated:completion: method, or provided
as-needed by the data source. Gesture-based navigation is enabled only
when a data source is provided.

Getting around Subclassing UIView and Using Pictures in Storyboard

I'm trying to write my first iPhone app, and I'm running into a sort of design struggle. What I want to do is have a grid of icons and when you touch one, all the icons above and to the left will "activate" and all the ones below and to the right will "deactivate." If an icon is activated it shows one picture, and if it's not activated it shows another.
The problem I have is that I want to assign a gesture recognizer to each one of these individual icons, and when that icon is tapped, it needs to call a function that updates my grid of icons. But in order to properly update, the function needs to pass as arguments the location of the image in the grid and there's no way to call a function with arguments as part of a gesture recognizer.
So really all I need to do is extend UIImageView to hold two extra integers and the grid it's contained in, and then I could have the following code:
imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapgr = [[UITapGestureRecognizer alloc] initWithTarget:
self action:#selector(handleTap)];
[imageView addGestureRecognizer:tapgr];
:
:
- (void)handleTap
{
[self.grid updateTableFromRow:self.row andCol:self.col
}
So I suppose this is one way of doing it, but I'm told that I'm not supposed to extend classes in Objective-C, that I should build them from the ground up. In this case, I would just make a custom view with all the properties and/or instance variables I need and I would just fill this custom view with the UIImageView.
This is mostly fine, except when it comes to building my Storyboard. I put all the code that manages and creates this table of icons (programmatically) in another custom view, GridOfIconsView. So on the Storyboard I drag out a custom view and set to be a GridOfIconsView, but then I just see a big white rectangle, and I really want to be able to visualize my app in Storyboard. I know that I can drag out actual image files that I use for the icons onto Storyboard and set them to be a custom view, but then how does that work? Is that image just a background to the custom view? Would I be able to change it programmatically? So if the activated image was a green square, but the deactivated was a red one and I initially dragged out red squares to the Storyboard, would I have access to that red square image?
And a more concerning issue is that I want to manage all these icons in a data structure, either as a 2d array (id icons[][]) or a NS(Mutable?)Array of NS(Mutable?)Arrays. Either way, how could I initialize the data structure to contain links to all these? The grid will be probably 8x8 or 10x10, and there's no way I'm going to have 64-100 #propertys connecting these icons. I'm thinking the only way to sensible organize this is programmatically, but then still, how can I visualize it in Storyboard?
First, it's completely fine to extend classes in Objective-C, and it's done all the time. UIView, UIViewController, UIComponent, etc., were all designed specifically to be subclassed and extended.
However, there are two ways you can do this that are much simpler than extending the class. First, you can have your grid as you already do, where each view has a gesture recognizer attached that calls back to a method on the view controller. Then, you can set a tag on each view (or even just use the view's frame for identification), and read that from the callback method (the gesture recognizer is passed back to the callback method). For example, let's say you had a grid of 4x4 views and you simply numbered them starting in the top-left, advancing each column to the right and then each row, from 0 to 11, you could easily identify the view as such:
// The system automatically passes the gesture recognizer as the only parameter
- (void)handleTap:(UIGestureRecognizer)gestureRecognizer
{
NSInteger viewNumber = [[gestureRecognizer view] tag];
// do something with this view
}
The other way you can do it is to have a single gesture recognizer on the parent view, and then in your -handleTap: callback, you'd query the position of the tap in the view. If the position is within the frame of any of your views, you'd know which one and what to do with it. If not, you could ignore it. This solution requires slightly more math, but also requires far less maintenance and far fewer gesture recognizer that need to be wired up. I would recommend this solution over tagging your views.

Adding Images in a TableView in Xcode

I tried to add images with the size of 32 by 32...it worked but it shows me only the first image for all the objects on the table view... In other words only image for all the other objects
You need to implement this in the tableView:willDisplayCell:forTableColumn:row delegate method. You should probably read the TableView Programming Guide before going further.

UIImageView on top of another UIImageView, changeing layers

Afternoon, I have a UIImageView that I progmatically add to the window. Infact I have multiple UIImageViews that do so and when I click on any specific UIImageView I want it to become 'top-dog' so to say and be drawn over all other objects on the screen. Basically like the priority drawing for MSWindows operating systems when it comes to their windows. I've scoured all the options built in for UIImageViews when it comes to layering but I cannot seem to find any! I know it exists because in UIBuilder there is a command for sending back/front toBack/toFront. How do I access these progmatically?
Edit*
Also I fear that you might have to access the order in which the subViews are pushed into the 'subView stack' and manually move these around to achieve the result that I want and if so, how would I go about doing this?
Edit2*
Perhapse these are the functions I'm looking for?
bringSubviewToFront
sendSubviewToBack
exchangeSubviewAtIndex
Does this allow for easy Index shuffling?
UIView class has bringSubviewToFront: and sendSubviewToBack: for changing subviews z-order (see "Managing the View Hierarchy" section in class reference for more).