this code tastes like coffee!

and other observations 

Launch The Settings App Directly From Your App

Open a special url to go directly to your app's settings.

NSURL *url = [NSURL URLWithString:@"prefs:root=YOUR_CASE_SENSITIVE_BUNDLE_DISPLAY_NAME"];
[[UIApplication sharedApplication] openURL:url];

Filed under  //   Objective-C  

Comments [0]

Force Superdrive To Eject

Open terminal and type:

drutil eject

Comments [0]

UIViewContentModeScaleAspectFill Not Cropping

Is your image disrespecting the bounds of its UIImageView container? Set the clipsToBounds property:

UIImageView *imageView = [[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)] autorelease];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.clipsToBounds = YES;

Filed under  //   Objective-C  

Comments [0]

animatewithduration blocking

If you want to receive touches while running an animation, you must set UIViewAnimationOptionAllowUserInteraction in your animation options:

 

[UIView animateWithDuration:10.0
 delay:0.0
 options:UIViewAnimationOptionAllowUserInteraction
 animations:^{
  myView.alpha = 0.0f;
 }
 completion:^(BOOL finished) {

 }];

 

Filed under  //   Objective-C  

Comments [0]

UIActivityIndicatorView Size

By default, the sizes for UIActivityIndicatorView are:

  • UIActivityIndicatorViewStyleWhite: 21x21 px
  • UIActivityIndicatorViewStyleGray: 21x21 px
  • UIActivityIndicatorViewStyleWhiteLarge: 36x36 px

You can adjust the size by changing the frame property:

UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(originx, originy, width, height);

When using a custom size greater than 21 pixels, it is best to use the UIActivityIndicatorViewStyleWhiteLarge for the image to look smooth.

Filed under  //   Objective-C  

Comments [0]

Access UITableView from UITableViewCell

It's possible to access a cell's parent table without having to set up a delegate:

self.superview

Done. It's that easy.

Filed under  //   Objective-C  

Comments [0]

UITapGestureRecognizer Single and Double

How to have your view respond differently to a single tap or double tap:

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
doubleTap.numberOfTapsRequired = 2;
[mainView addGestureRecognizer:doubleTap];
[doubleTap release];

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
[mainView addGestureRecognizer:singleTap];
[singleTap requireGestureRecognizerToFail:doubleTap];
[singleTap release];

The key line of code that prevents the single tap from also firing on a double tap:

[singleTap requireGestureRecognizerToFail:doubleTap];

Filed under  //   Objective-C  

Comments [0]

Disable Xcode's Live Issue Debugging

Xcode 4 has a new feature called Live Issue Debugging that builds in the background while you write code and displays errors.

It is a bit overzealous and will harass you with code issues and warnings about brackets and such before you've even finished a method! While this could be useful if you are learning programming, if you are an experienced programmer it can hamper your productivity while you are in the draft phase of your project.

Exprected_expression

Much like drafting a paper, you shouldn't concern yourself with spelling. Your goal should be to get all of your ideas on paper as quickly as possible.

The same applies to writing code. You shouldn't lose your train of thought to be bothered about a missing semicolon. You should be focused purely on getting your ideas into code form. You can come back in the build phase and correct any minor issues.

I recommend disabling Live Issue Debugging in the General section of Xcode's preferences.

Xcode_4_live_issue_debugging_preference

Comments [0]

The Previous Location of a Moving Touch

The touch nsset contains the previous location of a touch that has been moved. Use previousLocationInView to get it. If you need locations beyond the previous frame, you'll have to write your own method to store them.

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
        for (UITouch *touch in touches) {
                CGPoint location = [touch locationInView:[touch view]];
                CGPoint previousLocation = [touch previousLocationInView:[touch view]];
        }
}

Filed under  //   Objective-C  

Comments [0]

Give Your Game Dynamic Background Music

Remember the underwater scene in the Sonic game where the music becomes dramatic as Sonic starts to drown? Sorry to bring up those anxiety memories, but that is a great example of dynamic background music. I'm going to show you how to add seamless mood changing music that follows the action in your game.

 

WordSurge - An Example Of Rising Intensity

Here's how I did it in my latest game, WordSurge. The music gradually gets more intense as the player's situation becomes more precarious. Check out the video below:

 

Make Your Own Music In Apple's Garage Band

Before you can do anything, you need some music to work with. Fire up Apple's Garage Band and channel your inner Tiesto.

 

Create Equal Length Verses

The goal is to create different verses of the same length so that your game's sound engine can seamlessly transition between them. It doesn't matter what the length is, but it must be the same. In WordSurge, I used 16 second verses, 6 verses in total.

Create each verse in order from least intense to most intense. When you're done, you should have something like this, with more and more instruments used in each verse. Save your project.

Garage_band_1

 

Disable Normalization

Before you can start saving out your verses, you need to tell Garage Band to not adjust the volume for you. With the default settings, it will adjust the volume of the exported track to be as loud as possible. However, this will change the loudness of the verse relative to the entire song. Not what you want. Disable normalization in the advanced preferences of Garage Band.

Garage_band_normalization

 

Cut The Verses & Export

Highlight everything but your first verse and temporarily delete it. It should look something like this.

Garage_band_2

 

In the Share menu, select Export Song to Disk...

Garage_band_export_menu

 

The high quality setting is adequate.

Garage_band_export_settings

 

Name the file 1.m4a.

Notice that Garage Band exported an audio file slightly longer than you expected. It has included any extra echos and fadeouts and you need those for your background music to sound seamless. It all synchs up because you will be playing these tracks overlapping on each other. More on that later.

Go to undo, or revert back to your save, to get the rest of your song back. Repeat the cut and export process for each verse. You should end up with a folder of music, named 1.m4a, 2.m4a, 3.m4a, etc.

Garage_band_3

 

Meanwhile, Back in Xcode

I used cocos2d for this game, which makes scheduling very easy. It also ships with Steve Oldmeadow's excellent CocosDenshion sound library.

Open your game project and drag your files into the Resources folder of your project.

Include this at the top of your code:

#import "SimpleAudioEngine.h"

 

In The Init Statement

Schedule your background music manager to run every X seconds, where X is the length of your verses.

[self schedule: @selector(backgroundMusicManager) interval:16];

Preload Your Sounds In Memory

[[SimpleAudioEngine sharedEngine] preloadEffect:@"1.m4a"];        
[[SimpleAudioEngine sharedEngine] preloadEffect:@"2.m4a"];        
[[SimpleAudioEngine sharedEngine] preloadEffect:@"3.m4a"];        
[[SimpleAudioEngine sharedEngine] preloadEffect:@"4.m4a"];        
[[SimpleAudioEngine sharedEngine] preloadEffect:@"5.m4a"];        
[[SimpleAudioEngine sharedEngine] preloadEffect:@"6.m4a"];

Finally, kick start the manager to run the first time. Otherwise, it won't happen until 16 seconds have elapsed.

[self backgroundMusicManager];

 

Your Music Manager Method

-(void) backgroundMusicManager {
        float intensity;

/* Your own code to determine what intensity your music should be at */

if (intensity >= 0 && intensity < 0.1666f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"1.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else if (intensity >= 0.1666f && intensity < 0.3333f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"2.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else if (intensity >= 0.3333f && intensity < 0.5f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"3.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else if (intensity >= 0.5f && intensity < 0.6666f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"4.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else if (intensity >= 0.6666f && intensity < 0.8333f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"5.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else if (intensity >= 0.8333f) {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"6.m4a" pitch:1.0f pan:0.0f gain:0.35f];
} else {
        lastBGSoundPlaying = [[SimpleAudioEngine sharedEngine] playEffect:@"1.m4a" pitch:1.0f pan:0.0f gain:0.35f];
}
}

Remember how these tracks are slightly longer than 16 seconds because they include echos and fadeouts? That's why I'm using the playEffect method instead of cocosDenshion's background music methods. This way the tracks overlap each other at precisely the right time and you'll get that great seamless effect.

Before you run this code chunk, you need to come up with a method for deciding what intensity your game is currently at. In WordSurge, I used a combination of the player's remaining life and the number of bubbles on the screen. The logical choice is to use the player's life, but your game is unique and you can be creative here.

 

Additional Tips

  • Keep on eye on your memory usage. If you are pushing the limit after preloading your music, you may need to downmix to mono or a lower khz setting, like 22.050. Be sure to tell CocosDenshion to run at that khz by using the setMixSampleRate method. 
  • For complete optimization, make sure all of the sounds you use are the same khz.
  • Adjust the gain setting so your background music properly blends in with your other sounds. Generally, you're going to want it quieter. If you hear popping sounds when playing multiple effects, your gain is too high.

Filed under  //   Cocos2d  

Comments [0]