TestsTested | ✗ |
LangLanguage | Obj-CObjective C |
License | MIT |
ReleasedLast Release | Dec 2014 |
Maintained by Unclaimed.
A subclass of UIScrollView, SMGridView allows you to have a custom grid that uses methods similar to UITableView, UITableViewDataSource, and UITableViewDelegate. It provides support for:
See the full API documentation here.
To install, simply clone the project and drag SMGridView.h and SMGridView.m into your project. These files are inside SMGridView/source. After that, import SMGridView.h and you are ready to use it.
You can check out a complete example with lot of functionality just by running this project in xCode. The code is really simple, everything happens inside the SMGridViewTest.m
class. To manage the settings, we use inAppSettings, so that library is included in this project as well. Play with the settings to see how the grid reacts to changes.
Currently this project does not use ARC. It would be fairly simply to change it to support ARC, but until that happens, you will need to set a fno-obj-arc
compiler flag for SMGridView if your project uses ARC.
To set a compiler flag in Xcode, go to your active target and select the "Build Phases" tab. Then select all source files (SMGridView.h and SMGridView.m), press Enter, insert -fobjc-arc
or -fno-objc-arc
and then press "Done".
First import the header. We import it in the .h file of the view controller because we want the custom view controller to be the dataSource and delegate of the SMGridView.
#import "SMGridView.h"
@interface SMGridViewTestViewController : UIViewController <SMGridViewDataSource, SMGridViewDelegate>
@property (nonatomic, retain) SMGridView *grid;
@end
Now let's create the SMGridView. If you like to use nib files, you can create it in the interface and link it with an IBOutlet. I'm just going to create the SMGridView in the viewDidLoad method.
@implementation MyViewController
@synthesize grid = _grid;
- (void)viewDidLoad {
[super viewDidLoad];
self.grid = [[[SMGridView alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width,
self.frame.size.height)] autorelease];
self.grid.dataSource = self;
self.grid.delegate = self;
}
- (void)dealloc {
[_grid release];
[super dealloc];
}
All the methods in SMGridViewDelegate are optional, but SMGridDataSource requires you to implement some methods:
// 100 items, only one section (default), so no need to read and adapt for section parameters
- (NSInteger)smGridView:(SMGridView *)gridView numberOfItemsInSection:(NSInteger)section {
return 100;
}
// Return views of size 100x100
- (CGSize)smGridView:(SMGridView *)gridView sizeForIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(100, 100);
}
- (UIView *)smGridView:(SMGridView *)gridView viewForIndexPath:(NSIndexPath *)indexPath {
// Check if we can reuse
UILabel *label = [gridView dequeReusableView];
if (!label) {
label = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease]
}
label.text = [NSString stringWithFormat:@"%d", indexPath.row];
return label;
}
Now all we have to do is call the reloadData
method. Just in like a UITableView, this method will actually create the views. Let's do this in viewDidLoad:
@implementation MyViewController
@synthesize grid = _grid;
- (void)viewDidLoad {
[super viewDidLoad];
self.grid = [[[SMGridView alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width,
self.frame.size.height)] autorelease];
self.grid.dataSource = self;
self.grid.delegate = self;
[self.grid reloadData];
}
- (void)dealloc {
[_grid release];
[super dealloc];
}
You can use the vertical
property to decide wether you want vertical (YES) or horizontal (NO) orientation. The default is horizontal (NO). You need to call reloadData after changing this property.
There is a property called padding
that you can set to decide the space between the objects in the grid and the edges of the grid itself.
If you want to add or remove an item, all you have to do is adapt the dataSource (numberOfItemsInSection
should return a different number) and call reloadData. However, SMGridView supports animating adding or removing items.
To add a view using an animation you should call
- (void)addItemAtIndexPath:(NSIndexPath *)indexPath;
once the dataSource is ready.
Removing an item is a little bit more complicated (but not much more!). First, without adjusting the dataSource, you call
- (void)removeItemAtIndexPath:(NSIndexPath *)indexPath;
Once the grid scrolls to the position where the item needs to be removed, the dataSource will receive a call to
- (void)smGridView:(SMGridView *)gridView performRemoveIndexPath:(NSIndexPath *)indexPath;
This is where you have to adapt your dataSource. And that is it!
This project contains an example of how to use SMGridView. To test it out, simply open the project file in XCode and run it. You can press the Edit button to change some of the settings. The source code has comments but it should be easy to follow. Note that we did not use nib files.
To use drag & drop features for sorting the grid, you should have your views be subclasses of UIControl
. Then you need to set enableSort
to YES. Once you drag & drop an item into a new position, this method will be called in the SMGridViewDataSource
:
- (void)smGridView:(SMGridView *)gridView
shouldMoveItemFrom:(NSIndexPath *)fromIndexPath
to:(NSIndexPath *)indexPath;
In the implementation of this method, you should change the dataSource accordingly to reflect the order change. Currently you can only sort items within their section. No section change is allowed (yet!).
SMGridView supports sections. To have different sections, implement the following method in the SMGridViewDataSource
:
- (NSInteger)numberOfSectionsInSMGridView:(SMGridView *)gridView;
All the SMGridViewDataSource
methods will pass an NSIndexPath object. You can access the section and row properties of this object just like you would do with a UITableView.
You can have section headers just as you would with a UITableView. Simply implement these 2 methods in your dataSource:
- (CGSize)smGridView:(SMGridView *)gridView sizeForHeaderInSection:(NSInteger)section;
- (UIView *)smGridView:(SMGridView *)gridView viewForHeaderInSection:(NSInteger)section;
If you want your headers to stick in the top of the grid, just set the property stickyHeaders
to YES in the SMGridView.
SMGridView needs to know the size of all the items to calculate how long the content of the scroll is. This is why the dataSource has the method
- (CGSize)smGridView:(SMGridView *)gridView sizeForIndexPath:(NSIndexPath *)indexPath;
However, if all your views have the same size, SMGridView can perform better. If this is the case, simply implement this method in the SMGridViewDataSource
like this:
- (BOOL)smGridViewSameSize:(SMGridView *)gridView {
return YES
}
This improves performance because SMGridView will do fewer calculations.
You can enable pagination by setting the pagingEnabled
property to YES. However, section headers are not yet compatible with pagination, so you shouldn't combine these 2 features.
It is a common pattern to have a scrollView that will load more content once you scroll down. If you reach the bottom of the scroll and you are still loading more content, it's useful to display a loader on the bottom. SMGridView provides support for that To display a custom view at the bottom of the grid you need to do two things:
1. Set the loaderView
property to the UIView you want to show.
2. In the SMGridViewDataSource
, implement this method:
- (BOOL)smGridViewShowLoader:(SMGridView *)gridView;
and return YES whenever you want to show the loader. Typically the dataSource will know when you are still loading content or when you finish, so this method should reflect that.
If you are using the loader and want to add a batch of 100 more items to the bottom of the grid, instead of calling reloadData
, you can call reloadDataOnlyNew
. This will increase the performance of the grid, as it only needs to calculate positions for the new items, and not the whole grid.
SMGridView is distributed under the MIT license. See the attached LICENSE file for all the sordid details.