Some favorite site feeds aggregated locally: iPhone Development RSS   Adobe Labs RSS   Macrumors RSS

Friday, December 18, 2009

iPhone Development: My struggle with the distribution provisioning profile

Friday, December 18, 2009    0 Comments

In the end my solution to building an app bundle the iTunes Connect application would accept for upload would be a head scratcher yet simple enough. It took a long time getting there.

It began with a simple distribution provisioning profile creation and installation. I cleaned my targets and ran a build. No problems. I zipped up the bundle, uploaded it & got a signing error. Hmm. I googled about it and found tons of information about the problem - all with different solutions - some very labor intensive.

I checked my project build settings - I was setting to iPhone Distribution: Eric Dolecki. I then checked my target build settings - it was set to iPhone Developer (Eric Dolecki). Aha I thought. I changed the provision to distribution. Same error on upload.

I revoked the previous distribution provisioning profile and generated a new one. The expiration date was Saturday December 18th which was wrong... but whatever. I installed that and tried again. Same error.

I then changed the settings to iPhone Distribution (without my name trailing the title) & bam! It linked my name with the distribution profile and thus I was able to upload my bundle! Yay!

This didn't make a lot of sense to me (why have the generic distribution profile work & link up and the one with my actual name in it not work). I'm glad I finally figured it out & now await my approval.

Labels:

 

Wednesday, October 21, 2009

iPhone: UIWebView with images

Wednesday, October 21, 2009    0 Comments

In many iPhone applications I have noticed rather tidy and nice about screens, or simply people getting around having to code up tables, etc. and they opt to use HTML and a UIWebView to display their information. This is indeed cool, but if you're using images in your HTML, you'll need to make sure that you set your baseURL properly so that you can simply reference the images like so: <img src="image.jpg" alt="image"/>.

One thing to note... if you want to populate your HTML dynamically, you can compose an NSString, etc. with your HTML in it so that you can dynamically populate information based on your code.

The code below sets this up and loads an html file into a webview properly so that you can reference the images easily.
- (void)viewDidLoad {
 NSString *mainPath = [[NSBundle mainBundle] bundlePath];
 NSURL *baseURL = [NSURL fileURLWithPath:mainPath];
 NSString *path = [[NSBundle mainBundle] pathForResource:@"default.html" ofType:nil]; 
 NSString *pageHTML = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil]; 
 [webView loadHTMLString:pageHTML baseURL:baseURL];
    [super viewDidLoad];
}
This will make things a whole lot easier when pulling images, etc. into your HTML in your UIWebView. This is pretty common knowledge, but I still had to Google around to find out why my images weren't being loaded properly.

Labels:

 

Tuesday, October 20, 2009

Build Succeeded. It would make a great shirt.

Tuesday, October 20, 2009    0 Comments



When you're squashing bugs, replacing methods, and you're arms are covered in delegate dust, Seeing that pop up on compile is a welcome sight. In fact, I am wondering if anyone has made a custom t-shirt with this thing in mind and attended a Macworld. I'm sure someone must have at some point.

Now to break this build and add more stuff.

Labels:

 

Tuesday, August 18, 2009

AS3 Developers and the iPhone

Tuesday, August 18, 2009    0 Comments

Silently I have been watching a revolution of another kind take shape in the Flash Developer Community. A revolution that I at first found to be surprising, but now find exciting and interesting.

For a long while, the most exciting content on the web has been developed within the fold of the Flash Community. The Flash Community is an organic, open network of creative and devoted people constantly pushing the boundaries of cool, interactive, data-driven, and experimental. I am not going to get into a debate of AJAX versus Flash versus Silverlight, or anything like that... because I simply don't think you can bring Flash down to compare it to the others.

Of the hordes of Flash Developers, many have dipped their toes into the world of Xcode, Objective-C, and the iPhone SDK. I am seeing iPhone sessions popping up in many Flash conferences. I am seeing hardcore Flash developers (many for ages) twittering about various Obj-C methods, blogging about iPhone development, etc.

This is exciting because the iPhone itself is a platform that brings me a lot of satisfaction developing for. It's not Flash in terms of it's immediacy (at least not for me yet), but the things you can do with it after putting in some effort (googling, reading books, pouring over some code, reading header files, etc.) is astounding.

When I am developing for the iPhone, I feel like the sky is the limit and I am constantly trying to do things that were easy for me to do in Flash. Because of that Flash experience, I am able to deliver some compelling applications. And so are many other Flash developers.

The iPhone has a lot going for it in terms of apps. Yes, many throw their first few attempts on the store just to see what that feels like... but in the end we are going to be treated to a lot of wonderful content as more and more Flash developers and designers start learning Obj-C and making apps of their own.

AS3 is certainly fun. But the iPhone distraction is fun too; it's mysterious, enabling and almost as open as the Flash Community. You're still more likely to pull code out of readers on a Flash mailing list than you are on a Cocoa mailing list, but I see that softening a bit.

Anyway, this is just something I've noticed and something that I really like seeing.

Labels: ,

 

Friday, August 14, 2009

iPhone: loadView and viewDidLoad

Friday, August 14, 2009    0 Comments

I am not sure the exact cause (threads?) but I tried something today where I am setting up and running network detection status in loadView, setting a variable, and in my viewDidLoad checking that variable and then running some streaming stuff.

When I had the network detection stuff in loadView, my app would launch, go to black, and then the screen would go white... no error in the debugger... it would just stop and I'd get a blank white screen.

So I move it all into the viewDidLoad and everything behaves well now.

Does anyone know what might actually be causing this problem? I know what not to do, but I don't know why.

Labels:

 

Wednesday, August 5, 2009

iPhone: Image interpolation on the hack

Wednesday, August 5, 2009    1 Comments

I am currently making an analog clock for the iPhone just to play with some things that come second nature to me in Actionscript 3. For the most part it's been straightforward. I ran into a snag and needed a solution.

I have three hands for this analog clock: hour, minute and second hands. They of course need to rotate to their appropriate values. However, I am using UIImageView with PNGs with transparency to do this. There are better ways to do this I'm sure, but I wanted to get this to work.

Setting it up in IB is a nightmare, so I'll be changing the way I do this for sure, but I learned a little something along the way. Using an NSTimer firing every second, I update my clock. Tick, tick, tick motions (no animation). The hands look terrible and blocky because by default no interpolation (aliasing) is happening. In trying to get rid of that, I managed to introduce some animation as well for free.

The original method called from my timer:
#define DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0 * M_PI)
- (void) showActivity {
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit| NSMinuteCalendarUnit |NSSecondCalendarUnit;
NSDate *date = [NSDate date];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:date];
int h = [comps hour];
int m = [comps minute];
int s = [comps second];

CGAffineTransform cgaRotateHr = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(h * 30));
CGAffineTransform cgaRotateMin = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(m * 6));
CGAffineTransform cgaRotateSec = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(s * 6));

[hrHand setTransform:cgaRotateHr];
[minHand setTransform:cgaRotateMin];
[secondHand setTransform:cgaRotateSec];

NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setTimeStyle:NSDateFormatterMediumStyle];
[clockLabel setText:[formatter stringFromDate:date]];
}
Okay, that works, but I get horrid edges on my PNGs for the hands. Introducing these few lines before the setTransforms on the hands forces interpolation (default) and animation:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"transform"];
[[secondHand layer]addAnimation:animation forKey:@"transform"];
[[hrHand layer]addAnimation:animation forKey:@"transform"];
[[minHand layer]addAnimation:animation forKey:@"transform"];
Now the edges are prettier and I get animation of the hands when they change positions. It's on to looking at OpenGL and other things for something like this.

Labels: , ,

 

Thursday, July 30, 2009

iPhone: Make your Default.png sexy

Thursday, July 30, 2009    5 Comments

The Default.png file that automatically gets displayed while your application is loading is a nice way to give the user some feedback (instead of looking at a black screen for a little while). With the iPhone 3Gs this might not be such a big deal, but in any case when loading is complete, the Default.png goes away instantly and you merely snap into your application's view. It works well enough, but it's not very sexy.

I've seen where some will take their default view and snapshot that and bring it into Photoshop, and lay down a semi-transparent black on top of it to make it look disabled. Thus you snap into the view in a less jarring manner. This is a little better, but it's still not sexy.

Why not use animation? Place a UIImageView over everything and when we're done launching, remove it with a fade and some zoom? Sure... and here is some quick code to do just that:


In your YourAppDelegate.h create a reference to a UIImageView and also whip up a method you'll call through code:
@interface YourAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
YourAppViewController *viewController;
UIImageView *splashView;
}
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context;
Now, in YourAppDelegate.m add the guts of that method and some additional code in your applicationDidFinishLaunching:
- (void)startupAnimationDone:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
[splashView removeFromSuperview];
[splashView release];
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {

[window addSubview:viewController.view];
[window makeKeyAndVisible];

// Make this interesting.
splashView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 320, 480)];
splashView.image = [UIImage imageNamed:@"Default.png"];
[window addSubview:splashView];
[window bringSubviewToFront:splashView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationTransition:UIViewAnimationTransitionNone forView:window cache:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(startupAnimationDone:finished:context:)];
splashView.alpha = 0.0;
splashView.frame = CGRectMake(-60, -85, 440, 635);
[UIView commitAnimations];
}
You're done. You could mess with this in other ways, but it makes the whole entry into your application a lot more appealing in my opinion.

Labels:

 

Wednesday, July 29, 2009

iPhone: MPMusicPlayerController: currentPlaybackTime and playbackDuration

Wednesday, July 29, 2009    1 Comments

I started playing around with the iPhone MPMusicPlayerController and found that getting and displaying a track's duration is pretty simple, displaying the current time of the track wasn't so easy. I was expecting to find a notification of some kind akin to IsPlaying or something like that so that I didn't need to fire my own NSTimer.

Well, there doesn't seem to be one, we have access to MPMusicPlayerControllerNowPlayingItemDidChangeNotification, MPMusicPlayerControllerPlaybackStateDidChangeNotification, and MPMusicPlayerControllerVolumeDidChangeNotification. How do you like those constants? Verbose to say the least.

Anyway, I found that I had to run my own Timer, and I just started it up within viewDidLoad - every 0.5 seconds, almost like an onEnterFrame in Flash. In the end my code looks something like this... this may save you some time. The labels are IBOutlet objects, etc.

- (void)onTimer:(NSTimer *)timer {
long currentPlaybackTime = self.musicPlayer.currentPlaybackTime;
int currentHours = (currentPlaybackTime / 3600);
int currentMinutes = ((currentPlaybackTime / 60) - currentHours*60);
int currentSeconds = (currentPlaybackTime % 60);
self.currentLabel.text = [NSString stringWithFormat:@"%i:%02d:%02d", currentHours, currentMinutes, currentSeconds];
}

- (void)viewDidLoad {
[super viewDidLoad];
self.musicPlayer = [MPMusicPlayerController iPodMusicPlayer];

// Initial sync of display with music player state
[self handleNowPlayingItemChanged:nil];
[self handlePlaybackStateChanged:nil];
[self handleExternalVolumeChanged:nil];

// Register for music player notifications
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(handleNowPlayingItemChanged:)
name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification
object:self.musicPlayer];
[notificationCenter addObserver:self
selector:@selector(handlePlaybackStateChanged:)
name:MPMusicPlayerControllerPlaybackStateDidChangeNotification
object:self.musicPlayer];
[notificationCenter addObserver:self
selector:@selector(handleExternalVolumeChanged:)
name:MPMusicPlayerControllerVolumeDidChangeNotification
object:self.musicPlayer];
[self.musicPlayer beginGeneratingPlaybackNotifications];
currentTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
}

- (void)handleNowPlayingItemChanged:(id)notification {
MPMediaItem *currentItem = self.musicPlayer.nowPlayingItem;
self.songLabel.text = [currentItem valueForProperty:MPMediaItemPropertyTitle];
self.artistLabel.text = [currentItem valueForProperty:MPMediaItemPropertyArtist];
self.albumLabel.text = [currentItem valueForProperty:MPMediaItemPropertyAlbumTitle];

// Current Playback Value - via the running timer

// The total duration of the track...
long totalPlaybackTime = [[[musicPlayer nowPlayingItem] valueForProperty: @"playbackDuration"] longValue];
int tHours = (totalPlaybackTime / 3600);
int tMins = ((totalPlaybackTime/60) - tHours*60);
int tSecs = (totalPlaybackTime % 60 );
self.durationLabel.text = [NSString stringWithFormat:@"%i:%02d:%02d", tHours, tMins, tSecs ];

// Display album artwork. self.artworkImageView is a UIImageView.
CGSize artworkImageViewSize = self.artworkImageView.bounds.size;
MPMediaItemArtwork *artwork = [currentItem valueForProperty:MPMediaItemPropertyArtwork];
if (artwork != nil) {
self.artworkImageView.image = [artwork imageWithSize:artworkImageViewSize];
} else {
self.artworkImageView.image = nil;
}
}

Labels:

 

Friday, July 17, 2009

iPhone: External Accessory Framework snippets

Friday, July 17, 2009    17 Comments

I do not have any immediate plans to play around with this and I don't have any hardware or the protocol knowledge to do so with anything, but I was reading up on the External Accessory Framework that comes with the OS3.0 SDK.

Some information, but I didn't see any sample projects (for obvious reasons) or code snippets anywhere until just now. I'm not sure if this stuff is up to date or not, but here you go.

Add the ExternalAccessory.framework to your project. Make sure to add #import <ExternalAccessory/ExternalAccessory.h> to your .m file, and this is some example code:
- (EASession *)openSessionForProtocol:(NSString *)protocolString {
NSArray *accessories = [[EAAccessoryManager sharedAccessoryManager]
connectedAccessories];
EAAccessory *accessory = nil;
EASession *session = nil;

for (EAAccessory *obj in accessories) {
if ([[obj protocolStrings] containsObject:protocolString]){
accessory = obj;
break;
}
}

if (accessory){
session = [[EASession alloc] initWithAccessory:accessory
forProtocol:protocolString];
if (session) {
[[session inputStream] setDelegate:self];
[[session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[session inputStream] open];
[[session outputStream] setDelegate:self];
[[session outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[[session outputStream] open];
[session autorelease];
}
}
return session;
}

// Handle communications from the streams.
- (void)stream:(NSStream*)theStream handleEvent:(NSStreamEvent)streamEvent
{
switch (streamEvent)
{
// case NSStreamHasBytesAvailable: this was incorrect in Apple documentation...
case NSStreamEventHasBytesAvailable:
// Process the incoming stream data.
break;
case NSStreamEventHasSpaceAvailable:
// Send the next queued command.
break;
default:
break;
}
}
Enjoy. This is here merely as a kind of reference.

Labels: , ,

 

Thursday, July 16, 2009

iPhone Book: Beginning iPhone 3 Development (hold the phone)

Thursday, July 16, 2009    0 Comments

When I set out to jump into iPhone development, I knew that I wanted to collect a lot of material that I could read through.

I printed tons of pages found on the internet, and I also purchased a few books on the subject. I keep two on my desk at all times: Beginning iPhone Development (Exploring the iPhone SDK) by Dave Mark and Jeff LaMarche, and The iPhone Developer's Cookbook.

I refer to them every now and then in case I forget something pretty silly and need a quick reference. And yes, I am also guilty of opening up previous Projects looking for techniques I used before that I want to employ again. I just did that twenty minutes ago. I'm sure I'll do it again before the close of the day.

But what's the point of this post? You see, the Beginning iPhone 3 Development book is new, but it's not completely new. A little sprinkling of Core Data has been added and some Table View styling. That's about it.

If you are into the new stuff in the 3.0 SDK (GameKit, MapKit, Push Notifications, In-App Purchase, more Core Data, etc.) then you want to wait for the book from the same guys to be called More iPhone 3 Development. So check Amazon every now and then. These guys put together really fine books, so you'll be well-served. Just don't get the new one they just put out thinking it's going to cover SDK 3.0 in any great detail.

Labels: ,

 

Wednesday, July 15, 2009

iPhone: Previous example with touches

Wednesday, July 15, 2009    0 Comments

I just wanted to update my previous post in regards to an iPhone application that allows one to step through images as a presentation to a potential/current client, etc. So all I've really done here is added swiping left and right with a small allowance for vertical changes.

#import "ClickThroughViewController.h"
#import <QuartzCore/QuartzCore.h>

@implementation ClickThroughViewController
@synthesize imageView, imageViewTop, aboutView, aboutButton,
advanceButton, backButton, label, gestureStartPoint, dirString;

- (void)updateLabel {
NSString *c = [[NSString alloc] initWithFormat:@"%i", count];
label.text = c;
[c release];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
dirString = NULL;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
if(fabsf(gestureStartPoint.x - currentPosition.x ) >= kMinimumGestureLength && fabsf(gestureStartPoint.y - currentPosition.y) <= kMaximumVariance){
//Horizontal Swipe
if( gestureStartPoint.x < currentPosition.x ){
dirString = @"fromLeft";
} else {
dirString = @"fromRight";
}
}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if( [dirString isEqualToString:@"fromLeft"] ){
[self prevImage];
} else if( [dirString isEqualToString:@"fromRight"]){
[self nextImage];
}
}

/*
You need to make sure the prototype images that you use are JPGs
and use the "image_" convention, otherwise the total count will
be off and your application will bork.
*/
- (void)viewDidLoad {

UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image_1.jpg" ofType:nil]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image_1.jpg" ofType:nil]];
imageView.image = img2;
imageViewTop.image = img;
imageView.hidden = YES;
totalCount = 0;
aboutShowing = NO;

NSArray *d = [[NSBundle mainBundle] pathsForResourcesOfType:@"jpg" inDirectory:nil];
for(NSString *s in d){
if([[s lastPathComponent] hasPrefix:@"image_"]){
totalCount++;
}
}
count = 1;
prev = totalCount;
label.alpha = 0;

[self updateLabel];
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

- (void)dealloc {
[imageView release];
[backButton release];
[advanceButton release];
[super dealloc];
}

// #pragma isn't very cool in my opinion, so...
//MARK: Custom Code

- (IBAction)advanceImage:(id)sender {
[self nextImage];
}

- (void)nextImage {
count++;
if( count > totalCount ){
count = 1;
}
if( count == 1 ){
prev = totalCount;
} else {
prev = count - 1;
}
[self updateLabel];

NSString *tmp = [[NSString alloc] initWithFormat:@"image_%i", count];
NSString *old = [[NSString alloc] initWithFormat:@"image_%i", prev];
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:tmp ofType:@"jpg"]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:old ofType:@"jpg"]];

imageView.image = img2;
imageViewTop.image = img;
label.alpha = 1;

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:[self imageViewTop] cache:YES];
[UIView setAnimationDuration:0.5];
[UIView commitAnimations];

CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
label.alpha = 0;
[UIView commitAnimations];
}

- (IBAction)aboutChange:(id)sender {
if( !aboutShowing ){
aboutShowing = YES;
aboutView.hidden = NO;
aboutView.frame = CGRectMake(0, -50, 320, 480);
aboutView.alpha = 0.0;
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationDuration:1.0];
aboutView.frame = CGRectMake(0, 0, 320, 480);
aboutView.alpha = 1.0;
[UIView commitAnimations];

} else {
aboutShowing = NO;
CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
aboutView.frame = CGRectMake(0, -50, 320, 480);
aboutView.alpha = 0.0;
[UIView commitAnimations];
}
}

- (IBAction)backImage:(id)sender {
[self prevImage];
}

- (void)prevImage {
count--;
if( count < 1 ){
count = totalCount;
}
if( count == 1 ){
prev = totalCount;
} else {
prev = count + 1;
}
[self updateLabel];
NSString *tmp = [[NSString alloc] initWithFormat:@"image_%i", count];
NSString *old = [[NSString alloc] initWithFormat:@"image_%i", prev];
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:tmp ofType:@"jpg"]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:old ofType:@"jpg"]];
imageView.image = img2;
imageViewTop.image = img;
label.alpha = 1;

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:[self imageViewTop] cache:YES];
[UIView setAnimationDuration:0.5];
[UIView commitAnimations];

CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
label.alpha = 0;
[UIView commitAnimations];
}

@end

Labels: ,

 

Monday, July 13, 2009

iPhone: Voices That Matter iPhone Developers Conference

Monday, July 13, 2009    0 Comments

If you are in the Boston area, there is an iPhone developers conference taking place at the MCLE Conference Center in Boston October 17-18th. The early bird price (until September 12) is $495, after that the price jumps to $695.
You can check out details of the conference right here. Just to get you're nether bits in an excited state, Aaron Hillegass is delivering the Saturday keynote and Andy Ihnatko is delivering a talk on Sunday. Topics include:
  • Custom User Interfaces with Core Animation
  • Data Sync
  • Designing a Killer UI
  • iPhone View Controllers
  • Peer to Peer Networking
  • Cocoa Design Patterns
  • Core Data
  • Open GL ES for General Applications
  • Rapid Application Delivery: Going Hybrid
  • Accelerometer
  • Audio and OpenAL
  • Core Location
  • Cameras and Photos
and some more stuff sprinkled in there.

Labels: ,

 

Friday, July 10, 2009

iPhone: Easy step through presentation application

Friday, July 10, 2009    2 Comments

This does not account for left and right swiping, but it could be added easily. Have a bunch of photos that you'd like to show someone in a presentation? Perhaps mock-ups of layouts, etc. This bit can really help you along. I wrote this quickly but it works pretty well, even with some subtle and nice effects. You'll need to create two custom buttons that you'll hook up to backImage and advanceImage.

The code reads JPG images that use "image_" in their filename. That allows you just load up your project with the files you want and they end up in the bundle, available to you. You could tweak this to use online assets too if you really wanted.

Enjoy on this fine Friday.
ClickViewController.m
#import "ClickThroughViewController.h"
#import <quartzcore/quartzcore.h>

@implementation ClickThroughViewController
@synthesize imageView, imageViewTop, aboutView, aboutButton, advanceButton, backButton, label;

- (void)updateLabel {
NSString *c = [[NSString alloc] initWithFormat:@"%i", count];
label.text = c;
[c release];
}

/*
You need to make sure the prototype images that you use are JPGs
and use the "image_" convention, otherwise the total count will
be off and your application will bork.
*/
- (void)viewDidLoad {
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image_1.jpg" ofType:nil]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"image_6.jpg" ofType:nil]];
imageView.image = img2;
imageViewTop.image = img;
imageView.hidden = YES;
totalCount = 0;
aboutShowing = NO;

NSArray *d = [[NSBundle mainBundle] pathsForResourcesOfType:@"jpg" inDirectory:nil];
for(NSString *s in d){
if([[s lastPathComponent] hasPrefix:@"image_"]){
totalCount++;
}
}
count = 1;
prev = totalCount;
label.alpha = 0;
[self updateLabel];
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

- (void)dealloc {
[imageView release];
[backButton release];
[advanceButton release];
[super dealloc];
}

// #pragma isn't very cool in my opinion, so...
//MARK: Custom Code

- (IBAction)advanceImage:(id)sender {
count++;
if( count > totalCount ){
count = 1;
}
if( count == 1 ){
prev = totalCount;
} else {
prev = count - 1;
}
[self updateLabel];

NSString *tmp = [[NSString alloc] initWithFormat:@"image_%i", count];
NSString *old = [[NSString alloc] initWithFormat:@"image_%i", prev];
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:tmp ofType:@"jpg"]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:old ofType:@"jpg"]];

imageView.image = img2;
imageViewTop.image = img;
label.alpha = 1;

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:[self imageViewTop] cache:YES];
[UIView setAnimationDuration:0.5];
[UIView commitAnimations];

CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
label.alpha = 0;
[UIView commitAnimations];
}

- (IBAction)aboutChange:(id)sender {
if( !aboutShowing ){
aboutShowing = YES;
aboutView.hidden = NO;
aboutView.frame = CGRectMake(0, -50, 320, 480);
aboutView.alpha = 0.0;
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationDuration:1.0];
aboutView.frame = CGRectMake(0, 0, 320, 480);
aboutView.alpha = 1.0;
[UIView commitAnimations];

} else {
aboutShowing = NO;
CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
aboutView.frame = CGRectMake(0, -50, 320, 480);
aboutView.alpha = 0.0;
[UIView commitAnimations];
}
}

- (IBAction)backImage:(id)sender {
count--;
if( count < 1 ){
count = totalCount;
}
if( count == 1 ){
prev = totalCount;
} else {
prev = count + 1;
}
[self updateLabel];
NSString *tmp = [[NSString alloc] initWithFormat:@"image_%i", count];
NSString *old = [[NSString alloc] initWithFormat:@"image_%i", prev];
UIImage *img = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:tmp ofType:@"jpg"]];
UIImage *img2 = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:old ofType:@"jpg"]];
imageView.image = img2;
imageViewTop.image = img;
label.alpha = 1;

CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:[self imageViewTop] cache:YES];
[UIView setAnimationDuration:0.5];
[UIView commitAnimations];

CGContextRef context2 = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context2];
[UIView setAnimationDuration:1.0];
label.alpha = 0;
[UIView commitAnimations];
}

@end

Labels: , ,

 

Wednesday, July 8, 2009

iPhone: CorePlot

Wednesday, July 8, 2009    1 Comments

There is an open-source graphing library available for both Mac OS X and the iPhone called CorePlot. If you've gotten accustomed to the graphing packages available for Flex and Flash applications, you'll get a little tingle when you see this graphic library.

In order to integrate into your iPhone application, after checking the code out of the repository or downloading you can follow this information:

Because frameworks cannot be used in Cocoa Touch applications in the same way as on the Mac, the means of including Core Plot within an iPhone application are slightly different.

First, drag the CorePlot-CocoaTouch.xcodeproj file into your iPhone application's Xcode project (making sure that nothing's copied and the paths are relative to your project). Then go to the Targets tab in Xcode, select your application's target, and bring up the inspector. Go to the General settings page and add the CorePlot-CocoaTouch library as a direct dependency.

Core Plot is built as a static library for iPhone, so you'll need to drag the libCorePlot-CocoaTouch.a static library from under the CorePlot-CocoaTouch.xcodeproj group to your target's Link Binary With Libraries folder.

You'll also need to point to the right header location. Under your Build settings, set the Header Search Paths to the relative path from your application to the framework/ subdirectory within the Core Plot source tree. Make sure to make this header search path recursive. You need to add -ObjC to Other Linker Flags as well.

Core Plot is based on Core Animation, so if you haven't already, add the QuartzCore framework to your application project.

Finally, you should be able to import all of the Core Plot classes and data types by inserting the following line in the appropriate source files within your project:
#import "CorePlot-CocoaTouch.h

I have yet to try this out myself since I don't immediately have a need for it, but I am seeing some people encountering problems with the steps outlined. They get a EXC_BAD_ACCESS error. Perhaps this library isn't set up in it's downloaded form properly and may require some tinkering to get it rolling properly. Here is the Issues List where you can tool around to see what problems currently exist that have been commented upon.

Labels: ,

 

Tuesday, July 7, 2009

Are you using Amazon's Product Advertising APIs? You could be in trouble.

Tuesday, July 7, 2009    0 Comments

If you are using Amazon's Product Advertising APIs in your iPhone application, guess what? You could be libel and be on the tail-end of some legal action.

You see, the APA APIs stipulate that use of the API is prohibited from use on a mobile device.

From the agreement: 4. Usage Requirements, (e):
You will not, without our express prior written approval requested via this link, use any Product Advertising Content on or in connection with any site or application designed or intended for use with a mobile phone or other handheld device.

So you might be in violation and not know it yet. I'd imagine an application with a small user footprint will probably go unnoticed. You may want to look into this if you are using Amazon's service.

Labels: ,

 

Monday, July 6, 2009

iPhoneSimulatorExchange

Monday, July 6, 2009    0 Comments

There is a new program out for iPhone developers that allows you to create a one-click installer of your iPhone application that allows you to package up your app so it can be run on someone else's Simulator. It's said to help with testing and also for allowance of screencasts for review purposes.

I can see the value for the screencasts certainly, and use for other developers is neat without them having to worry about your frameworks, etc. I do think that it might almost be easy enough to zip up a project file and have another developer just compile the thing you want tested, but I could certainly be wrong about that. Check it out.

Labels: , ,

 

Thursday, June 25, 2009

[iPhone] Getting images from your bundle without hardcoding

Thursday, June 25, 2009    0 Comments

Update 3. Better yet:
totalCount = 0;
NSArray *d = [[NSBundle mainBundle] pathsForResourcesOfType:@"jpg" inDirectory:nil];
for(NSString *s in d){
if([[s lastPathComponent] hasPrefix:@"image_"]){
totalCount++;
}
}
Update 2. Since the file structure normally gets flattened out within your iPhone application (I guess there are some compiler tricks you can do), you'll always be targeting the root of the app directory. With that in mind, you'll want to probably use some kind of naming convention to separate any special files you'd like to collect from the bundle. You can also use extension. Say collect up and get the count of all images in your bundle that are JPGs and contain "image_" in the title:
totalCount = 0;
NSArray *d = [[NSBundle mainBundle] pathsForResourcesOfType:@"jpg" inDirectory:nil];
for( int i=0;i<[d count];i++){
NSString *searchForMe = @"image_";
NSString *s = [[NSString alloc] initWithString:[d objectAtIndex:i]];
NSRange range = [s rangeOfString:searchForMe];
if( range.location != NSNotFound ){
totalCount++;
}
}
Boom.

Update. I'm a moron. I should have just looked into NSBundle.h.
NSUInteger jpegCount = [[[NSBundle mainBundle] pathsForResourcesOfType:@"jpg" inDirectory:subDirName] count];

You can get the number of a type of item in your bundle on the iPhone, and this can come in very handy. No magic numbers. This code example doesn't look in a resource directory, but you could supply the inDirectory with something like @"backgrounds" and thus build up arrays of images of differing types, etc. There may be a better way of doing this (ie. if you only want to get the count of the items), but I don't know about it yet.
NSMutableArray * imageArray = [[NSMutableArray alloc] init];
NSEnumerator * imageBundlePathEnumerator = [[[NSBundle mainBundle] pathsForResourcesOfType: @"jpg" inDirectory: nil] objectEnumerator];
NSString * imageBundlePath = nil;

while (imageBundlePath = [imageBundlePathEnumerator nextObject])
{
[imageArray addObject: [[[NSImage alloc] initWithContentsOfFile: imageBundlePath] autorelease]];
}

NSLog(@"Number of images loaded : %d", [imageArray count]);

NSImage * anImage = nil;
NSEnumerator * imageEnumerator = [imageArray objectEnumerator];

while (anImage = [imageEnumerator nextObject])
{
NSLog(@"image = %@", anImage);
}
Tada. I hope to find some more examples.

Labels: ,

 

Thursday, May 28, 2009

Pixel formats in cocos2d v0.7.3+

Thursday, May 28, 2009    0 Comments

[per riq] Since cosos2d v0.7.3, you can specify the texture's pixel format of your PNG/TIFF/BMP/GIF images.
The texture's pixel format is the way the image is stored in GPU memory.

Possible pixel formats:
  • RGBA8888 (32-bit) (kTexture2DPixelFormat_RGBA8888)
  • RGBA4444 (16-bit) (kTexture2DPixelFormat_RGBA4444)
  • RGB5_A1 (16-bit)(kTexture2DPixelFormat_RGB5A1)
  • RGB565 (16-bit) (kTexture2DPixelFormat_RGB565)
RGBA8888:
  • 8 bits are assigned to the red channel, 8 bits to the green channel, 8 bits to the blue channel and 8 bits to the alpha channel.
  • Use this pixel format when you need the maximum possible quality for your image.
  • But it will consume the double of memory compared to 16-bit textures. Memory is a precious resource on the iPhone
  • Usually it is also slower to render.
  • Useful for: background image of your intro scene, and for images with lots of gradient colors
RGBA4444:
  • 4 bits are assigned to the red channel, 4 bits to the green channel, 4 bits to the blue channel, and 4 bits to the alpha channel
  • It gives you good quality in all channels, good speed, good memory consumption.
  • Useful for: sprites that have different values of transparency
RGB5A1:
  • 5 bits are assigned to the red channel, 5 bits to the green channel, 5 bits to the blue channel, and only 1 bit to the alpha channel
  • It gives you good quality in RGB channels but poor quality on the A channel. It also gives you good speed and good memory consumption.
  • Useful for: sprites that have transparent parts, but the transparency is either On or Off
RGB565:
  • 5 bits are assigned to the red channel, 6 bits to the green channel, and 5 bits to the blue channel. It has no alpha channel support
  • It gives you the best possible quality for 16-bit textures, but without alpha channel support.
  • Useful for: background images in your game.
The default pixel format in v0.7.3 is RGBA8888.

How to use it:
// Set the pixel format before loading the image
// RGBA 8888 image (32-bit)
[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
Sprite *sprite1 = [Sprite spriteWithFile:@"test-rgba1.png"];

// Set the pixel format before loading the image
// RGBA 4444 image (16-bit)
[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA4444];
Sprite *sprite2 = [Sprite spriteWithFile:@"test-rgba2.png"];

// Set the pixel format before loading the image
// RGB5A1 image (16-bit)
[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGB5A1];
Sprite *sprite3 = [Sprite spriteWithFile:@"test-rgba3.png"];

// Set the pixel format before loading the image
// RGB565 image (16-bit)
[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGB565];
Sprite *sprite4 = [Sprite spriteWithFile:@"test-rgba4.png"];

// restore the default pixel format
[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_Default];

Labels: , ,

 

Wednesday, May 13, 2009

iPhone: Detect online network status

Wednesday, May 13, 2009    6 Comments

For my previous post I showed you how you can cache in memory images from URLs... however, what if the device is not able to connect to a network? The previous example will hang and eventually bomb back to the springboard because it's not coded to handle that condition.

For me this network detection thing was a bit of a Holy Grail item. I'd seen a few examples around from Apple that seemed like they used tons of code to get the job done (or maybe I just didn't understand their code well enough yet). Then I read online about a seismic XML tutorial from Apple that did network detection.

I popped open the project in the Xcode Documentation window and started checking out the .h and .m files. I saw the implementation they used, and it didn't involve tons of code. And after testing it, it works just fine for the moment.

I've since been told that I could use a separate thread and populate the initial UIImageViews with a stock image, and the threaded process would fetch and place the artwork as needed (to allow for initial smooth scrolling). I don't know enough about that yet though, so this will have to do.

Make sure you Add the SystemConfiguration.framework to your project, and include it in your .m (#import ).

In your .h file, define a BOOL, I called mine "availableNetwork" - you don't need to assign it as a property, just up in the interface block. Make sure you also define the method in the .h... -(BOOL)isDataSourceAvailable;

Here are the two methods I'm using to report back network status (in my .m):
- (BOOL)isDataSourceAvailable {

static BOOL checkNetwork = YES;
static BOOL _isDataSourceAvailable = NO;
if (checkNetwork) { // Since checking the reachability of a host can be expensive, cache the result and perform the reachability check once.
checkNetwork = NO;
Boolean success;
const char *host_name = "google.com"; //pretty reliable :)
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, host_name);
SCNetworkReachabilityFlags flags;
success = SCNetworkReachabilityGetFlags(reachability, &flags);
_isDataSourceAvailable = success && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired);
CFRelease(reachability);
}
return _isDataSourceAvailable;
}
That's the method we'll call when we load our view, and set our "availableNetwork" value to. loadView is triggered before viewDidLoad.
- (void)loadView {

availableNetwork = NO;
BOOL returnVal = [self isDataSourceAvailable];
availableNetwork = returnVal;
...
And there you have it. It's a one shot deal here, if connectivity returns, the flag won't be reset. You could always run a NSTimer that checks every now and then, or I am sure there is some kind of notification of that kind of change one could listen for... I'm not far enough along with all of this to know yet.

Labels: , ,

 

Tuesday, May 12, 2009

iPhone: UITableView customization

Tuesday, May 12, 2009    1 Comments

I have a UITableView where I am providing my own graphics for the normal and selected states. Because my graphic for the selected state is dark, the dark text in my UILabels in each cell needs to turn to white for legibility. Then of course it needs to be reset afterwards to black on the normal graphic.

After spending a lot of time on forums, Google, and email lists, I have a solution that should have been easier to get to. I received suggestions that I keep a pointer around for the previously selected item, so when a selection is detected I could introspect the current cell, change the text color there, and then change the previous cell's UILabels back to normal. A lot of work, and it's keeping state which is a bad idea. There were mentions of reloadData for the table, etc.

However one thing I overlooked initially was that I was thinking of the table cell as retaining it's selected state. Normally you'd just call up another view from a selection in a table. So I didn't really need to maintain the selected visual state in the table, but I needed to show it to the user.

A fine chap from Apple emailed me that most iPhone applications implement a nice animation for selected cells in a table and that I should follow that paradigm. So that got me digging.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//NSLog(@"%d:%d", [indexPath row], [indexPath section] );
// Need to get to the cell
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *cellLabel = (UILabel *)[newCell.contentView viewWithTag:1];
NSLog( @"%@", [cellLabel text] );
}
That bit of animation basically tells the table to deselect the selected item with animation. This works great but it doesn't reset the text color in the cell itself. That's when I stumbled onto the holy grail solution for me being able to reset the text without having to dig into the cell itself or keep a pointer around for the previous selected cell. It was simple using a property I hadn't run into yet before so I didn't know of it's existence... highlightedTextColor. DOH! That was it!

So pairing the above method with this detail in my cellForRowAtIndexPath, I get exactly what I needed:
...
labelThree = [[[UILabel alloc] initWithFrame:CGRectMake( 65, 34, 200, 25)] autorelease];
labelThree.tag = 3;
labelThree.font = [UIFont systemFontOfSize:10.0];
labelThree.backgroundColor = [UIColor clearColor];
labelThree.textColor = UIColorFromRGB(0x555555);
labelThree.highlightedTextColor = [UIColor whiteColor]; // Why didn't I know about this!!!
labelThree.text = [NSString stringWithFormat:@"%@", [[photoArray objectAtIndex:row] objectForKey:@"rating"]];
...
I set the text color (using a macro) and also the highlighted text color. So what happens is that the background transitions to my dark selected graphic while the text turns white, and upon deselection the text returns back to that 0x555555 color while the dark selected graphic fades away leaving the normal graphic for the cell.

This works perfectly without me keeping state myself anywhere... let the APIs handle all of that crap for me. I burned a whole day on this simple thing, but it takes moments like these to really understand and learn (in my opinion).

Labels: , ,

 

Thursday, May 7, 2009

iPhone: Want to use Hex for UIColor?

Thursday, May 7, 2009    1 Comments

When you need to set basic colors to things in Objective-C, it's really easy:
//This works, but come on...
cell.textColor = [UIColor grayColor];
However, if you want to be more specific normally you need to get the RGBA for the color and set it that way. That's time consuming especially if the color may be tweaked several times before the final is settled upon. In flash we can just use Hex. Now, with this little macro, you can too & it's a HUGE time saver:
#define UIColorFromRGB(rgbValue) [UIColor \
colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (nil == cell) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"cell"] autorelease];
}
cell.textColor = UIColorFromRGB(0x333333);
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.text = @"Testing 1 2 3";
}
Shazaam!!!

Labels: ,

 

Thursday, May 8, 2008

Ignite Boston event

Thursday, May 8, 2008    0 Comments

[via the gaping chasm that is my stockpile of mostly un-read emails]:

O'Reilly is hosting another fun, free Ignite Boston event!

Ignite Boston is happening on Thursday, May 29, from 6 to 10pm at Tommy Doyle's in Harvard Square, Cambridge (www.tommydoyles.com). We're heading back to the venue of our first Ignite, but we're using two floors so we can accommodate more folks.

The evening's keynote speakers are:
  • Jonathan Zdziarski, iPhone maven and author of "iPhone Open Application Development" (This has me more excited than anything else right now)
  • John Viega, security guru and author of many O'Reilly titles, including the upcoming "Beautiful Security"
We're now accepting proposals for lightning talks, so if you want to speak for five minutes about a cool, new, or exciting topic, submit your idea here:

http://ignitenight.thirdeye.railsplayground.net/events/form/ignite_boston_3

Presentation Guidelines:
  • Be no longer than 5 minutes.
  • Be on an innovative topic (no sales pitches or launches, please!).
  • Be viewable on a PC with standard AV equipment.
Want to come to the event? RSVP at IgniteBoston at oreilly dot com for the chance to win $300 worth of O'Reilly books of your choosing. You must be present to win.

Stay in the loop by visiting our blog for updates, speaker lists, and other info:

http://www.oreillynet.com/ignite/blog/

See you there!

And while you're waiting for Ignite...our friends at BarCampBoston are running another BarCamp on May 17th and 18th. BarCamp is a free unConference where you can participate in discussions, demo your projects, or join into another cooperative event.

Find out more and register at http://www.barcampboston.org/

Labels: , , ,

 

Friday, July 6, 2007

Flash on the iPhone...

Friday, July 6, 2007    0 Comments

Okay, so here are rumors galore about the Flash Player coming to an iPhone sometime in the future. There are three things that I can see wrong with the player on the iPhone (and with some genius they could all be sorted).

  1. Battery life. Someone needs to make a player that isn't Flashlite, but consumes a lot less CPU to do its rendering - taking advantage of the hardware more. I am sure the iPhone does this on its own already with all the speedy transitions. Give that to the Flash Player, and that would be a big help.
  2. Edge sucks, so we'd have to worry a little about the size of most Flash content, how it gets cached and eats memory, and how it would perform on a slow connection (something most of us have simply forgotten about). A caching mechanism or something could possibly help.
  3. The iPhone gesturing might get confused with Flash content underneath it that also sucks up mouse events? Probably not, but maybe. Depending on how the Flash was developed, it could make things weird, especially for full-screen Flash apps and websites. This is probably the easiest thing to fix.

Labels: ,