Draw text faster with CGContextShowTextAtPoint
Luckily, this part was easier to figure out. To get the game out the door, I used text files for the data. With some time to step back and get the game cleaned up, I changed the puzzle metadata over from flat files to SQLite and Core Data. This has had a small impact on load time--around 2 seconds. Not that significant for just 10 puzzles, but in my profiling, and I seeing a significant increase as the number of puzzles increases. So my update for this should be coming out soon.
But what about the main page load time? Again, I performed some analysis to determine what was the bottleneck. I had assumed loading the page data from files again was the problem. So I started commenting out code and testing. Sure enough, the puzzle data loaded very quickly from a single file. That wasn't it.
Next I tried changing my looping logic, changed from a dynamically allocated NSMutableArray to an array I preallocated to the correct size, and even changed my short-cutting logic to decide whether to write out a letter or not. None of these were the culprit.
But then I commented out my writing the letters to the screen with NSString drawInRect. Immediately, even my largest puzzles (85x85) loaded in a second. So I set out to find a more efficient way to write out text. As I learned more, I saw that drawInRect is really trying to do a lot for you--like using the output area of the text for alignment and clipping. I am just trying to write out a single letter, so this was all unnecessary overhead.
If you are doing something like this, try CGContextShowTextAtPoint. This is a much more high-performance function that is not trying to align the text or perform clipping--it is just writing out the letters the way you tell it to. For me, this was perfect, and much more efficient. I noticed load time on even the largest puzzles was much more efficient, around 2 seconds or so, down from as many as 5.
This is also good news since I plan to release an iPad version of FindIt. Sure, the processor is going to be faster, but I also plan to display a lot more on a page. Considering that, I wanted to be sure to have the efficiency tweaked for optimal performance.
Hopefully this helps you too! Good luck with your iPhone and iPad development!
Expected specifier-qualifier-list before 'NSManagedObjectModel' error and how to fix it
So, I was happily cutting and pasting code between iPhoneCoreDataRecipes and my iPhone app (the premium version of FindIt, if you must know) when I ran into the following error everywhere:
Expected specifier-qualifier-list before 'NSManagedObjectModel'
The object changed (i.e. it was not always 'NSManagedObjectModel') but the error was consistent. And yet I was copying everything over from the .m and .h files in the Apple example. Strange.
My first thought was checking that I had included the Core Data framework in the app. Yup. That wasn't it.
Then I realized--it was the global prefix file. I went and checked out the Apple example, and sure enough, Core Data was there. I just needed to through this into my
Hope this helps you too!
My concerns with Quattro Wireless
That is, until lately. I have noticed my fill ratio drop significantly, reaching between 20-30% in recent weeks, and hitting an abysmal 10% average for the last 3 days. I am sure Quattro has a lot of requests, with the huge growth in iPhone apps lately, and the unprecedented downloading that has been going on. But 10%??!?!? These guys are paid to provide a service, and I was not very happy with the previous 20-30% range.
I really hope Apple buying Quattro can turn this kind of marked decline around. But I am not waiting to see what happens. Over the weekend, I converted my apps over to use the MobClix provider, which promises 100% fill rate--presumably caching ads and presenting them when a new one is not available from the provider.
I am actually pretty impressed with MobClix. I used them for one of my apps, and I am slowly starting to convert over to them for other apps as well. Their framework is small, integration was simple, and I have noticed a significant reduction in application errors from users that are running MobClix over other providers. If you get a chance, check them out. If nothing else, the promise of 100% fill rate has really caught my attention!
Default image not showing up for iPhone app
So if you don't see your Default.png image showing up, try these steps:
1) First, make sure your "Default.png" is named exactly that ("default.png" will not work).
2) Second, make sure Default.png is in the root directory of your project.
3) Do a clean of your target.
4) Then build and your default image should show up.
I also noticed that running this in the simulator was not helpful because my app started very quickly and the default image only displayed for a second. So, make sure you give this a try on the device to see how long the default image really displays.
Good luck!
iPhone / Objective-C Code Examples (and .NET equivalents)
1) Use NSLog liberally to debug as you write your code. You can really do a lot with it. A few standard statements are for printing strings or numbers, and you can also print an Objective-C object. Here are the three I use most often:
NSLog(@"%@", aString); // prints any NSString, for example
NSLog(@"%d", anInt); // prints a signed int
NSLog(@"%f", aFloat); // prints a float
2) If you have a UITableView that you need to have updated, you can reload it with this command (assuming it is named "tableView"):
[self.tableView reloadData];
3) Need to iterate through a collection? Try this handy code snippet, which is similar to For Each in .NET languages:
for (id currentObject in allObjects) {
// do something here with currentObject
}
4) Here is how you can check to see if an object is certain type. This is a bit like using gettype or typeof in .NET to find a specific type:
if ([myObject isKindOfClass:[TheClassIAmLookingFor class]]) {
}
5) Here is how you cast an object in Objective-C, like using CType or DirectCast in .NET:
TypeToCastTo typeCastObject = (TypeToCastTo *)myObject;
6) I show alert pop-ups a lot in my apps. Here is a simple example for an alert launched when a button is clicked, and responded to to perform some action.
-(void)resetClicked:(id)sender withEvent: (UIEvent *) event {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Reset" message:@"Are you sure you want to reset this puzzle?\nAll of your work will be lost!" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
[alert show];
[alert release];
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked the OK button
if (buttonIndex == 1)
{
// reset logic goes here
}
}
7) Split is a great function in .NET. Here is the Objective-C equivalent:
NSArray *arrayOfValues = [commaDelimitedListOfValues componentsSeparatedByString:@","];
More code snippets next time! Good luck!
iPhone Blogs That I Follow
iPhone Developer/Development Blogs | Travis Dunn - Imagine my surprise when the internet failed to produce a select list of iPhone development blogs more than 10 links wide. Well, I've beaten the internet at its own game and collected the links myself, like a neurotic SERP.
A More In Depth Look At The How To Make An iPhone App Book - ... where you should have no trouble figuring out how to use those libraries on your own. You can get more information on the How To Make An iPhone App homepage by clicking here. ©2009 iPhone Development Tutorials and Programming Tips. ...
creating a calander - iPhone Dev SDK Forum - I want to create a calendar. which is can be scrolled vertically and contains dates of the next seven weeks. I am new to iphone development therefore.
iPhone Game Development With Core Animation | iPhone Development ... - I've made quite a few posts about OpenGL ES, and the available game engines on the iPhone, and I'd like to iterate that you do not need to use OpenGL ES or.
FurdLog » Messing With Developer Infrastructure Conventions - By creating games and other programs for the iPhone, software developers hoped to find millions of new customers. But they didn't expect to feel muzzled. The software development kit that Apple Inc. distributed to programmers bound them ...
WordPress for iPhone + Source Code Available! < A Fool's Wisdom - Earlier this week my Automattic colleagues and Effigent released WordPress for iPhone and iPod Touch. Now, the source is also available and Trac is set up for reporting bugs and participating in development! ...
The iPhone App Store is Pure Genius - Apple's iPhone App Store allows third-parties to release software for the iPhone (built with an API provided by Apple) to all iPhone users. They can charge what they want for the app, and delivery and payment is managed beautifully within the iPhone interface through your iTunes account. By providing the medium (iPhone) and the delivery service (App Store), Apple has created a completely self-sustaining business model that generates passive income for them and increases demand for the product with each awesome release on the App Store.
Top 15 iPhone Web Development Resources - Here is a list of links to the best available iPhone development resources such as simulators, development plugins, wikis, and other JavaScript/HTML/Safari documentation. iPhoney – A free iPhone web simulator for designers. ...
How Much Is Too Much For An iPhone App? » Webomatica ... - Just tossing this out there, since I have come down with a case of iPhone app price sensitivity.
Using the UITableView, Part 1
If you are a novice, you may still be learning to get a view transition to occur when a UITableItem is clicked. Here is code that you need in the tableView:didSelectRowAtIndexPath: method. This assumes you have a NIB named "PageTwo" and that its class is named pageTwo.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
pageTwo *anotherViewController = [[pageTwo alloc] initWithNibName:@"PageTwo" bundle:nil];
[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}
Now you can transition to a new page, any new page, when a record in the UITableView is selected. Note that this can be animated, so you immediately get the great sliding animation effects standard in all iPhone apps.
Consider adding buttons to the Navigation bar at the top. I found some complicated examples of this, but Apple provided some good direction to keep this simple. This is all I needed to do to provide a button on the right side of a Navigation bar. I put this code in my viewDidLoad method:
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithTitle:@"Button" style:UIBarButtonItemStyleBordered target:self action:@selector(myFunction:)];
self.navigationItem.rightBarButtonItem = addButton;
[addButton release];
For my next trick, I will demonstrate how to use your controller class as a delegate for the UITableView, so your class can respond to calls from the UITableView, allowing you to populate the class on the fly. See you next time!
John
Load and Parse a File on the iPhone
Anyway, this means that I had to be able to read in the file once I got it over to XCode. Luckily, like most things, there are an impressive amount of examples to do the load....assuming you don't need to parse the results and pass them into a custom object.
The format of my file is pretty simple: I am using an identifier for a node which is a string, then I am using 3 floating point values for the x-, y-, and z-coordinates of a point in 3-dimensional space. So I need to be able to read in the rows from the file, and parse out the results that I can use to create these custom nodes.
It was pretty tedious getting there, because the main method to parse out delimited strings,
componentsSeparatedByString
, returns types that you might not expect. Coming from a non-C background, I was slowed down by having to convert each node to something to be able to use it. I am a .NET guy...I expect some implicit conversion!!Hopefully this bit of code helps you. This will load in all the lines in your string, parse out the values, then create a custom object for each one. Some of the supporting code is left out, so you can see the meat of the example; I also converted this to loading ints instead of floats because it made the code a little simpler. Enjoy!
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"nodeOutputFile" ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:filePath encoding:NSASCIIStringEncoding error:NULL];
if (fileContents) {
NSArray *lines = [fileContents componentsSeparatedByString:@"\n"];
for (int i = 0; i < [lines count]; i++) {
JTNode *newNode = [JTNode alloc];
NSArray *tokens = [[lines objectAtIndex:i] componentsSeparatedByString:@","];
NSString *identifier = [tokens objectAtIndex:0];
NSNumber *x = [NSNumber numberWithInt:[[tokens objectAtIndex:1] integerValue]];
NSNumber *y = [NSNumber numberWithInt:[[tokens objectAtIndex:2] integerValue]];
NSNumber *z = [NSNumber numberWithInt:[[tokens objectAtIndex:3] integerValue]];
[newNode createWithX:[x integerValue]
Y:[y integerValue]
Z:[z integerValue]
Identifier:identifier];
// do something here with newNode
}
}
Good luck to you!
John Tabernik
Put AdWhirl Ads into Your iPhone App
When I decided to release a lite ad-supported version of my app, I searched around to find out about ad providers. Luckily, in my search, I happened upon AdWhirl. AdWhirl is not actually an ad provider--instead, they provide an interface to other ad provider networks, allowing you to add one ad API into your app, and get 8 ad providers instantly (including the ability to put in your own ads).
AdWhirl has outstanding step-by-step instructions on their site here. I was trying to incorporate Quattro Wireless into my app and was having some issues, not the least of which was the lack of cut-and-paste of example code. I was searching on example code when I found AdWhirl, which includes Quattro Wireless as a provider. And AdWhirl made it easy for me to set up my ads, so it was a no brainer. [Update--I have received word from Quattro Wireless that they do provide cut and paste code in their developer wiki. My mistake--I guess I did not look in the right place.]
But now, let's look at some code. For my sliding tile game, I used the cocos2d framework. I was worried this would make it hard to incorporate in AdWhirl, but it was no problem at all! Take the steps in the instructions document above, then do a few simple steps to incorporate AdWhirl into your app:
First, I recommend adding a few constants into a common header file:
#define PUBLISHER_ID @"12345678901234567890"
#define SITE_ID @"MyApplication"
#define ADWHIRLAPPKEY @"1234567890abcdef1234567890"
These are the constants needed by the AdWhirl interface.
Include these variables in your scene header file:
ARRollerView *rollerView;
BOOL isPaused;
Next, I went into my main Scene file and put the following at the end of the init method:
rollerView = [ARRollerView requestRollerViewWithDelegate:self];
[ARRollerView startPreFetchingConfigurationDataWithDelegate:self];
rollerView.center = CGPointMake(160, 454);
UIView *myView = [[Director sharedDirector] openGLView];
[myView addSubview:rollerView];
isPaused = FALSE;
This code will begin prefetching data for ads. Also, you will notice I specifically placed the ads at the bottom of the view--that took some trial and error to get it to line up perfectly. isPaused is going to be the notifier to pause the game timer while an ad would be displayed if the user clicks on one during game play.
Now, your scene will need to provide a method to return your app key:
- (NSString*)adWhirlApplicationKey
{
return ADWHIRLAPPKEY; //Return your AdWhirl application key here
}
Also, add these two methods to pause and unpause the game if you have a game timer:
- (void)willDisplayWebViewCanvas {
isPaused = TRUE;
}
- (void)didDismissWebViewCanvas {
isPaused = FALSE;
}
You will also need to add some code in your timer method to pause timing if isPaused is true. For example, I have a tick event, which I bail out of immediately if isPaused is true.
I was also getting bunch of errors until I added a special line in the dealloc, to set the ad delegate to nil before you release it. So don't forget this part!!
-(void)dealloc{
if (rollerView) {
[rollerView setDelegateToNil];
[rollerView release];
}
.
.
.
And that's it! Hopefully using these few code snippets, you can avoid some of the learning I went through and get ads into your apps in less than an hour.
Obviously, you also need to go through the process on the AdWhirl web site to set up the ad providers, etc, but this is pretty simple. There can be some lag time for some of the providers waiting for codes and things to be emailed to you, but it does not take much of your time to submit the requests.
I hope this is a pretty complete overview. If you have questions, please feel free to send me a note. Good luck!!