diff --git a/Classes/AQGridView.m b/Classes/AQGridView.m index 134a109..3199c73 100755 --- a/Classes/AQGridView.m +++ b/Classes/AQGridView.m @@ -1146,8 +1146,15 @@ - (void) _selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated - (void) selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated scrollPosition: (AQGridViewScrollPosition) scrollPosition { + if( index > [self.dataSource numberOfItemsInGridView:self] ) + { + NSLog(@"[%@] ERROR: attempted to select item at out-of-bounds index = %i", [self class], index ); + } + else + { [self _selectItemAtIndex: index animated: animated scrollPosition: scrollPosition notifyDelegate: NO numFingersTouch: 1]; + } } - (void) deselectItemAtIndex: (NSUInteger) index animated: (BOOL) animated @@ -1205,10 +1212,12 @@ - (void) setSeparatorColor: (UIColor *) color _separatorColor = color; +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE for ( AQGridViewCell * cell in _visibleCells ) { cell.separatorColor = _separatorColor; } +#endif } #pragma mark - @@ -1230,6 +1239,9 @@ - (UIView *) _basicHitTest: (CGPoint) point withEvent: (UIEvent *) event - (BOOL) _canSelectItemContainingHitView: (UIView *) hitView { + if( hitView == self ) + return TRUE; + if ( [hitView isKindOfClass: [UIControl class]] ) return ( NO ); @@ -1317,7 +1329,7 @@ - (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event _flags.ignoreTouchSelect = ([self isDragging] ? 1 : 0); UITouch * touch = [touches anyObject]; - _touchBeganPosition = [touch locationInView: nil]; + _touchBeganPosition = [touch locationInView: self]; if ( (touch != nil) && (_pendingSelectionIndex == NSNotFound) ) { CGPoint pt = [touch locationInView: self]; diff --git a/Classes/AQGridViewCell+AQGridViewCellPrivate.h b/Classes/AQGridViewCell+AQGridViewCellPrivate.h old mode 100644 new mode 100755 index 627f64b..a8f0628 --- a/Classes/AQGridViewCell+AQGridViewCellPrivate.h +++ b/Classes/AQGridViewCell+AQGridViewCellPrivate.h @@ -45,7 +45,6 @@ typedef NSUInteger AQGridViewCellSeparatorEdge; @interface AQGridViewCell (AQGridViewCellPrivate) -@property (nonatomic, retain) UIColor * separatorColor; @property (nonatomic, assign) AQGridViewCellSeparatorStyle separatorStyle; @property (nonatomic, assign) AQGridViewCellSeparatorEdge separatorEdge; diff --git a/Classes/AQGridViewCell.h b/Classes/AQGridViewCell.h old mode 100644 new mode 100755 index e23f978..a42b51c --- a/Classes/AQGridViewCell.h +++ b/Classes/AQGridViewCell.h @@ -54,16 +54,9 @@ typedef enum { #endif } AQGridViewCellSelectionStyle; -@interface AQGridViewCell : UIView +@interface AQGridViewCell : UITableViewCell { - NSString * _reuseIdentifier; - UIView * _contentView; - UIView * _backgroundView; - UIView * _selectedBackgroundView; - UIView * _selectedOverlayView; - CGFloat _selectionFadeDuration; - UIColor * _backgroundColor; - UIColor * _separatorColor; + UIColor * _AQseparatorColor; UIColor * _selectionGlowColor; CGFloat _selectionGlowShadowRadius; UIView * _bottomSeparatorView; @@ -91,19 +84,11 @@ typedef enum { - (id) initWithFrame: (CGRect) frame reuseIdentifier: (NSString *) reuseIdentifier; -// If you want to customize cells by simply adding additional views, you should add them to the content view so they will be positioned appropriately as the cell transitions into and out of editing mode. -@property (nonatomic, readonly, retain) UIView * contentView; -// default is nil. The background view will be added as a subview behind all other views -@property (nonatomic, retain) UIView * backgroundView; - -// The 'selectedBackgroundView' will be added as a subview directly above the backgroundView if not nil, or behind all other views. It is added as a subview only when the cell is selected. Calling -setSelected:animated: will cause the 'selectedBackgroundView' to animate in and out with an alpha fade. -@property (nonatomic, retain) UIView * selectedBackgroundView; - -@property (nonatomic, readonly, copy) NSString * reuseIdentifier; - (void) prepareForReuse; // if the cell is reusable (has a reuse identifier), this is called just before the cell is returned from the grid view method dequeueReusableCellWithIdentifier:. If you override, you MUST call super. -@property (nonatomic) AQGridViewCellSelectionStyle selectionStyle; // default is AQGridViewCellSelectionStyleGlow +@property (nonatomic,retain) UIColor * AQseparatorColor; +@property (nonatomic) AQGridViewCellSelectionStyle AQselectionStyle; // default is AQGridViewCellSelectionStyleGlow @property (nonatomic, getter=isSelected) BOOL selected; // default is NO @property (nonatomic, getter=isHighlighted) BOOL highlighted; // default is NO @property (nonatomic, retain) UIColor * selectionGlowColor; // default is dark grey, ignored if selectionStyle != AQGridViewCellSelectionStyleGlow diff --git a/Classes/AQGridViewCell.m b/Classes/AQGridViewCell.m old mode 100644 new mode 100755 index 7cc9b68..080d2ce --- a/Classes/AQGridViewCell.m +++ b/Classes/AQGridViewCell.m @@ -34,6 +34,8 @@ * */ +#define AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE 0 + #import "AQGridViewCell.h" #import "AQGridViewCell+AQGridViewCellPrivate.h" #import "UIColor+AQGridView.h" @@ -45,24 +47,21 @@ #endif @interface AQGridViewCell () -@property (nonatomic, retain) UIView * contentView; -@property (nonatomic, copy) NSString * reuseIdentifier; - (void) flipHighlightTimerFired: (NSTimer *) timer; @end @implementation AQGridViewCell -@synthesize contentView=_contentView, backgroundView=_backgroundView, selectedBackgroundView=_selectedBackgroundView; -@synthesize reuseIdentifier=_reuseIdentifier, selectionGlowColor=_selectionGlowColor; +@synthesize AQseparatorColor=_AQseparatorColor; +@synthesize selectionGlowColor=_selectionGlowColor; @synthesize selectionGlowShadowRadius=_selectionGlowShadowRadius; - (id) initWithFrame: (CGRect) frame reuseIdentifier: (NSString *) reuseIdentifier { - self = [super initWithFrame: frame]; + self = [super initWithFrame: frame reuseIdentifier:reuseIdentifier]; if ( self == nil ) return ( nil ); - self.reuseIdentifier = reuseIdentifier; _cellFlags.usingDefaultSelectedBackgroundView = 1; _cellFlags.separatorStyle = AQGridViewCellSeparatorStyleEmptySpace; @@ -118,32 +117,17 @@ - (NSComparisonResult) compareOriginAgainstCell: (AQGridViewCell *) otherCell return ( NSOrderedSame ); } -- (UIView *) contentView -{ - if ( _contentView == nil ) - { - _contentView = [[UIView alloc] initWithFrame: self.bounds]; - _contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; - _contentView.autoresizesSubviews = YES; - self.autoresizesSubviews = YES; - _contentView.backgroundColor = [UIColor whiteColor]; - [_contentView.layer setValue: [NSNumber numberWithBool: YES] forKey: @"KoboHackInterestingLayer"]; - [self addSubview: _contentView]; - } - return ( _contentView ); -} - - (CALayer *) glowSelectionLayer { - return ( _contentView.layer ); + return ( self.contentView.layer ); } -- (AQGridViewCellSelectionStyle) selectionStyle +- (AQGridViewCellSelectionStyle) AQselectionStyle { return ( _cellFlags.selectionStyle ); } -- (void) setSelectionStyle: (AQGridViewCellSelectionStyle) style +- (void) setAQselectionStyle: (AQGridViewCellSelectionStyle) style { if ( (style == AQGridViewCellSelectionStyleGlow) && ([CALayer instancesRespondToSelector: @selector(shadowPath)] == NO) ) style = AQGridViewCellSelectionStyleGray; @@ -286,9 +270,10 @@ - (void) resetHighlightForSubviewsOfView: (UIView *) aView } } +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE - (void) _beginBackgroundHighlight: (BOOL) highlightOn animated: (BOOL) animated { - if ( (_cellFlags.usingDefaultSelectedBackgroundView == 1) && (_selectedBackgroundView == nil) ) + if ( (_cellFlags.usingDefaultSelectedBackgroundView == 1) && (self.selectedBackgroundView == nil) ) { NSString *imageName = nil; #ifdef BUILTIN_IMAGES @@ -400,6 +385,7 @@ - (void) _beginBackgroundHighlight: (BOOL) highlightOn animated: (BOOL) animated [self resetHighlightForSubviewsOfView: self]; } } +#endif - (void) flipHighlightTimerFired: (NSTimer *) timer { @@ -425,7 +411,9 @@ - (void) highlightAnimationStopped: (NSString * __unused) animationID context: ( [UIView setAnimationsEnabled: YES]; _cellFlags.highlighted = 0; +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE [_selectedBackgroundView removeFromSuperview]; +#endif CFDictionaryRemoveAllValues( _selectionColorInfo ); } @@ -463,7 +451,9 @@ - (void) setHighlighted: (BOOL) value animated: (BOOL) animated case AQGridViewCellSelectionStyleGreen: case AQGridViewCellSelectionStyleRed: { +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE [self _beginBackgroundHighlight: value animated: animated]; +#endif break; } @@ -508,6 +498,7 @@ - (void) setHighlighted: (BOOL) value animated: (BOOL) animated [self highlightAnimationStopped: @"" context: NULL]; } +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE - (void) setBackgroundView: (UIView *) aView { if ( aView == _backgroundView ) @@ -532,6 +523,7 @@ - (void) setBackgroundView: (UIView *) aView _backgroundView.frame = bgFrame; [self insertSubview: _backgroundView atIndex: 0]; } +#endif - (void) layoutSubviews { @@ -551,7 +543,7 @@ - (void) layoutSubviews [self insertSubview: _bottomSeparatorView atIndex: 0]; } - _bottomSeparatorView.backgroundColor = (_separatorColor == nil ? [UIColor AQDefaultGridCellSeparatorColor] : _separatorColor); + _bottomSeparatorView.backgroundColor = ( self.AQseparatorColor == nil ? [UIColor AQDefaultGridCellSeparatorColor] : self.AQseparatorColor); cFrame.size.height -= 1.0; } @@ -570,7 +562,7 @@ - (void) layoutSubviews [self insertSubview: _rightSeparatorView atIndex: 0]; } - _rightSeparatorView.backgroundColor = (_separatorColor == nil ? [UIColor AQDefaultGridCellSeparatorColor] : _separatorColor); + _rightSeparatorView.backgroundColor = ( self.AQseparatorColor == nil ? [UIColor AQDefaultGridCellSeparatorColor] : self.AQseparatorColor); cFrame.size.width -= 1.0; } @@ -593,6 +585,7 @@ - (void) setSelectionGlowColor: (UIColor *) aColor _cellFlags.selectionGlowColorSet = (aColor == nil ? 0 : 1); } +#if AQ_SOURCE_BREAKS_APPLE_LIBRARIES_SO_WE_REMOVE_THE_CRAP_THAT_SHOULDNT_BE_THERE - (void) setSelectedBackgroundView: (UIView *) aView { if ( aView == _selectedBackgroundView ) @@ -619,6 +612,7 @@ - (void) setSelectedBackgroundView: (UIView *) aView _cellFlags.usingDefaultSelectedBackgroundView = (aView == nil ? 1 : 0); } +#endif - (void) prepareForReuse { @@ -644,21 +638,6 @@ - (void)setEditing:(BOOL)editing animated:(BOOL)animated @implementation AQGridViewCell (AQGridViewCellPrivate) -- (UIColor *) separatorColor -{ - return ( _separatorColor ); -} - -- (void) setSeparatorColor: (UIColor *) color -{ - if ( _separatorColor == color ) - return; - - _separatorColor = color; - - _bottomSeparatorView.backgroundColor = _separatorColor; - _rightSeparatorView.backgroundColor = _separatorColor; -} - (AQGridViewCellSeparatorStyle) separatorStyle { diff --git a/README.textile b/README.textile index abd26cb..1453472 100644 --- a/README.textile +++ b/README.textile @@ -5,111 +5,9 @@ h3=. Winner of the _Best Developer Tool/Helper_ award at iPadDevCamp 2010 in San p=. Version 1.2 -- 10 January 2011 p=. By "Jim Dovey":mailto:jimdovey@mac.com
-Originally written for the "Kobo iPad Application":http://itunes.apple.com/ca/app/ebooks-by-kobo/id301259483?mt=8 -h3. Used In These Applications: +...but the original author hasn't updated in more than a year, with 100+ stranded forks on GitHub. -* "eBooks by Kobo":http://itunes.apple.com/ca/app/ebooks-by-kobo/id301259483?mt=8 -- Bookshelf and multi-column table views, 'I'm Reading' overlay view.
-* "Netflix Actors":http://itunes.apple.com/us/app/actors-for-netflix/id377007136?mt=8 (Source code "here":http://github.com/adrianco/Actors-for-Netflix-on-iPad) -- Movie and actor chooser views.
-* "Stocks - The Finance App":http://itunes.apple.com/ca/app/stocks-the-finance-app/id373066200?mt=8 -- Portfolio view contents.
-* "Rivet for iPad":http://itunes.apple.com/ca/app/rivet-for-ipad/id375055319?mt=8 -- Media shelves. -* "Slide By Slide":http://itunes.apple.com/us/app/slide-by-slide/id387580384?mt=8 -- Slideshow picker. -* "AppStart for iPad":http://itunes.apple.com/ca/app/appstart-for-ipad/id408984648 -- App reviews and guides. -* "Slippy":http://itunes.apple.com/app/id408254506 -- Level chooser. -* "Equineline Sales Catalog":http://itunes.apple.com/us/app/equineline-sales-catalog/id440355734?mt=8&ls=1 -- Bookshelf view. -* "Completion":http://www.completionapp.com -- Springboard-style home screen. +This fork is - like all the others - probably just fixing basic bugs that have been fixed a hundred times already, butwe all have to re-invent the wheel. -h3. Supporting AQGridView - -People have asked how they can show their support. Other than implementing nice features & pushing them back upstream, you can always take a look at my "Amazon Wish List":http://amzn.com/w/34WILFNNUUDKQ. - -h2. Introduction - -*AQGridView* is an attempt to create something similar to *NSCollectionView* on the iPhone. If *CALayoutManager* were available on the iPhone, specifically the *CAConstraintLayoutManager*, then this would be relatively easy to put together. However, since none of those exist, there's a lot of work to be done. - -AQGridView is based around the programming model of *UITableView* and its associated classes. To create this class I looked long and hard at how UITableView does what it does, and attempted to replicate it as closely as possible. This means that if you are familiar with table view programming on the iPhone or iPad, you will find AQGridView simple to pick up. - -h3. Similarities with UITableView - -* A subclass of *UIScrollView*. -* Reusable grid cells, similar to *UITableViewCell*. -* Data source and delegate very similar to those used with UITableView. -* Immediate and batched changes to the content list (insert, remove, reorder, reload). -* Similar change animations (top, bottom, left, right, fade). -* Simple *AQGridViewController* provided which performs grid view setup for you, similar to *UITableViewController*. -* Support for custom header and footer views. - -h3. Differences from UITableView - -* No sections-- uses NSUInteger as its index location rather than NSIndexPath. -* Data source can specify a desired minimum size for all grid cells. -* Cells are not automatically resized to fit in layout grid-- this can be changed via a property. -* The delegate gets an opportunity to adjust the layout frame for each cell as it is displayed. -* The grid layout is adjusted to fit the contentSize width. You can specify left and/or right padding to reach a size which can be divided into three, five, etc. cells per row. -* A customizable 'glow' selection style, which places a glow around a cell's layer (or a specified sublayer) using the *shadowRadius* property of *CALayer*. Note that this is only available in iPhone OS 3.2 or later. - -h3. Requirements - -* The iPhone OS 3.2 SDK is needed to build. It can however run on iPhones running OS 3.0 (iPhone/iPad universal app compatible). - -h3. How to get it - -Download or clone it "from GitHub":http://github.com/AlanQuatermain/AQGridView - -h3. How to use it in your project - -This project compiles to a static library which you can include, or you can just reference the source files directly. Note that there are some resources to copy into your project for the tableview-style selection backgrounds. - -h2. Overview - -AQGridView has a number of supporting internal classes. The ones you'll interact with directly are: - -* AQGridView -* AQGridViewCell -* AQGridViewController - -h3. Basic setup - -Create a subclass of *AQGridViewController*. In -viewDidLoad you can change any properties you desire, add background, header, or footer views, and so on. - -Unless you want a grid cell size of 96x128 (the default) you should implement the *AQGridViewDataSource* method -portraitGridCellSizeForGridView:, from which you can return a suitable _minimum_ size for your cells. This is used as the basis of the layout grid; the grid view will expand the width of this size until it reaches a factor of the current content size, then uses that for its layout. - -Cells will be placed in the center of each layout grid rectangle. By default, the cells are not resized to fill this grid rectangle, but you can change this using the resizesCellWidthToFit property of AQGridView. If your cell should not be positioned dead center (for example, if your cell contains an image with a shadow on one side, and the _image_ should be centered, not the whole thing) then you can implement the *AQGridViewDelegate* method -gridView:adjustCellFrame:withinGridCellFrame: to tweak the auto-centered cell frame calculated by the grid view's layout code. For instance, the "Kobo iPad App":http://itunes.apple.com/ca/app/ebooks-by-kobo-hd/id364742849?mt=8 does this for its shelf view, and uses resizesCellWidthToFit for its two-column list view. - -h2. Future Directions - -* *Section support*. This will need a large amount of refactoring to support moves between sections, and will need a new way of keeping track of visible cell indices (it currently uses an NSRange). -* *High-performance rendering*. If cells don't need to update their content after being drawn, each row could be composited into a single view for reduced load on the CoreAnimation renderer. This would need support in the grid view for displaying these composited rows, and would also need special support in KBGridViewCell to mark individual cells as needing dynamic updates, so they could be skipped when compositing and displayed normally on top of the composited row. KVO would be used to keep track of this. NB: This would also need special handling for the 'glow' selection style (or possibly the tracking cell would always be placed on screen, regardless of its dynamism requirements). -* -*Content adjustments*. There are possibly still a couple of deeply-buried bugs in the cell movement code inside *KBGridViewUpdateInfo*. These are a pain to track down, and the code in that class could possibly use some cleanup. This is also something which would need to change a lot for section support (it makes heavy use of *NSIndexSet* right now).- - -h2. Known Bugs - -* Don't try to pile multiple animations on top of one another. i.e. don't call -beginUpdates on a grid view whose -isAnimatingUpdates method returns YES. Bad things will happen, cells will end up in the wrong places, stacked on top of one another. - -h2. Examples - -All examples are located in the *Examples* folder. - -h3. ImageDemo - -This is the demo which was presented at iPad Dev Camp in San Jose. It is primarily a showcase for automatic content reordering and animation, but also includes examples of the two main cell types: centered empty-space bordered cells, and filled line-separated cells. - -Rotating the display will change the number of columns in the grid, with the associated animation. In addition, there are two buttons which cause items to be reordered. The top left button will shuffle the image order randomly, and the top right button will return everything to its original position. - -The second button at the top right of the screen will pop up a menu which changes from the default empty-space grid style to a UITableView-like 'filled cell' grid style. In this version, the cells are automatically resized to fit their grid slots, and the selection style (and the cells' behavior when selected) will match UITableView more closely. - -h3. SpringBoard - -This is a new demo which shows how to use gesture recognizers to implement a manual reordering interface similar to that used by the iPad springboard. If you tap and hold on a cell for half a second, it will pop out of the grid, whereupon you can drag it around and drop it in a new position by letting go. The rest of the cells move out of the way as you drag your chosen cell around. - -Additionally, it shows how to use the left and right insets to force the grid view to create the desired number of columns. In portrait mode, we want four columns, and the default width of 768 divides by four nicely, so we leave the insets at zero. In landscape mode we want five columns, and so we inset left & right by two pixels each to reduce the grid's _layout_ width to 1020 pixels, which is cleanly divisible by 5. As a result, rotating to landscape mode results in five columns being visible. - -h3. ExpanderDemo - -This demo shows how to make a new grid view instance expand into existence from a single point. The *ExpanderDemoViewController* implements a basic grid view with a single cell. When this cell is tapped, it creates a new instance of *ExpandingGridViewController*, which then expands its image cells into view (only using those cells which would be visible, not ALL cells). - -The expansion is triggered by calling the -expandCellsFromRect:ofView: method of ExpandingGridViewController. Before calling this, you must set the expanding grid view's frame (so it can figure out what cells should be visible at what indices) and add it to a superview (so the passed rectangle can be mapped across). Afterward you should ensure that you call the new controller's -viewDidAppear:. This last function implements the last part of the expansion algorithm. - -In -expandCellsFromRect:ofView:, the controller converts the source rectangle into its own view's coordinate space and caches it. It also goes through the grid view's cell list and stores their existing frames ready to animate them later. Lastly, it sets its view's background colour to clear. Then in -viewDidAppear: it first moves all the visible cells to the saved starting rectangle, then in an animation block it restores its background colour and moves the cells to their original positions. - -Also: Yes, this example contains a memory leak, and doesn't go in reverse. The only exemplary code is in the two methods discussed above. \ No newline at end of file +Welcome to modern Open Source: UNcollaborative development. \ No newline at end of file