2012年10月28日星期日

Creating an iPad flip-clock with Core Animation

Creating an iPad flip-clock with Core Animation:
flipclock_oneAs part of the sliding puzzle game I’m developing for the iPhone and iPad (well, I can’t survive on the profits from BattleFingers forever), I looked for a way to represent a numeric score and time display in an interesting way. One of the nicer visual effects you could use for this is the “flip-card clock” style, where each number consists of a top and bottom part, and the top part flips down to reveal the next number. It’s been used in a few other places including the home screen in the HTC Diamond device, and its physical, realistic style fits well with the iPad, so I set about creating a version for the iPhone and iPad using the built-in Core Animation library. Read on for more details.



I started by thinking about the motions that would be required for the animation. We can break it down as three elements, the top, the bottom and the flipping part. Each of these parts can be represented as a Core Animation Layer (a CALayer). We create them using the class method on CALayer, there’s no additional retain here, as the _toplayer property is retained.

_toplayer = [CALayer layer];

Splitting images

Now we’ve got the layers, we need something to but into them. I didn’t want to create lots of pre-processed images for the top and bottom sections of each number, so instead I wrote a routine to split the images in code, drawing each half into an image context and grabbing a UIImage from it. This is then stored in an array for later use. We can get any of the elements from the array and set it as the content of the layer as required.

// The size of each part is half the height of the whole image
CGSize size = CGSizeMake([img size].width, [img size].height/2);
 
// Create image-based graphics context for top half
UIGraphicsBeginImageContext(size);
 
// Draw into context, bottom half is cropped off
[img drawAtPoint:CGPointMake(0.0,0.0)];
// Grab the current contents of the context as a UIImage 
// and add it to our array
UIImage *top = [UIGraphicsGetImageFromCurrentImageContext() retain];   
[_imagesTop addObject:(id)top];
 
// Now draw the image starting half way down, to get the bottom half
[img drawAtPoint:CGPointMake(0.0,-[img size].height/2)];
// And store that image in the array too
UIImage *bottom = [UIGraphicsGetImageFromCurrentImageContext() retain];   
[_imagesBottom addObject:(id)bottom];
 
UIGraphicsEndImageContext();

Now our _imagesTop and _imagesBottom arrays contains all the images we need, let’s get ‘em moving.

Applying animation


The axes and anchor point of a CALayer
The axes and anchor point of a CALayer
The animation we need to apply to the ‘flipping’ part is a rotation around the bottom of the layer, at the join between the two halves. The default location of the anchor point is the middle of the layer, which would mean the piece would rotate around the middle, but we can easily change it to instead be at the middle in the bottom (for our purposes it could be anywhere on the bottom, as we’re only interested in x-axis rotation):

_fliplayer.anchorPoint = CGPointMake(0.5, 1.0);

I use the CATransform3DMakeRotation function to create a transform around the X axis using the vector [1,0,0]. For the first part of the motion, we go from 0 radians to pi/2 radians (0 to 90 degrees), using an explicit CABasicAnimation object:

CABasicAnimation *topAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
topAnim.duration=0.25;
topAnim.repeatCount=1;
topAnim.fromValue= [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0, 1, 0, 0)];
float f = -M_PI/2;
topAnim.toValue=[NSValue valueWithCATransform3D:CATransform3DMakeRotation(f, 1, 0, 0)];
topAnim.delegate = self;
topAnim.removedOnCompletion = FALSE;
[_fliplayer addAnimation:topAnim forKey:[NSString stringWithUTF8String:k_TopAnimKey]];

Drawing layer content

Unfortunately a CALayer doesn’t have a “back”, so creating the flip layer is not as easy as it could be. We need to add a step in the process to change the image displayed by the flip layer half way through its fall. We can do this in a variety of ways, I’ve chosen to set a delegate object to perform the drawing of the layer contents:

[_fliplayer setDelegate:_layerHelper];

This means our delegate object (_layerHelper) has its drawLayer method called whenever the layer needs to display its contents. Be wary though, this may not be as often as you think! Core graphics aggressively caches the contents of the layer, and will only call your delegate when absolutely necessary. You can force it to happen by calling setNeedsDisplay, which invalidates the contents of the layer:

[_fliplayer setNeedsDisplay];

I encapsulated this in the helper object itself, by providing a custom implementation of the isTop property setter that calls setNeedsDisplay. At least that way callers don’t have to be aware of this requirement.
The drawLayer method itself simply draws one of two images into the context, depending on whether it’s in ‘top’ mode. A slight further complication is that when drawing the bottom part, the image needs to be inverted to display upside down. This is achieved by applying a translation and scale to the context before drawing the image:

CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0f, r.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);
 
CGContextDrawImage(context, r, [_imgTop CGImage]);
 
CGContextRestoreGState(context);

Avoiding implicit animation

I’ve tried to avoid using implicit animation here, instead using explicit construction of CABasicAnimation objects, because we need some control of when things happen, and the order in which they happen. Normally, it would be easier to just set some property of the view and let the OS manage animating it. Many properties have default transition that are applied whenever they’re changed, so it takes some additional effort to make them change immediately.
In our example we change the contents of the top half and show the flip layer, but we need that to happen immediately so we disable actions within an explicit transaction that we then commit. This gives us the control we need, and avoids the default behaviour of the transition occurring when the message loop next runs.

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
    forKey:kCATransactionDisableActions];
_toplayer.contents = (id)[[_imagesTop objectAtIndex:[self getNextIndex]] CGImage];
_fliplayer.hidden = NO;
[CATransaction commit];

Getting some perspective

Although the images I’ve used to illustrate this post are isometric, what we really want is a front-on perspective view, so that the falling piece seems to stick out. For this we have to set a sublayerTransform on the view’s inherent layer, by setting an individual element on a transform:

CATransform3D aTransform = CATransform3DIdentity;
float zDistance = 1000;
aTransform.m34 = 1.0 / -zDistance; 
[self layer].sublayerTransform = aTransform;

This gives us a much more realistic look.

Putting it together

Wow, so now we’ve got some appropriately sliced images, some layers and some animations. Let’s put them together to get the final effect.


The animation states (last is same as first)
The animation states (last is same as first)


I’ve used a very simple state machine that moves through 3 states; TopToMiddle, MiddleToBottom, ResetForNext. We can use the animationDidStop delegate as the state transition point. We don’t have to bother determining exactly which animation finished, because we do the same thing (change state) regardless.

- (void)animationDidStop:(CAAnimation *)oldAnimation finished:(BOOL)flag
{
 [self changeState];
}

  • TopToMiddle: show flip layer, and start rotation through 90 degrees
  • MiddleToBottom: change image on flip layer, start rotation through further 90 degrees
  • ResetForNext: change displayed top and bottom layer contents and hide flip layer
Let’s see how it looks in action:

2012年10月8日星期一

Getting symbolicate stack call in logs

Getting symbolicate stack call in logs:
In last version of Xcode we can’t see full crash log.
For resolving this issue we can use next solution:

1. add function to your main class
1
2
3
4
5
void uncaughtExceptionHandler(NSException *exception) {

    NSLog(@"CRASH: %@", exception);

    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);

    // Internal error reporting

}
2. And last thing
1
2
3
4
5
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{  

    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

    // Normal launch stuff

}
Or you can use commands
if you use GDB
1
(gdb) info line *0x2658
or
1
(gdb) bt
And One more Beautiful solution
1. Open the breakpoint navigation in XCode 4 (This looks like a rectangle with a point on the right side)

2. Press the ‘+’ button at the bottom left and add an ‘Exception Breakpoint’. Ensure you break ‘On Throw’ for ‘All’ exceptions.
Now you should get a full backtrace immediately prior to this exception occurring. This should allow you to at least zero in on where this exception is being thrown.
source discussion on stackoverflow one more source
The post Getting symbolicate stack call in logs appeared first on Notes of a Developer.

Debugging Core Data Objects

Debugging Core Data Objects:
If you’re working on an app that uses Core Data, it’s inevitable that you’ll end up in the debugger and need to dig around in the object graph. You’ll also quickly realize that Core Data’s -description of an object isn’t terribly helpful:
(lldb) po myListObj
(List *) $19 = 0x08054ac0 <List: 0x8054ac0> (entity: List; id: 0x8074280 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/List/p175> ; data: <fault>)
Core Data’s documentation is excellent, but surprisingly doesn’t cover some of the tricks you can use to examine managed objects in the debugger.
The first trick is to fire a fault on the object using -willAccessValueForKey. After that, you can see what’s really there:
(lldb) po [myListObj willAccessValueForKey:nil]
(id) $20 = 0x08054ac0 <List: 0x8054ac0> (entity: List; id: 0x8074280 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/List/p175> ; data: {
    containerId = "8E4652D3-5516-4186-B1C9-DDBE41E108CF";
    createdAt = "2009-10-08 15:17:53 +0000";
    itemContainers = "<relationship fault: 0x8020850 'itemContainers'>";
})
You might also be surprised when you try to access one of the properties of the object:
(lldb) po myListObj.containerId
error: property 'containerId' not found on object of type 'List *'
error: 1 errors parsing expression
Remember that these properties are defined as @dynamic and there’s a lot of work done by Core Data at runtime to provide the implementation. The solution here is to use the KVC accessor -valueForKey: to get the object’s value:
(lldb) po [myListObj valueForKey:@"containerId"]
(id) $26 = 0x08069b60 8E4652D3-5516-4186-B1C9-DDBE41E108CF
Often, you’ll want to examine the relationships between objects. As you can see in the output above, the attribute itemContainers, which is a to-many relationship, is a fault. To fire the fault, get all the objects from the set:
(lldb) po [[myListObj valueForKey:@"itemContainers"] allObjects]
(id) $23 = 0x0d0667b0 <__NSArrayI 0xd0667b0>(
<Container: 0x807a180> (entity: Container; id: 0x801bc50 <x-coredata://29B10357-0723-4950-9EB6-E6D7AD6269B9/Container/p1> ; data: {
    containerId = TestContainer;
    items = "<relationship fault: 0x805b100 'items'>";
    state = "(...not nil..)";
    type = 4;
})
)
Finally, you may be using Transformable attribute types. The state attribute above is an example. If you’d like more information than (...not nil...), use the KVC getter and you’ll see that it’s an empty NSDictionary:
(lldb) po [[[[myListObj valueForKey:@"itemContainers"] allObjects] lastObject] valueForKey:@"state"]
(id) $29 = 0x0807dd30 {
}

(lldb) po [[[[[myListObj valueForKey:@"itemContainers"] allObjects] lastObject] valueForKey:@"state"] class]
(id) $30 = 0x01978e0c __NSCFDictionary
It’s taken me months to learn these tricks, so hopefully this short essay helps you come up to speed more quickly than I did!

Second Set of TouchTargets Code Published

Second Set of TouchTargets Code Published:

All of the code needed to scan in, draw, and manage text strings from font sheets is in and has been published. Chapter 4: Say “Hello, World!”, OpenGL starts us off with a walkthrough of what we want to accomplish with the new font scanner, and how it will work conceptually.
Chapter 5: Building the Importer walks us through the actual code involved in creating the importer.
After that, in Chapter 6: Using the Importer, we think through what our text string class needs to be able to do to work well with the new importer class. We also start to think about coordinate systems and another class to manage all of our text strings.
Chapter 7: Creating the Text Class takes us on a long journey through the creation of the EDTextString class, a complicated but versatile class that allows us to take the data created by the font importer class and actually create and manipulate OpenGL texture-based dynamic text strings.
At the end of Chapter 7, we find that we still have a few pieces missing from our text rendering system, and finish up the effort with the final class, the EDTextStringManager. Chapter 8: Managing Our Text Strings walks through the creation of the final text handling class, and even takes time out to create a new utility class to contain our texture loading code, EDOpenGLTools.
At last, Chapter 9: Finally, OpenGL Says “Hello, World!”, and More takes all of our hard work from chapters 4 through 8 and creates an iPhone “Hello, World!” demo application.
With this set of code, we’re well on our way to finishing the TouchTargets application. All that’s left are the targets, which we’ll tackle in the next set of chapters, the background, and a scoring mechanism.