Sunday, May 31, 2009

Workaround for UIPicker / UIDatePicker landscape bug

There's an odd bug with the UIPicker and UIDatePicker classes on the iPhone when used in a landscape orientation. Rather than rendering as would be expected, the UIDatePicker is shifted to the right by about 50 pixels and the height is wrong. Even worse, if you decide to change the date format to be a count down timer (myPicker.datePickerMode = UIDatePickerModeCountDownTimer), you get a not so nice view like this:



After searching a bit on the various iPhone developer forums, I noticed different people mention that the problem only occurs if your view is already in landscape mode. If the view is first in portrait and you rotate the view to landscape, then the picker is rendered properly during the auto-rotation. This bit of information led me to a simple workaround for getting a properly rendered picker in landscape without having to have the user see a portrait version of the view first.

All you have to do is fake some part of the underlying UI into thinking you are in portrait mode even though your aren't. I have no idea why this works, but it does. I've already wasted too many hours on this annoying bug, so I settled on this hack. (I'm very surprised Apple still has this obvious bug in the SDK when we are coming up on a 3.0 release, but it's there. And yes, people have logged bug reports about it. Ok, enough of my side rant.)

So, how do you do this bit of trickery? Assuming your main app is already in landscape mode, you can do the following in the view controller for the view containing the picker view:


-(void)viewWillAppear: (BOOL)animated {

// Fake the UI rendering to think we are in portrait orientation
[[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated: YES];

}


For some reason, this causes the picker view to render properly even though the view really does remain in landscape mode. My countdown timer picker now looks good and proper:



That's it. Just one line of code and the landscape rendering of a picker is fixed. Again, I have no idea why. Perhaps someone more in touch with the inner workings of Cocoa will point out the reason why this hack works.

As a somewhat humorous aside, while searching (in vain) for a solution to this problem on various iPhone SDK forums, I did run into an odd feature of the official Apple developer forums (currently in beta). I typed in a search for "uipickerview uiinterfaceorientationlandscaperight" and the search came back with no matching results, but Apple was kinda enough to offer this helpful suggestion: "Did you mean: cupcake itinerancies?"

Saturday, May 30, 2009

Robotics and Artificial Life

Today I received an email from someone who enjoys my Artificial Life iPhone app. He included a link to an interesting article about an evolutionary robotics experiment in Switzerland that is somewhat similar in concept to what I am trying to do with my app - start with random behaviors and see what useful behavior evolves over time.

Here's a link to the article: http://www.dailygalaxy.com/my_weblog/2009/05/a-robot-hitler.html

If you like artificial life and AI systems, it's an interesting read.

Friday, May 29, 2009

SoundEngine 2.0.3 update

I've uploaded a newer version of the SoundEngine code - version 2.0.3. This provides a few memory leak fixes courtesy of an email I received from HeeHun Kang.

You can read more about the free library code here. The main point of it is to provide a fairly simple method for playing multiple sound effects and background music in an iPhone app using OpenAL. You are welcome to use this code for free however you'd like.

I'm now using this code in a few apps of mine, so I plan on maintaining it for the foreseeable future. I'll post updates and improvements as I make them.

At some point, if I have the spare cycles, I'd like to rewrite the whole library. Right now, as I've mentioned in the past, it's based on the SoundEngine 1.7 code from Apple and it isn't the best example of coding out there. But, I needed something on short notice so modifying it was my best option at the time.

One tip: you need to have the sound effects in the proper format in order to be usable with this code. The following command will convert an audio CAF file to the necessary format:

afconvert -f caff -d LEI16@44100 originalfile.caf convertedfile.caf

The above assumes you have initialized the sound engine to use 44.1Mhz. If you are using a different frequency, adjust the command accordingly.

Tuesday, May 26, 2009

Artificial Life tip

This question has come up several times regarding the recent updates to the Artificial Life app.

Why do I no longer see population explosions like I did with earlier versions of the app?

The reproduction rate of the protozoa is tied directly to their "desire" meter. Two protozoa are only allowed to mate if their desire is 50% or higher. The default timer controlling the protozoa's life meters was adjusted to allow an individual protozoa's life span to be somewhat longer. As a result, the desire meter now increases at a slower rate.

If you would prefer to have the earlier behavior of fairly rapid population explosions, you can achieve this by adjusting the protozoa's "Max Energy" setting. Choosing a value of 100, rather than the default 200 will achieve the desired effect. Protozoa will be able to reproduce at twice the normal rate, although any one protozoa will have half the normal life span if it does not eat anything and will get hungry at twice the default rate.

In a future update I will add a slider so you can choose the desire level at which a protozoa will be able to reproduce. This way you will have a finer level of control over what is occurring without having to also adjust the life-span of a creature.

Friday, May 22, 2009

Replacing the QuickTime logo with a custom image

IMPORTANT NOTE: As of iPhone OS 3.1, this technique no longer works. As I mentioned in the original post, this technique depended on the internal structure of the QuickTime player view hierarchy and could stop working at any time. And, so it has. I leave this post for historical purposes, but it is no longer accurate or valid. Except maybe as a warning to not depend on the internal structure of Apple's classes.

On an iPhone developer forum, someone recently asked whether it is possible to replace the QuickTime 'QT' logo that appears with the iPhone movie player when playing an audio file or stream in an iPhone app. Several people responded saying it is not possible, even though the YouTube app does it. I respectfully disagree. Presented below is a very simple technique which currently will in fact work, and is a technique I am using in an app already available on iTunes.

If you are curious what the end result looks like, take a look at the Hi Tony app on iTunes (link opens iTunes). It's a free download. [ EDIT: the technique I was originally using only works with 2.x of the iPhone OS. A new update to the Hi Tony! app which will work with 3.0 is pending with Apple.]

If you just want to see the code, it is provided below. It assumes a certain view name ("MPVideoBackgroundView") will exist in the movie player class. There's no guarantee Apple won't change this in the future. Use at your own risk.

I should point out that no hidden APIs or function calls are being used, so in theory Apple should not reject your app if you use this technique. Again, however, use at your own risk. As I've mentioned before Apple's review process can be arbitrary and subjective. Just because my app was approved using this technique does not mean yours will.

But, in the interest of "sharing the knowledge", I figured I should make this technique available to any developer that might want to try it themselves. The function for recursively searching an object for its underlying views was based on a simple example from Erica Sadun. As for finding the magical view name of "MPVideoBackgroundView", this just involved a lot of trial and error on my part examining all the subviews of the movie player.

Please note: This is just a rough example to demonstrate the principle. The code as shown below does not clean up after itself memory-wise.

[EDIT: The following code is a lot different than my original post. It has been rewritten to work with iPhone OS 2.x and 3.0]



- (bool) findVideoBackgroundView: (id) aView level: (int) level
{
NSString *name = [[aView class] description];

if ([name isEqualToString:@"MPVideoBackgroundView"]){
CGRect viewRect = CGRectMake(0, 0, 480, 320);
UIView *temp_view = [[UIImageView alloc] initWithFrame:viewRect];
[temp_view setAlpha:1.0];
[temp_view setBackgroundColor:[UIColor blackColor]];

UIImage *img = [UIImage imageNamed:@"Picture 9.png"];

int x_offset = (480 - img.size.width)/2;
int y_offset = (320 - img.size.height)/2;
CGRect titleRect = CGRectMake(x_offset, y_offset, img.size.width, img.size.height);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:titleRect];
[myImage setImage:img];
[temp_view addSubview:myImage];
[aView insertSubview:temp_view atIndex:0];

return true;
}

for (UIView *subview in [aView subviews]){
if ([self findVideoBackgroundView:subview level:(level + 1)]){
return TRUE;
}
}
return FALSE;
}


- (void) monitorMovieWindow
{
// With iPhone OS 3.0, the movie view does not appear immmediately after calling play.
// It could take several seconds before the view appears.
// As a result, we run this thread in the background to look for when the movie player appears.

// There's probably a better way to do this.

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

bool movieViewNotFound = TRUE;
int maxRetry = 40; // try waiting for the movie view to appear for 4 seconds

while(movieViewNotFound && maxRetry > 0){

// According to Apple's MoviePlayer sample code, the movie player should be the second view in the window stack
NSArray *windows = [[UIApplication sharedApplication] windows];
// Locate the movie player window
UIWindow *moviePlayerWindow = [windows objectAtIndex:1];

if ([self findVideoBackgroundView: moviePlayerWindow level:0]){
movieViewNotFound = FALSE;
}else{
[NSThread sleepForTimeInterval:0.1];
-- maxRetry;
}
}
[pool release];
}


...

// Here's how you would make use of the above methods

// create a new player and initialize it with the movie URL path
MPMoviePlayerController* theMovie=[[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:@"http://blahblah-insert your URL here"]];
[theMovie play];
// Call the method to overlay the QTlogo view with a custom image
[self performSelectorInBackground:@selector(monitorMovieWindow) withObject:nil];


Thursday, May 21, 2009

Artificial Life 1.4 now available

As the subject line reads, Artificial Life 1.4 is now available in the iTunes store! This update is mainly "cosmetic" - no significant changes were made to the behavior of the life forms. However, the app grew in size by about 16 meg! I've added sound effects and 16 minutes of ambient music. Sound effects and music can be toggled on of off per user preference, so if you prefer silent protozoa, that's your choice.

Special thanks to R. Scott Lachance for granting me a license to use his ambient down tempo jazz tracks in the app! The app only includes excerpts of his music that best fit within the context of the app - full length individual tracks from his CD are not present. Check out his music on iTunes if you like what you hear in the the app. It's listed under the artist name DigiTube.

The next update (1.5) is currently under development and will include some other feature suggestions I've received as feedback. Thanks for sending in your ideas!

Wednesday, May 13, 2009

iPhone Developer account renewal

Last night I noticed this message at the top of the screen when logged into Apple's iPhone developer portal (click on the image for a better view):



For those not wanting to click, the text says "Your iPhone Developer Program is set to expire in 60 days. To maintain your application(s) on the App Store and your access to the Program Portal, please renew your membership."

This is an interesting point that answers a question I had been wondering about. Apparently, if you don't renew your Apple iPhone developer account, all your apps are removed from the store! It makes perfect sense and will eliminate the problem of unsupported apps cluttering the store, but it's the first confirmation I've seen that Apple will in fact remove apps of expired developer accounts.

Oddly, it appears Apple currently has a bit of a math problem in their reminder calculation. When I tried to click through and renew my account, I received the following message (again, you can click on the image for a more readable view):



"Thank you for you interest in renewing your iPhone Developer Program membership. You will be able to renew your membership 60 days prior to your expiration date. Log in to the iPhone Developer Program Portal to view the expiration date of your current iPhone Developer Program."

One part of Apple's system thinks my account has less than 60 days left, but the part that lets me renew says I can't renew until I have less than 60 days left. Hmmm... I signed up around mid-July of 2008, so hopefully within the next few days I'll actually be able to renew.

And, in even more strangeness, the intermediate click through page that expounds on the virtues of renewing your account includes a link for Frequently Asked Questions about renewing your membership. Clicking on that brings up a lovely page that says "We're Sorry. The page you have requested can't be found." At the moment, http://developer.apple.com/support/iphone/enrollment/renewal.html is a broken link.

Friday, May 8, 2009

XCode tip: don't use spaces in your project path

I'm not sure if this is documented somewhere, but the XCode debugger does not work properly if you have a space in your project path name. For example, I recently put my Radio Paradise project under Subversion control. When I was re-extracting the project to a work directory, I named the target folder "Radio Paradise".

Whenever I tried to debug my app, I would receive the following error:

warning: Unable to read symbols for ""/Users/Stormy/Development/iPhone/Projects/Radio Paradise/build/Debug-iphoneos"/RadioPlayer.app/RadioPlayer" (file not found).

The path XCode was reporting seemed a bit odd with the double-quoted portion of the string. I discovered if I just renamed the "Radio Paradise" folder to "RadioParadise" and reopened my project, the debugger worked just fine.

Is this documented somewhere? It seems XCode should at least give a better error message than "file not found". Like maybe - "Spaces not allowed in the project path".

I logged a bug with Apple using their reporting tool, but based on the past three bugs I've reported, I don't expect much. Some of my bug reports are almost a year old and Apple has never even acknowledged or changed the status of any them.

Thursday, May 7, 2009

DevDiary: Artificial Life 1.4 in development

I'm making progress with the newest updates to Artificial Life. I now have working sound effects along with a few other tweaks to the app. Below is a video demo of how the sound effects will be used. I'm trying to make them somewhat "pleasant" and tried to select sounds that complimented each other. I'm no musician, so if you think it's terrible, let me know. These aren't necessarily the final sounds I'll use, but they work well for a proof of concept.

The sound effects are produced using my modified SoundEngine code I mention earlier. The code is freely available if you want to use it in your own app.

Along with sound effects, this update will hopefully include some professionally composed ambiant jazz tracks. I'm still working out the details with the artist to use his music, but if all goes well, it will be available. I'm trying to make the sound effects such that they'll blend with the music.

And, if you like your Artificial Life to be quiet, that will remain an option. Each of the sound triggers can be toggled on or off (along with the music tracks), so, for example, if you only want the sound of creatures burning up in the sun, that's your choice.

Artificial Life 1.4 audio demo from Brian Stormont on Vimeo.

Playing multiple sound effects on the iPhone

Looking for a quick and admittedly very dirty solution for playing sound effects in an iPhone app? Then try this free source code. It uses OpenAL for the sound playing and supports mixing of 32 simultaneous sound effects, along with playing one background music track.

PLEASE NOTE: This code was based on Apple's 1.7 SoundEngine code available from the Crash Landing demo supplied with the iPhone SDK 2.0.

Apple has acknowledged significant problems with the original code and has in fact removed the code from all future SDKs. Per the original source code agreement, Apple grants the right to anyone to modify and reuse the software in any manner (see Apple's original licensing agreement in the source files). As such, I have changed the structure of the code to better suit my needs and I am making it available to anyone who might find it useful.

Why use SoundEngine as a base? Isn't oalTouch a better example?

Yes, oalTouch is a much cleaner example, but it makes some API calls that require SDK 2.1. SoundEngine was made for SDK 2.0. I have an app that has already been released on iTunes using the 2.0 OS and needed to add some sound support to it. So, I had to use oalTouch's ugly third cousin as a starting point.

My main motivation for modifying the SoundEngine code was to alter it for providing a means to easily play the same sound effect multiple times, with the playing times overlapping. The SoundEngine as originally written would not do this. Each instance of the sound was bound directly to an OpenAL source, which meant you could only play each sound effect once at a time. If you wanted to play the same sound mutiple times, you have to load the sound mutiple times, which was very wasteful with memory.

This new version of the code allows sharing of the same source sound data buffer.

You can read more details and find a link for the source code here

Monday, May 4, 2009

DevDiary: Audio Stream stitching

I've made some more improvements to the audio engine used in my radio apps. I am now able to successfully "stitch" two streams together seamlessly. So, if you are listening to an audio stream, lose your connection and reconnect again before the audio buffer runs out, the app will be able to detect where the two streams overlap and will seamlessly join them together. It will now be completely undetectable that the connection was even lost - assuming there was enough audio data buffered.

I need to do some more extensive testing to ensure I've not introduced any other problems, but so far this is looking/sounding good.

Friday, May 1, 2009

Radio Paradise app 1.5 - coming soon

A new 1.5 update for the Radio Paradise iPhone app has been submitted to Apple for review and if all goes well will most-likely be approved late next week.

I pretty much re-wrote all the audio stream handling code and made some significant improvements. Dropped connections should be a lot less noticeable - now, rather than stopping the audio playback as soon as a connection is lost, the app will continue to play any audio is has while it simultaneously tries to reconnect to Radio Paradise's streaming servers.

A second minor change was also made. If you unplug your headphones while listening, the audio will now automatically be stopped rather than suddenly broadcasting the current track out the external iPhone speaker. If you plug your headphones back in, the audio will automatically restart.

One for All and All-In-One : an Xcode setting


I recently stumbled across this XCode setting which makes me like the XCode integrated development environment a whole lot more. I'm not 100% sure, but I think I saw this mentioned in a comment by Jeff Lamarche on his iPhone Development Blog. If you aren't familiar with that blog but are interested in iPhone development, you should bookmark the link immediately. Really. Stop reading this sentence and do it now. If you don't do it now and you forget, you'll have lost a good opportunity for learning a great many things. Jeff's got a great writing style and knack for explaining very technical details clearly and often with a mild sense of humor.

Anyhow, back to All-in-one mode for XCode. By default, the XCode layout is configured in such a way that most actions you perform (such as debugging, viewing compiler errors, searching for text) open a separate window. For the past 6 months of using XCode, I thought that was just how it had to be. Little did I know there was a one click setting in XCode preferences to turn on All-In-One layout mode. Finally, I don't have to go flipping through layers of windows to toggle between viewing compiler errors and looking at a normal source code view. It's all integrated.

And, if you really want to open a separate window for editing some files, the double-click method still works.

All content copyright © 2009  Brian Stormont, unless otherwise noted.   All rights reserved.