├── .gitattributes ├── .gitignore ├── JOGridView.h ├── JOGridView.m ├── JOGridViewCell.h ├── JOGridViewCell.m ├── README.md ├── gridview.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── gridview ├── en.lproj ├── InfoPlist.strings └── MainWindow.xib ├── gridview-Info.plist ├── gridview-Prefix.pch ├── gridviewAppDelegate.h ├── gridviewAppDelegate.m └── main.m /.gitattributes: -------------------------------------------------------------------------------- 1 | *.m diff=objc 2 | *.mm diff=objc 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | !project.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | .com.apple.timemachine.supported -------------------------------------------------------------------------------- /JOGridView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JOGridView.h 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JOGridViewCell.h" 11 | 12 | @class JOGridView; 13 | 14 | /// Delegate 15 | 16 | @protocol JOGridViewDelegate 17 | @optional 18 | -(void)willDisplayCell:(JOGridViewCell *)cell forGridView:(JOGridView *)gridView atIndexPath:(NSIndexPath *)indexPath; 19 | -(CGFloat)gridView:(JOGridView *)gridview heightForRow:(NSUInteger)row; 20 | @end 21 | 22 | /// Data Sources are required 23 | 24 | @protocol JOGridViewDataSource 25 | @required 26 | -(NSUInteger)rowsForGridView:(JOGridView *)gridView; 27 | -(NSUInteger)columnsForGridView:(JOGridView *)gridView; 28 | -(JOGridViewCell *)cellForGridView:(JOGridView *)gridView atIndexPath:(NSIndexPath *)indexPath; 29 | @end 30 | 31 | @interface JOGridView : UIScrollView { 32 | 33 | // debug 34 | 35 | BOOL debug; 36 | UILabel* debugInfoLabel; 37 | 38 | // cached data 39 | 40 | NSUInteger __rows; 41 | NSUInteger __columns; 42 | 43 | NSUInteger __firstWarpedInRow; 44 | CGFloat __firstWarpedInRowHeight; 45 | NSUInteger __lastWarpedInRow; 46 | CGFloat __lastWarpedInRowHeight; 47 | 48 | // ivar properties 49 | 50 | CGFloat __previousOffset; 51 | BOOL __dataSourceDirty; 52 | 53 | NSMutableArray* __visibleRows; 54 | NSMutableDictionary* __reusableViews; 55 | 56 | id gridViewDelegate; 57 | id gridViewDataSource; 58 | } 59 | @property (nonatomic, assign) id datasource; 60 | @property (nonatomic, assign) id delegate; 61 | @property (readwrite) BOOL debug; 62 | 63 | /// cell accessors 64 | @property (readonly) NSArray *visibleRows; 65 | -(NSArray *)indexPathsForVisibleCells; 66 | -(NSIndexPath *)indexPathForVisibleCell:(JOGridViewCell *)cell; 67 | -(JOGridViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; 68 | 69 | /// scrolling 70 | -(void)scrollToRow:(NSUInteger)row animated:(BOOL)animated; 71 | -(void)scrollToIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; 72 | 73 | /// gridview properties 74 | @property (readonly) NSUInteger numberOfRows; 75 | @property (readonly) NSUInteger numberOfColumns; 76 | 77 | /// reload methods 78 | -(void)reloadData; 79 | -(void)reloadRow:(NSUInteger)row; 80 | -(void)reloadCellAtIndexPath:(NSIndexPath *)indexPath; 81 | -(JOGridViewCell *)dequeueReusableCellWithIdenitifer:(NSString *)identifier; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /JOGridView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JOGridView.m 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "JOGridView.h" 10 | #import 11 | 12 | #define JOGRIDVIEW_DEFAULT_ROW_HEIGHT 44.0 13 | 14 | @interface JOGridView (InternalMethods) 15 | -(void)enqueueReusableCellsForRow:(NSUInteger)row; 16 | -(void)enqueueReusableCell:(JOGridViewCell *)cell; 17 | 18 | -(void)purgeCells; 19 | -(void)buildCells; 20 | 21 | -(NSArray *)cellsForLayoutOfRow:(NSUInteger)row atHeight:(CGFloat)height; 22 | -(void)layoutRow:(NSUInteger)row atHeight:(CGFloat)height scrollingUp:(BOOL)scrollingUp; 23 | -(JOGridViewCell *)cellForlayoutAtIndexPath:(NSIndexPath *)indexPath atHeight:(CGFloat)height; 24 | 25 | -(CGFloat)heightRelativeToOriginForRow:(NSUInteger)row; 26 | -(NSUInteger)rowForHeightRelativeToOrigin:(CGFloat)height; 27 | 28 | // delegate datasource single point of entry 29 | -(CGFloat)delegateHeightForRow:(NSUInteger)row; 30 | -(JOGridViewCell *)dataSourceCellAtIndexPath:(NSIndexPath *)indexPath; 31 | -(void)delegateWillDisplayCell:(JOGridViewCell *)cell atIndexPath:(NSIndexPath *)indexPath; 32 | 33 | @end 34 | 35 | @implementation JOGridView 36 | @synthesize visibleRows = __visibleRows; 37 | @synthesize datasource = gridViewDataSource, delegate = gridViewDelegate; 38 | @synthesize numberOfRows = __rows, numberOfColumns = __columns; 39 | @synthesize debug; 40 | 41 | - (id)initWithFrame:(CGRect)frame { 42 | if ((self = [super initWithFrame:frame])) { 43 | __reusableViews = [[NSMutableDictionary alloc] initWithCapacity:0]; 44 | __visibleRows = [[NSMutableArray alloc] initWithCapacity:0]; 45 | __rows = 0; 46 | __previousOffset = 0.0; 47 | 48 | __dataSourceDirty = YES; 49 | 50 | __firstWarpedInRow = 0; 51 | __firstWarpedInRowHeight = 0.0; 52 | 53 | __lastWarpedInRow = 0; 54 | __lastWarpedInRowHeight = 0.0; 55 | 56 | self.alwaysBounceVertical = YES; 57 | self.showsVerticalScrollIndicator = YES; 58 | self.showsHorizontalScrollIndicator = NO; 59 | 60 | self.canCancelContentTouches = NO; 61 | self.clipsToBounds = YES; 62 | self.pagingEnabled = NO; 63 | self.scrollEnabled = YES; 64 | 65 | debugInfoLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, self.frame.size.height - 10 - 22.0, 100.0, 22.0)]; 66 | debugInfoLabel.textAlignment = UITextAlignmentCenter; 67 | debugInfoLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; 68 | debugInfoLabel.backgroundColor = [UIColor whiteColor]; 69 | debugInfoLabel.textColor = [UIColor blackColor]; 70 | debugInfoLabel.layer.cornerRadius = 5.0; 71 | debugInfoLabel.font = [UIFont systemFontOfSize:10.0]; 72 | 73 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(conserveMemory:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 74 | } 75 | 76 | return self; 77 | } 78 | 79 | -(void)dealloc { 80 | 81 | [debugInfoLabel release], debugInfoLabel = nil; 82 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 83 | [__reusableViews release], __reusableViews = nil; 84 | [__visibleRows release], __visibleRows = nil; 85 | [super dealloc]; 86 | } 87 | 88 | #pragma mark - 89 | #pragma mark Override Accessors 90 | 91 | -(void)setDatasource:(id)datasource { 92 | if (datasource != gridViewDataSource) { 93 | gridViewDataSource = datasource; 94 | __dataSourceDirty = YES; 95 | } 96 | } 97 | 98 | #pragma mark - 99 | #pragma mark View Layout and Maintainence 100 | 101 | -(void)purgeCells { 102 | [__visibleRows removeAllObjects]; 103 | [__reusableViews removeAllObjects]; 104 | 105 | for (UIView *view in self.subviews) { 106 | [view removeFromSuperview]; 107 | } 108 | } 109 | 110 | -(void)buildCells { 111 | // figure out the visible rows 112 | 113 | __firstWarpedInRow = [self rowForHeightRelativeToOrigin:self.contentOffset.y]; 114 | __firstWarpedInRowHeight = [self heightRelativeToOriginForRow:__firstWarpedInRow]; 115 | 116 | // lets find the last row 117 | CGFloat adjustedOffset = self.contentOffset.y + (self.contentOffset.y - __firstWarpedInRowHeight); 118 | NSUInteger startrow = __firstWarpedInRow; 119 | 120 | while ((adjustedOffset < (self.contentOffset.y + self.frame.size.height)) && (startrow < __rows)) { 121 | adjustedOffset += [self delegateHeightForRow:startrow]; 122 | startrow++; 123 | } 124 | 125 | __lastWarpedInRow = startrow-1; 126 | __lastWarpedInRowHeight = [self heightRelativeToOriginForRow:__lastWarpedInRow]; 127 | 128 | if (__dataSourceDirty) { 129 | // if the data source is dirty, it means we must purge all existing 130 | // cells and recreate them in the view hierachy 131 | 132 | __dataSourceDirty = NO; 133 | [self purgeCells]; 134 | 135 | JOGridViewCell *cell = nil; 136 | NSMutableArray *rowArray = nil; 137 | 138 | for (NSUInteger i=__firstWarpedInRow;i<__lastWarpedInRow+1;i++) { 139 | 140 | rowArray = [NSMutableArray arrayWithCapacity:__columns]; 141 | 142 | for (NSUInteger q=0;q<__columns;q++) { 143 | cell = [self dataSourceCellAtIndexPath:[NSIndexPath indexPathForRow:q inSection:i]]; 144 | 145 | [self delegateWillDisplayCell:cell atIndexPath:[NSIndexPath indexPathForRow:q inSection:i]]; 146 | 147 | [rowArray addObject:cell]; 148 | 149 | if (!cell.superview) { 150 | [self addSubview:cell]; 151 | } 152 | 153 | 154 | cell.frame = CGRectMake(-CGFLOAT_MAX, -CGFLOAT_MAX, 0, 0); 155 | } 156 | 157 | [__visibleRows addObject:rowArray]; 158 | } 159 | 160 | if (self.debug) { 161 | [self.superview addSubview:debugInfoLabel]; 162 | } else { 163 | [debugInfoLabel removeFromSuperview]; 164 | } 165 | 166 | } else { 167 | 168 | // as a precaution, move all cells into hiding position if datasource 169 | // is not dirty but we call a reload data 170 | 171 | for (UIView *view in self.subviews) { 172 | view.frame = CGRectMake(-CGFLOAT_MAX, -CGFLOAT_MAX, 0, 0); 173 | } 174 | } 175 | 176 | // layout the cells 177 | 178 | CGFloat startHeight = __firstWarpedInRowHeight; 179 | CGFloat rowHeight = 0.0; 180 | JOGridViewCell *cell = nil; 181 | 182 | for (NSUInteger i=0;i<[__visibleRows count];i++) { 183 | rowHeight = [self delegateHeightForRow:__firstWarpedInRow + i]; 184 | 185 | for (NSUInteger q=0;q<[[__visibleRows objectAtIndex:i] count];q++) { 186 | cell = [[__visibleRows objectAtIndex:i] objectAtIndex:q]; 187 | 188 | cell.frame = CGRectMake(q * (self.frame.size.width / __columns), startHeight, self.frame.size.width / __columns, rowHeight); 189 | } 190 | 191 | startHeight += rowHeight; 192 | } 193 | 194 | } 195 | 196 | -(NSArray *)cellsForLayoutOfRow:(NSUInteger)row atHeight:(CGFloat)height { 197 | 198 | JOGridViewCell *cell = nil; 199 | 200 | NSMutableArray *rowOfCells = [NSMutableArray arrayWithCapacity:__columns]; 201 | 202 | for (NSUInteger i=0;i<__columns;i++) { 203 | cell = [self cellForlayoutAtIndexPath:[NSIndexPath indexPathForRow:i inSection:row] atHeight:height]; 204 | [rowOfCells addObject:cell]; 205 | } 206 | 207 | return rowOfCells; 208 | 209 | } 210 | 211 | -(void)layoutRow:(NSUInteger)row atHeight:(CGFloat)height scrollingUp:(BOOL)scrollingUp { 212 | 213 | NSArray *rowOfCells = [self cellsForLayoutOfRow:row atHeight:height]; 214 | 215 | if (scrollingUp) { 216 | [__visibleRows addObject:rowOfCells]; 217 | } else { 218 | [__visibleRows insertObject:rowOfCells atIndex:0]; 219 | } 220 | } 221 | 222 | -(JOGridViewCell *)cellForlayoutAtIndexPath:(NSIndexPath *)indexPath atHeight:(CGFloat)height { 223 | JOGridViewCell *cell = [self dataSourceCellAtIndexPath:indexPath]; 224 | 225 | NSAssert(cell != nil, @"Cells cannot be nil!"); 226 | 227 | [self delegateWillDisplayCell:cell atIndexPath:indexPath]; 228 | 229 | if (!cell.superview) { 230 | [self addSubview:cell]; 231 | } 232 | 233 | cell.frame = CGRectMake(indexPath.row * (self.frame.size.width / __columns), height, self.frame.size.width / __columns, [self delegateHeightForRow:indexPath.section]); 234 | [cell layoutSubviews]; 235 | 236 | return cell; 237 | 238 | } 239 | 240 | -(void)layoutSubviews { 241 | [super layoutSubviews]; 242 | 243 | BOOL scrollingDownwards = (__previousOffset > self.contentOffset.y) ? YES : NO; 244 | 245 | if ([gridViewDataSource conformsToProtocol:@protocol(JOGridViewDataSource)]) { 246 | 247 | if (__dataSourceDirty) { 248 | [self reloadData]; 249 | } 250 | 251 | if (self.debug) { 252 | NSUInteger cellcount = 0; 253 | 254 | for (id obj in self.subviews) { 255 | if ([obj isKindOfClass:[JOGridViewCell class]]) { 256 | cellcount++; 257 | } 258 | } 259 | 260 | debugInfoLabel.text = [NSString stringWithFormat:@"cells in view: %i", cellcount]; 261 | } 262 | 263 | if ([gridViewDataSource conformsToProtocol:@protocol(JOGridViewDataSource)]) { 264 | if (scrollingDownwards) { 265 | // scrolling down 266 | 267 | // decide if we are even gonna warp in new rows 268 | 269 | while ((__firstWarpedInRow > 0) && (self.contentOffset.y <= __firstWarpedInRowHeight)) { 270 | // lets warp in a row! 271 | __firstWarpedInRowHeight -= [self delegateHeightForRow:__firstWarpedInRow]; 272 | __firstWarpedInRow--; 273 | 274 | [self layoutRow:__firstWarpedInRow 275 | atHeight:__firstWarpedInRowHeight 276 | scrollingUp:NO]; 277 | 278 | // decide if we need to warp out a row that's now hidden 279 | 280 | while (([__visibleRows count] > 0) && ((self.contentOffset.y + self.frame.size.height) <= __lastWarpedInRowHeight)) { 281 | 282 | [self enqueueReusableCellsForRow:__lastWarpedInRow]; 283 | [__visibleRows removeLastObject]; 284 | 285 | __lastWarpedInRowHeight -= [self delegateHeightForRow:__lastWarpedInRow]; 286 | __lastWarpedInRow--; 287 | } 288 | 289 | // agressive enqueuing of anything below the supposedly 290 | // last visible row 291 | for (UIView *view in self.subviews) { 292 | if (([view isKindOfClass:[JOGridViewCell class]]) && (view.frame.origin.x >= 0) && (view.frame.origin.y > __lastWarpedInRowHeight + [self delegateHeightForRow:__lastWarpedInRow])) { 293 | [self enqueueReusableCell:(JOGridViewCell *)view]; 294 | } 295 | } 296 | 297 | } 298 | 299 | 300 | 301 | } else { 302 | // scrolling up 303 | 304 | 305 | while ((__lastWarpedInRow < __rows-1) && ((self.contentOffset.y + self.frame.size.height) >= (__lastWarpedInRowHeight + [self delegateHeightForRow:__lastWarpedInRow]))) { 306 | 307 | __lastWarpedInRowHeight += [self delegateHeightForRow:__lastWarpedInRow]; 308 | __lastWarpedInRow++; 309 | 310 | [self layoutRow:__lastWarpedInRow 311 | atHeight:__lastWarpedInRowHeight 312 | scrollingUp:YES]; 313 | 314 | // deal with enqueueing 315 | while (([__visibleRows count] > 0) && (self.contentOffset.y >= (__firstWarpedInRowHeight + [self delegateHeightForRow:__firstWarpedInRow]))) { 316 | 317 | [self enqueueReusableCellsForRow:__firstWarpedInRow]; 318 | [__visibleRows removeObjectAtIndex:0]; 319 | 320 | __firstWarpedInRowHeight += [self delegateHeightForRow:__firstWarpedInRow]; 321 | __firstWarpedInRow++; 322 | 323 | } 324 | 325 | for (UIView *view in self.subviews) { 326 | if (([view isKindOfClass:[JOGridViewCell class]]) && (view.frame.origin.x >= 0) && (view.frame.origin.y < __firstWarpedInRowHeight)) { 327 | [self enqueueReusableCell:(JOGridViewCell *)view]; 328 | } 329 | } 330 | 331 | } 332 | 333 | } 334 | } 335 | } 336 | 337 | __previousOffset = self.contentOffset.y; 338 | } 339 | 340 | -(NSUInteger)rowForHeightRelativeToOrigin:(CGFloat)height { 341 | 342 | // find out the row that the current height represents all the way from the 343 | // origin of the content view. if the height is exactly the height of the 344 | // row or less than the height, it is that row 345 | 346 | if ([gridViewDelegate respondsToSelector:@selector(gridView:heightForRow:)]) { 347 | 348 | CGFloat calcheight = 0.0; 349 | int row=0; 350 | 351 | while (calcheight < height) { 352 | calcheight += [gridViewDelegate gridView:self heightForRow:row]; 353 | row++; 354 | } 355 | 356 | if (calcheight > height) { 357 | return row-1; 358 | } else { 359 | return row; 360 | } 361 | 362 | } else { 363 | 364 | return (NSUInteger)(height / JOGRIDVIEW_DEFAULT_ROW_HEIGHT); 365 | 366 | } 367 | } 368 | 369 | -(CGFloat)heightRelativeToOriginForRow:(NSUInteger)row { 370 | 371 | // returns the height for the row accurate to its full height from the 372 | // origin to the end of the row. 373 | 374 | if ([gridViewDelegate respondsToSelector:@selector(gridView:heightForRow:)]) { 375 | CGFloat height = 0.0; 376 | 377 | for (NSUInteger i=0;i= __firstWarpedInRow) && (indexPath.section <= __lastWarpedInRow)) { 426 | cell = [[__visibleRows objectAtIndex:(indexPath.section - __firstWarpedInRow)] objectAtIndex:indexPath.row]; 427 | } else { 428 | cell = [self dataSourceCellAtIndexPath:indexPath]; 429 | } 430 | 431 | return cell; 432 | } 433 | 434 | 435 | #pragma mark - 436 | #pragma mark Scrolling to Cells 437 | 438 | -(void)scrollToRow:(NSUInteger)row animated:(BOOL)animated { 439 | if (row < __rows) { 440 | CGFloat heightForRow = [self heightRelativeToOriginForRow:row]; 441 | [self scrollRectToVisible:CGRectMake(0, heightForRow, self.frame.size.width, [self delegateHeightForRow:row]) animated:animated]; 442 | } 443 | } 444 | 445 | -(void)scrollToIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated { 446 | [self scrollToRow:indexPath.section animated:animated]; 447 | } 448 | 449 | 450 | #pragma mark - 451 | #pragma mark Reloading stuff 452 | 453 | -(void)reloadData { 454 | 455 | if ([gridViewDataSource conformsToProtocol:@protocol(JOGridViewDataSource)]) { 456 | 457 | __rows = [gridViewDataSource rowsForGridView:self]; 458 | __columns = [gridViewDataSource columnsForGridView:self]; 459 | 460 | // gather total height 461 | CGFloat totalHeight = 0.0; 462 | 463 | if ([gridViewDelegate respondsToSelector:@selector(gridView:heightForRow:)]) { 464 | for (NSUInteger i=0;i<__rows;i++) { 465 | totalHeight += [gridViewDelegate gridView:self heightForRow:i]; 466 | } 467 | } else { 468 | totalHeight = __rows * JOGRIDVIEW_DEFAULT_ROW_HEIGHT; 469 | } 470 | 471 | self.contentSize = CGSizeMake(self.frame.size.width, totalHeight); 472 | 473 | [self buildCells]; 474 | 475 | } else { 476 | NSLog(@"Y U NO CONFIRM TO PROPER REQUIRED PROTOCOL?"); 477 | 478 | } 479 | 480 | } 481 | 482 | -(void)reloadCellAtIndexPath:(NSIndexPath *)indexPath { 483 | if ((indexPath.section >= __firstWarpedInRow) && (indexPath.section <= __lastWarpedInRow)) { 484 | NSArray *cellsForVisibleRow = [__visibleRows objectAtIndex:indexPath.section - __firstWarpedInRow]; 485 | JOGridViewCell *cellToRefresh = [cellsForVisibleRow objectAtIndex:indexPath.row]; 486 | 487 | [self enqueueReusableCell:cellToRefresh]; 488 | 489 | [self cellForlayoutAtIndexPath:indexPath atHeight:[self heightRelativeToOriginForRow:indexPath.row]]; 490 | } 491 | 492 | } 493 | 494 | -(void)reloadRow:(NSUInteger)row { 495 | if ((row >= __firstWarpedInRow) && (row <= __lastWarpedInRow)) { 496 | [self enqueueReusableCellsForRow:row]; 497 | [self cellsForLayoutOfRow:row atHeight:[self heightRelativeToOriginForRow:row]]; 498 | } 499 | } 500 | 501 | 502 | #pragma mark - 503 | #pragma mark Reusable Views 504 | 505 | -(void)conserveMemory:(NSNotification *)notification { 506 | 507 | NSArray *reusuableCells = nil; 508 | 509 | for (id key in [__reusableViews keyEnumerator]) { 510 | reusuableCells = [__reusableViews objectForKey:key]; 511 | 512 | for (JOGridViewCell *cell in reusuableCells) { 513 | [cell removeFromSuperview]; 514 | } 515 | } 516 | 517 | [__reusableViews removeAllObjects]; 518 | } 519 | 520 | -(JOGridViewCell *)dequeueReusableCellWithIdenitifer:(NSString *)identifier { 521 | 522 | NSMutableArray *stack = [__reusableViews objectForKey:identifier]; 523 | 524 | if ([stack count] > 0) { 525 | 526 | JOGridViewCell *view = [stack objectAtIndex:0]; 527 | [stack removeObjectAtIndex:0]; 528 | 529 | return view; 530 | } else { 531 | return nil; 532 | } 533 | } 534 | 535 | -(void)enqueueReusableCellsForRow:(NSUInteger)row { 536 | if ((row >= __firstWarpedInRow) && (row <= __lastWarpedInRow)) { 537 | NSArray *rowToEnqueu = [__visibleRows objectAtIndex:row - __firstWarpedInRow]; 538 | 539 | for (JOGridViewCell *cell in rowToEnqueu) { 540 | [self enqueueReusableCell:cell]; 541 | } 542 | } 543 | } 544 | 545 | -(void)enqueueReusableCell:(JOGridViewCell *)cell { 546 | 547 | cell.frame = CGRectMake(-CGFLOAT_MAX, -CGFLOAT_MAX, 0, 0); 548 | 549 | if (cell.reuseIdentifier) { 550 | if ([__reusableViews objectForKey:cell.reuseIdentifier]) { 551 | [[__reusableViews objectForKey:cell.reuseIdentifier] addObject:cell]; 552 | 553 | } else { 554 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:0]; 555 | [array addObject:cell]; 556 | [__reusableViews setObject:array forKey:cell.reuseIdentifier]; 557 | } 558 | 559 | } else { 560 | [cell removeFromSuperview]; 561 | } 562 | } 563 | 564 | 565 | #pragma mark - 566 | #pragma mark Delegate/Datasource Methods 567 | 568 | // inspired by Peter Steinberger's article at 569 | // http://petersteinberger.com/2011/09/fast-and-elegant-delegation-in-objective-c/ 570 | // keeps things clean 571 | 572 | -(CGFloat)delegateHeightForRow:(NSUInteger)row { 573 | if ([gridViewDelegate respondsToSelector:@selector(gridView:heightForRow:)]) { 574 | return [gridViewDelegate gridView:self heightForRow:row]; 575 | } else { 576 | return JOGRIDVIEW_DEFAULT_ROW_HEIGHT; 577 | } 578 | } 579 | 580 | -(JOGridViewCell *)dataSourceCellAtIndexPath:(NSIndexPath *)indexPath { 581 | if ([gridViewDataSource respondsToSelector:@selector(cellForGridView:atIndexPath:)]) { 582 | return [gridViewDataSource cellForGridView:self atIndexPath:indexPath]; 583 | } else { 584 | return nil; 585 | } 586 | } 587 | 588 | -(void)delegateWillDisplayCell:(JOGridViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { 589 | if ([gridViewDelegate respondsToSelector:@selector(willDisplayCell:forGridView:atIndexPath:)]) { 590 | [gridViewDelegate willDisplayCell:cell forGridView:self atIndexPath:indexPath]; 591 | } 592 | } 593 | 594 | 595 | @end 596 | -------------------------------------------------------------------------------- /JOGridViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // JOGridViewCell.h 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JOGridViewCell : UIView { 12 | NSString* __reuseIdentifier; 13 | UILabel* textLabel; 14 | } 15 | @property (readonly) UILabel *textLabel; 16 | @property (nonatomic, copy) NSString *reuseIdentifier; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /JOGridViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // JOGridViewCell.m 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "JOGridViewCell.h" 10 | 11 | @implementation JOGridViewCell 12 | @synthesize reuseIdentifier = __reuseIdentifier; 13 | @synthesize textLabel; 14 | 15 | - (id)initWithFrame:(CGRect)frame 16 | { 17 | if ((self = [super initWithFrame:frame])) { 18 | // Initialization code 19 | self.clipsToBounds = YES; 20 | 21 | textLabel = [[UILabel alloc] initWithFrame:frame]; 22 | textLabel.backgroundColor = [UIColor clearColor]; 23 | textLabel.textColor = [UIColor blackColor]; 24 | textLabel.textAlignment = UITextAlignmentCenter; 25 | textLabel.text = @""; 26 | 27 | [self addSubview:textLabel]; 28 | 29 | } 30 | return self; 31 | } 32 | 33 | -(void) dealloc { 34 | [__reuseIdentifier release], __reuseIdentifier = nil; 35 | [textLabel release], textLabel = nil; 36 | [super dealloc]; 37 | } 38 | 39 | -(void)layoutSubviews { 40 | textLabel.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JOGridView 2 | ========== 3 | 4 | This is a grid view based on the concepts that UITableView set forth to provide. 5 | 6 | - A cell is one portion of a grid 7 | - Cells are bare now, need more features to pack it. 8 | - Cells are re-usable 9 | - The grid is defined by rows and columns 10 | - Rows have individually settable heights 11 | - Column widths are limited to the equal distribution amongst all columns in a row 12 | - There is no selection at this moment. 13 | - It automatically purges reusable cells when there is a memory constrain 14 | 15 | TODO: 16 | ===== 17 | - clean up first before laying out to conserve on memory 18 | - consistent delegate/datasource method signatures 19 | - cellforrowatindexpath in JOGridView (wtf is it really for?) 20 | - reflow cells? 21 | - heightforrowatindexpath doesn't work before cache values are not done right when heightforrow is set dynamically (should cache using assoc storage) -------------------------------------------------------------------------------- /gridview.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3FBB3E541417055F00588168 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FBB3E531417055F00588168 /* UIKit.framework */; }; 11 | 3FBB3E561417055F00588168 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FBB3E551417055F00588168 /* Foundation.framework */; }; 12 | 3FBB3E581417055F00588168 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FBB3E571417055F00588168 /* CoreGraphics.framework */; }; 13 | 3FBB3E5E1417055F00588168 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3FBB3E5C1417055F00588168 /* InfoPlist.strings */; }; 14 | 3FBB3E601417055F00588168 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FBB3E5F1417055F00588168 /* main.m */; }; 15 | 3FBB3E641417055F00588168 /* gridviewAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FBB3E631417055F00588168 /* gridviewAppDelegate.m */; }; 16 | 3FBB3E671417055F00588168 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3FBB3E651417055F00588168 /* MainWindow.xib */; }; 17 | 3FBB3E6F1417058700588168 /* JOGridView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FBB3E6E1417058700588168 /* JOGridView.m */; }; 18 | 3FBB3E7514172B0100588168 /* JOGridViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FBB3E7414172B0100588168 /* JOGridViewCell.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 3FBB3E4F1417055F00588168 /* gridview.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = gridview.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 3FBB3E531417055F00588168 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 24 | 3FBB3E551417055F00588168 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 25 | 3FBB3E571417055F00588168 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 26 | 3FBB3E5B1417055F00588168 /* gridview-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "gridview-Info.plist"; sourceTree = ""; }; 27 | 3FBB3E5D1417055F00588168 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 28 | 3FBB3E5F1417055F00588168 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 3FBB3E611417055F00588168 /* gridview-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "gridview-Prefix.pch"; sourceTree = ""; }; 30 | 3FBB3E621417055F00588168 /* gridviewAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = gridviewAppDelegate.h; sourceTree = ""; }; 31 | 3FBB3E631417055F00588168 /* gridviewAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = gridviewAppDelegate.m; sourceTree = ""; }; 32 | 3FBB3E661417055F00588168 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainWindow.xib; sourceTree = ""; }; 33 | 3FBB3E6D1417058700588168 /* JOGridView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JOGridView.h; sourceTree = ""; }; 34 | 3FBB3E6E1417058700588168 /* JOGridView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JOGridView.m; sourceTree = ""; }; 35 | 3FBB3E7314172B0100588168 /* JOGridViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JOGridViewCell.h; sourceTree = ""; }; 36 | 3FBB3E7414172B0100588168 /* JOGridViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JOGridViewCell.m; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 3FBB3E4C1417055F00588168 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 3FBB3E541417055F00588168 /* UIKit.framework in Frameworks */, 45 | 3FBB3E561417055F00588168 /* Foundation.framework in Frameworks */, 46 | 3FBB3E581417055F00588168 /* CoreGraphics.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | /* End PBXFrameworksBuildPhase section */ 51 | 52 | /* Begin PBXGroup section */ 53 | 3FBB3E441417055F00588168 = { 54 | isa = PBXGroup; 55 | children = ( 56 | 3FBB3E7314172B0100588168 /* JOGridViewCell.h */, 57 | 3FBB3E7414172B0100588168 /* JOGridViewCell.m */, 58 | 3FBB3E6D1417058700588168 /* JOGridView.h */, 59 | 3FBB3E6E1417058700588168 /* JOGridView.m */, 60 | 3FBB3E591417055F00588168 /* gridview */, 61 | 3FBB3E521417055F00588168 /* Frameworks */, 62 | 3FBB3E501417055F00588168 /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 3FBB3E501417055F00588168 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 3FBB3E4F1417055F00588168 /* gridview.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 3FBB3E521417055F00588168 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3FBB3E531417055F00588168 /* UIKit.framework */, 78 | 3FBB3E551417055F00588168 /* Foundation.framework */, 79 | 3FBB3E571417055F00588168 /* CoreGraphics.framework */, 80 | ); 81 | name = Frameworks; 82 | sourceTree = ""; 83 | }; 84 | 3FBB3E591417055F00588168 /* gridview */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 3FBB3E621417055F00588168 /* gridviewAppDelegate.h */, 88 | 3FBB3E631417055F00588168 /* gridviewAppDelegate.m */, 89 | 3FBB3E651417055F00588168 /* MainWindow.xib */, 90 | 3FBB3E5A1417055F00588168 /* Supporting Files */, 91 | ); 92 | path = gridview; 93 | sourceTree = ""; 94 | }; 95 | 3FBB3E5A1417055F00588168 /* Supporting Files */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 3FBB3E5B1417055F00588168 /* gridview-Info.plist */, 99 | 3FBB3E5C1417055F00588168 /* InfoPlist.strings */, 100 | 3FBB3E5F1417055F00588168 /* main.m */, 101 | 3FBB3E611417055F00588168 /* gridview-Prefix.pch */, 102 | ); 103 | name = "Supporting Files"; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | 3FBB3E4E1417055F00588168 /* gridview */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = 3FBB3E6A1417055F00588168 /* Build configuration list for PBXNativeTarget "gridview" */; 112 | buildPhases = ( 113 | 3FBB3E4B1417055F00588168 /* Sources */, 114 | 3FBB3E4C1417055F00588168 /* Frameworks */, 115 | 3FBB3E4D1417055F00588168 /* Resources */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = gridview; 122 | productName = gridview; 123 | productReference = 3FBB3E4F1417055F00588168 /* gridview.app */; 124 | productType = "com.apple.product-type.application"; 125 | }; 126 | /* End PBXNativeTarget section */ 127 | 128 | /* Begin PBXProject section */ 129 | 3FBB3E461417055F00588168 /* Project object */ = { 130 | isa = PBXProject; 131 | buildConfigurationList = 3FBB3E491417055F00588168 /* Build configuration list for PBXProject "gridview" */; 132 | compatibilityVersion = "Xcode 3.2"; 133 | developmentRegion = English; 134 | hasScannedForEncodings = 0; 135 | knownRegions = ( 136 | en, 137 | ); 138 | mainGroup = 3FBB3E441417055F00588168; 139 | productRefGroup = 3FBB3E501417055F00588168 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 3FBB3E4E1417055F00588168 /* gridview */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 3FBB3E4D1417055F00588168 /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 3FBB3E5E1417055F00588168 /* InfoPlist.strings in Resources */, 154 | 3FBB3E671417055F00588168 /* MainWindow.xib in Resources */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | /* End PBXResourcesBuildPhase section */ 159 | 160 | /* Begin PBXSourcesBuildPhase section */ 161 | 3FBB3E4B1417055F00588168 /* Sources */ = { 162 | isa = PBXSourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 3FBB3E601417055F00588168 /* main.m in Sources */, 166 | 3FBB3E641417055F00588168 /* gridviewAppDelegate.m in Sources */, 167 | 3FBB3E6F1417058700588168 /* JOGridView.m in Sources */, 168 | 3FBB3E7514172B0100588168 /* JOGridViewCell.m in Sources */, 169 | ); 170 | runOnlyForDeploymentPostprocessing = 0; 171 | }; 172 | /* End PBXSourcesBuildPhase section */ 173 | 174 | /* Begin PBXVariantGroup section */ 175 | 3FBB3E5C1417055F00588168 /* InfoPlist.strings */ = { 176 | isa = PBXVariantGroup; 177 | children = ( 178 | 3FBB3E5D1417055F00588168 /* en */, 179 | ); 180 | name = InfoPlist.strings; 181 | sourceTree = ""; 182 | }; 183 | 3FBB3E651417055F00588168 /* MainWindow.xib */ = { 184 | isa = PBXVariantGroup; 185 | children = ( 186 | 3FBB3E661417055F00588168 /* en */, 187 | ); 188 | name = MainWindow.xib; 189 | sourceTree = ""; 190 | }; 191 | /* End PBXVariantGroup section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | 3FBB3E681417055F00588168 /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 199 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 200 | COPY_PHASE_STRIP = NO; 201 | GCC_C_LANGUAGE_STANDARD = gnu99; 202 | GCC_DYNAMIC_NO_PIC = NO; 203 | GCC_OPTIMIZATION_LEVEL = 0; 204 | GCC_PREPROCESSOR_DEFINITIONS = ( 205 | "DEBUG=1", 206 | "$(inherited)", 207 | ); 208 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 209 | GCC_VERSION = com.apple.compilers.llvmgcc42; 210 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 211 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 212 | GCC_WARN_UNUSED_VARIABLE = YES; 213 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 214 | SDKROOT = iphoneos; 215 | }; 216 | name = Debug; 217 | }; 218 | 3FBB3E691417055F00588168 /* Release */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | ALWAYS_SEARCH_USER_PATHS = NO; 222 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 223 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 224 | COPY_PHASE_STRIP = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu99; 226 | GCC_VERSION = com.apple.compilers.llvmgcc42; 227 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 229 | GCC_WARN_UNUSED_VARIABLE = YES; 230 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 231 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 232 | SDKROOT = iphoneos; 233 | VALIDATE_PRODUCT = YES; 234 | }; 235 | name = Release; 236 | }; 237 | 3FBB3E6B1417055F00588168 /* Debug */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | GCC_C_LANGUAGE_STANDARD = c99; 241 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 242 | GCC_PREFIX_HEADER = "gridview/gridview-Prefix.pch"; 243 | INFOPLIST_FILE = "gridview/gridview-Info.plist"; 244 | PRODUCT_NAME = "$(TARGET_NAME)"; 245 | WRAPPER_EXTENSION = app; 246 | }; 247 | name = Debug; 248 | }; 249 | 3FBB3E6C1417055F00588168 /* Release */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | GCC_C_LANGUAGE_STANDARD = c99; 253 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 254 | GCC_PREFIX_HEADER = "gridview/gridview-Prefix.pch"; 255 | INFOPLIST_FILE = "gridview/gridview-Info.plist"; 256 | PRODUCT_NAME = "$(TARGET_NAME)"; 257 | WRAPPER_EXTENSION = app; 258 | }; 259 | name = Release; 260 | }; 261 | /* End XCBuildConfiguration section */ 262 | 263 | /* Begin XCConfigurationList section */ 264 | 3FBB3E491417055F00588168 /* Build configuration list for PBXProject "gridview" */ = { 265 | isa = XCConfigurationList; 266 | buildConfigurations = ( 267 | 3FBB3E681417055F00588168 /* Debug */, 268 | 3FBB3E691417055F00588168 /* Release */, 269 | ); 270 | defaultConfigurationIsVisible = 0; 271 | defaultConfigurationName = Release; 272 | }; 273 | 3FBB3E6A1417055F00588168 /* Build configuration list for PBXNativeTarget "gridview" */ = { 274 | isa = XCConfigurationList; 275 | buildConfigurations = ( 276 | 3FBB3E6B1417055F00588168 /* Debug */, 277 | 3FBB3E6C1417055F00588168 /* Release */, 278 | ); 279 | defaultConfigurationIsVisible = 0; 280 | defaultConfigurationName = Release; 281 | }; 282 | /* End XCConfigurationList section */ 283 | }; 284 | rootObject = 3FBB3E461417055F00588168 /* Project object */; 285 | } 286 | -------------------------------------------------------------------------------- /gridview.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /gridview/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /gridview/en.lproj/MainWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 800 5 | 10D540 6 | 760 7 | 1038.29 8 | 460.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 81 12 | 13 | 14 | YES 15 | 16 | 17 | 18 | YES 19 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 20 | 21 | 22 | YES 23 | 24 | YES 25 | 26 | 27 | YES 28 | 29 | 30 | 31 | YES 32 | 33 | IBFilesOwner 34 | IBCocoaTouchFramework 35 | 36 | 37 | IBFirstResponder 38 | IBCocoaTouchFramework 39 | 40 | 41 | IBCocoaTouchFramework 42 | 43 | 44 | 45 | 1316 46 | 47 | {320, 480} 48 | 49 | 50 | 1 51 | MSAxIDEAA 52 | 53 | NO 54 | NO 55 | 56 | IBCocoaTouchFramework 57 | YES 58 | 59 | 60 | 61 | 62 | YES 63 | 64 | 65 | delegate 66 | 67 | 68 | 69 | 4 70 | 71 | 72 | 73 | window 74 | 75 | 76 | 77 | 5 78 | 79 | 80 | 81 | 82 | YES 83 | 84 | 0 85 | 86 | 87 | 88 | 89 | 90 | 2 91 | 92 | 93 | YES 94 | 95 | 96 | 97 | 98 | -1 99 | 100 | 101 | File's Owner 102 | 103 | 104 | 3 105 | 106 | 107 | 108 | 109 | -2 110 | 111 | 112 | 113 | 114 | 115 | 116 | YES 117 | 118 | YES 119 | -1.CustomClassName 120 | -2.CustomClassName 121 | 2.IBAttributePlaceholdersKey 122 | 2.IBEditorWindowLastContentRect 123 | 2.IBPluginDependency 124 | 3.CustomClassName 125 | 3.IBPluginDependency 126 | 127 | 128 | YES 129 | UIApplication 130 | UIResponder 131 | 132 | YES 133 | 134 | 135 | YES 136 | 137 | 138 | {{198, 376}, {320, 480}} 139 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 140 | gridviewAppDelegate 141 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 142 | 143 | 144 | 145 | YES 146 | 147 | 148 | YES 149 | 150 | 151 | 152 | 153 | YES 154 | 155 | 156 | YES 157 | 158 | 159 | 160 | 9 161 | 162 | 163 | 164 | YES 165 | 166 | gridviewAppDelegate 167 | NSObject 168 | 169 | window 170 | UIWindow 171 | 172 | 173 | IBProjectSource 174 | gridviewAppDelegate.h 175 | 176 | 177 | 178 | gridviewAppDelegate 179 | NSObject 180 | 181 | IBUserSource 182 | 183 | 184 | 185 | 186 | 187 | 0 188 | IBCocoaTouchFramework 189 | 190 | com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 191 | 192 | 193 | YES 194 | gridview.xcodeproj 195 | 3 196 | 81 197 | 198 | 199 | -------------------------------------------------------------------------------- /gridview/gridview-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | net.ornyx.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | NSMainNibFile 30 | MainWindow 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /gridview/gridview-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'gridview' target in the 'gridview' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_3_0 8 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /gridview/gridviewAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // gridviewAppDelegate.h 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JOGridView.h" 11 | 12 | @interface gridviewAppDelegate : NSObject 13 | 14 | @property (nonatomic, retain) IBOutlet UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /gridview/gridviewAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // gridviewAppDelegate.m 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import "gridviewAppDelegate.h" 10 | 11 | @implementation gridviewAppDelegate 12 | 13 | @synthesize window = _window; 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | // Override point for customization after application launch. 18 | [self.window makeKeyAndVisible]; 19 | 20 | [[UIApplication sharedApplication] setStatusBarHidden:YES]; 21 | 22 | JOGridView *gridview = [[JOGridView alloc] initWithFrame:CGRectMake(0, 0, self.window.frame.size.width, self.window.frame.size.height)]; 23 | [self.window addSubview:gridview]; 24 | gridview.debug = YES; 25 | gridview.delegate = self; 26 | gridview.datasource = self; 27 | 28 | [gridview release]; 29 | 30 | return YES; 31 | } 32 | 33 | - (void)applicationWillResignActive:(UIApplication *)application 34 | { 35 | /* 36 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 37 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 38 | */ 39 | } 40 | 41 | - (void)applicationDidEnterBackground:(UIApplication *)application 42 | { 43 | /* 44 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 45 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 46 | */ 47 | } 48 | 49 | - (void)applicationWillEnterForeground:(UIApplication *)application 50 | { 51 | /* 52 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 53 | */ 54 | } 55 | 56 | - (void)applicationDidBecomeActive:(UIApplication *)application 57 | { 58 | /* 59 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 60 | */ 61 | } 62 | 63 | - (void)applicationWillTerminate:(UIApplication *)application 64 | { 65 | /* 66 | Called when the application is about to terminate. 67 | Save data if appropriate. 68 | See also applicationDidEnterBackground:. 69 | */ 70 | } 71 | 72 | #pragma mark - 73 | #pragma mark JOGridView Stuff 74 | 75 | -(NSUInteger)rowsForGridView:(JOGridView *)gridView { 76 | return 50; 77 | } 78 | 79 | -(NSUInteger)columnsForGridView:(JOGridView *)gridView { 80 | return 5; 81 | } 82 | 83 | 84 | -(CGFloat)gridView:(JOGridView *)gridview heightForRow:(NSUInteger)row { 85 | return 64.0; 86 | } 87 | 88 | 89 | -(JOGridViewCell *)cellForGridView:(JOGridView *)gridView atIndexPath:(NSIndexPath *)indexPath { 90 | static NSString *identifier = @"cell"; 91 | 92 | JOGridViewCell *cell = [gridView dequeueReusableCellWithIdenitifer:identifier]; 93 | 94 | if (!cell) { 95 | cell = [[[JOGridViewCell alloc] init] autorelease]; 96 | cell.reuseIdentifier = @"cell"; 97 | } 98 | 99 | return cell; 100 | } 101 | 102 | + (UIColor *) randomColor 103 | { 104 | // stolen from http://kwigbo.com/post/680994703/iphone-sdk-random-uicolor 105 | // because i was lazy to write my own 106 | 107 | CGFloat red = (CGFloat)random()/(CGFloat)RAND_MAX; 108 | CGFloat blue = (CGFloat)random()/(CGFloat)RAND_MAX; 109 | CGFloat green = (CGFloat)random()/(CGFloat)RAND_MAX; 110 | return [UIColor colorWithRed:red green:green blue:blue alpha:1.0]; 111 | } 112 | 113 | -(void)willDisplayCell:(JOGridViewCell *)cell forGridView:(JOGridView *)gridView atIndexPath:(NSIndexPath *)indexPath { 114 | cell.textLabel.text = [NSString stringWithFormat:@"%i:%i", indexPath.section, indexPath.row]; 115 | cell.textLabel.backgroundColor = [gridviewAppDelegate randomColor]; 116 | } 117 | 118 | 119 | - (void)dealloc 120 | { 121 | [_window release]; 122 | [super dealloc]; 123 | } 124 | 125 | @end 126 | -------------------------------------------------------------------------------- /gridview/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // gridview 4 | // 5 | // Created by Jeremy Foo on 9/7/11. 6 | // Copyright 2011 __MyCompanyName__. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 14 | int retVal = UIApplicationMain(argc, argv, nil, nil); 15 | [pool release]; 16 | return retVal; 17 | } 18 | --------------------------------------------------------------------------------