- Nice Classes
5 weeks 5 days ago - If you have a lot of table
6 weeks 2 days ago - No bad post, write more
7 weeks 6 days ago - Landscap issue
8 weeks 3 days ago - Landscap issue
8 weeks 3 days ago - Very good sample
9 weeks 1 day ago - very helpful
11 weeks 4 days ago - You are a saviour
12 weeks 2 days ago - Easy Gradient Backgrounds for UITextViewCells - Selected Cell BG
15 weeks 1 day ago - Saving button position when app terminates
17 weeks 4 days ago
iPhone Sample Code: Tiles
As an exercise in using the Core Animation API, I've implemented a little iPhone app that reproduces the behavior of the iPhone home screen's icon reorganization interface. (You know, dragging the wiggly icons around.) You can download my sample code to see how it works. Some descriptions of the highlights follow below.

The source code and Xcode project can be downloaded here: Tiles-v1.0.zip.
(If you're a Mercurial user, you may want to grab it from bitbucket.org.)
The primary classes to look at are TilesViewController and Tile. The view controller implements all of the "logic" of the application, while the Tile class has the animations.
An instance of Tile represents one of the icons, and is derived from CAGradientLayer. The gradient layer properties get set to provide a gloss effect for the tiles. Tile also provides a few animations, initiated by calling these methods:
appearDraggable: Changes the tile to be partially transparent, and makes it slightly bigger. This is invoked when the user touches a tile.appearNormal: Reverses the effects ofappearDraggable. This is invoked when the tile is released.startWiggling: Starts a tile "wiggling", as in the iPhone home screen while in reorganization mode.stopWiggling: Stops the wiggling effect
The TilesViewController class is pretty straightforward. When the user touches a the screen, the touchesBegan method determines which tile was touched, calls its appearDraggable method, and calls other tiles' startWiggling methods.
As the user drags the tile around the screen, the touchesMoved method moves the dragged tile, and moves the other tiles as needed to provide an open space for it. Core Animation takes care of all the zooming around of the icons.
When the user lets go of the tile, the touchesEnded method drops it in place and removes all the animations.
Things I learned from this project:
- Turning on the
masksToBoundsproperty for layers slows things down quite noticeably. - When hit-testing layers, you have to use a layer's presentation layer, not a model layer itself.
CAGradientLayeris easy to use.
Here are some things I don't understand. (Maybe some smart person can explain.)
- When hit-testing to see which layer was touched, I had to do both
[touch locationInView:view]and[view convertPoint:location toView:nil]. However, when handling touch-moves, I only have to use[touch locationInView:view]. I don't understand why the coordinate systems are (apparently) different.
Comments
Saving button position when app terminates
Hi,
It would be interesting to 'save' the position of the buttons so that the next time the app loads, everything is where the user left it.
Any ideas of how you would implement this feature?
UIImage for tiles?
This looks great and I'm going to start experimenting tonight. But perhaps you know the answer before I pull out too much hair. I'd like to modify this to display images (UIImage objects). From a quick look at the code I think I can replace the CAGradientLayers (and not do the gloss manipulation & drawing), but if you have any advice I'd appreciate it.
This is a great example!
I think this is a great example, thanks. Could you provide any insight on using multiple UIViews in interface builder to construct the grid? Similar to the other post "I'd like to modify this to display images (UIImage objects)", but using UIViews. Thanks in advance for any additional help.
Yes, you can create a layer
Yes, you can create a layer and set the
contentsproperty to whatever image you want.Landscap issue
Hello, i have create springborad view like same as , like wiggling,icon arrangement. its working fine when application launch potriate view but when i rotate in Landscan view it display perfectly but frame not set perfectly;
may be portrait and landscape frame are different. i have set Row = 6;Column = 5; in potrait mode and Landscape mode i have set Row = 7;Column = 6;
-(void)GridViewIcon {
[gridView.layer removeFromSuperlayer];
arrayIcon = [[NSMutableArray alloc]init];
NSArray *array = [[NSArray alloc] initWithObjects:@"1.png",@"2.png",@"3.png",.. 26 icon,nil];
arrayIcon = [NSMutableArray arrayWithArray:array];
[array release];
IconCount = [arrayIcon count];
k = 0;
gridView = [[UIView alloc]init];//[gridView setUserInteractionEnabled:NO];
int gridH,gridW;
for (int row = 0; row <Row; ++row)
{
for (int col = 0; col < Column ; ++col)
{
int index = (row * Column) + col;
CGRect frame = CGRectMake(TILE_MARGIN + col * (TILE_MARGIN + TILE_WIDTH), TILE_MARGIN + row * (TILE_MARGIN + TILE_HEIGHT),
TILE_WIDTH, TILE_HEIGHT);
NSString *APPICON = [arrayIcon objectAtIndex:index];
UIImage *aImage = [UIImage imageNamed:APPICON];
WiggleEffect *objWiggle;
tileFrame[index] = frame;
objWiggle = [[WiggleEffect alloc]init];
objWiggle.tileIndex = index;
tileForFrame[index] = objWiggle;
objWiggle.frame = frame;
objWiggle.backgroundColor = [UIColor colorWithPatternImage:aImage].CGColor;
objWiggle.delegate = self;
[gridView.layer addSublayer:objWiggle];
[objWiggle setNeedsDisplay];
[objWiggle release];
k++;
if(k == arrayIcon.count)
{
[self.view addSubview:gridView];
return;
}
gridW = col * (TILE_MARGIN + 90) + 90;
gridH = (row + 1)*(TILE_MARGIN + 90 ) + 90;
}
gridView.frame= CGRectMake(0, 0, gridW, gridH);
gridView.center=self.view.center;
}
[self.view addSubview:gridView];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchStartTime = [event timestamp];//[touches performSelector:@selector(longTap:) withObject:nil afterDelay:1.5];
if(isMoveable)
{
CALayer *hitLayer = [self layerForTouch:[touches anyObject]];
if ([hitLayer isKindOfClass:[WiggleEffect class]])
{
WiggleEffect *tile = (WiggleEffect*)hitLayer;
//NSInteger inte = tile.tileIndex;
heldTile = tile;
touchStartLocation = [[touches anyObject] locationInView:gridView];
heldStartPosition = tile.position;
//titleSelect = tile.tileIndex;
titleSelect = tile.messageTypeId;
heldFrameIndex = [self frameIndexForTileIndex:tile.tileIndex];
[tile moveToFront];
[tile appearDraggable];
}
}
CALayer *hitLayer = [self layerForTouch:[touches anyObject]];
if ([hitLayer isKindOfClass:[WiggleEffect class]])
{
WiggleEffect *tile = (WiggleEffect*)hitLayer;
//titleSelect = tile.tileIndex;
titleSelect = tile.messageTypeId;
}
else
{
titleSelect = 0;
}
}
{
//NSLog(@" Touch touchesMoved at %d",[event timestamp]);
if (heldTile)
{
UITouch *touch = [touches anyObject];
UIView *view = gridView;
CGPoint location = [touch locationInView:view];
[self moveHeldTileToPoint:location];
[self moveUnheldTilesAwayFromPoint:location];
}
}
{
// Calculate how long touch lasted
NSTimeInterval touchTimeDuration = [event timestamp] - touchStartTime;
//NSLog(@" Touch duration: %3.2f seconds",touchTimeDuration);
if(touchTimeDuration == 0.00)
{
if (!isWoggleRunning){
//NSLog(@" Touch Began at %d",touchStartTime);
isWoggleRunning = YES;
isMoveable = YES;
CALayer *hitLayer = [self layerForTouch:[touches anyObject]];
if ([hitLayer isKindOfClass:[WiggleEffect class]])
{
//UIGestureRecognizer *obj;
// obj = [[UILongPressGestureRecognizer alloc]init];
WiggleEffect *tile = (WiggleEffect*)hitLayer;
//NSInteger inte = tile.tileIndex;
heldTile = tile;
touchStartLocation = [[touches anyObject] locationInView:gridView];
heldStartPosition = tile.position;
//titleSelect = tile.tileIndex;
titleSelect = tile.messageTypeId;
heldFrameIndex = [self frameIndexForTileIndex:tile.tileIndex];
[tile moveToFront];
[tile appearDraggable];
NSLog(@"PRESSED AT %d to START WIGGLE",heldFrameIndex);
[self startTilesWiggling];
}
}
else
{
isMoveable = NO;
isWoggleRunning = NO;
[self stopTilesWiggling];
}
}
if (heldTile)
{
[heldTile appearNormal];
heldTile.frame = tileFrame[heldFrameIndex];
heldTile = nil;
}
CALayer hitLayer = [self layerForTouch:[touches anyObject]];
if ([hitLayer isKindOfClass:[WiggleEffect class]])
{
WiggleEffect *tile = (WiggleEffect)hitLayer;
//titleSelect = tile.tileIndex;
titleSelect = tile.messageTypeId;
}
// [self stopTilesWiggling];
}
{
// NSLog(@" Touch touchesCancelled at %d",[event timestamp]);
[self touchesEnded:touches withEvent:event];
}
Landscap issue
I am using like same as but i added extra functionlit, when i run application in potrarite mode it working fine and i get all title and perfect layer touch, but when i change orientation (Landscape) that time not working proper layer. here is the code please help me out. if it possible.
[gridView.layer removeFromSuperlayer];
arrayIcon = [[NSMutableArray alloc]init];
NSArray *array = [[NSArray alloc] initWithObjects:@"1.png",@"2.png",@"3.png",.. 26 icon,nil];
arrayIcon = [NSMutableArray arrayWithArray:array];
[array release];
IconCount = [arrayIcon count];
k = 0;
gridView = [[UIView alloc]init];
//[gridView setUserInteractionEnabled:NO];
int gridH,gridW;
for (int row = 0; row