PIXEL
DOCK

I like the smell of Swift in the morning…

How to compare two NSDecimalNumber values

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , , | No Comments »

Recently I had to use NSDecimalNumber objects to avoid the rounding errors that come with float values. While comparing two float values is as easy as it gets comparing two NSDecimalNumbers works a bit different:

NSDecimalNumber is a subclass of NSNumber so you have to use NSNumber’s compare: method to compare the values:

NSDecimalNumber *number1 = [NSDecimalNumber numberWithFloat:0.1];
NSDecimalNumber *number2 = [NSDecimalNumber numberWithFloat:0.2];

// check if number1 is less than number2
if ([number1 compare:number2] == NSOrderedAscending {
   NSLog(@"number1 is less than number2")
}

CoreData error: “The model used to open the store is incompatible with the one used to create the store”

Posted: | Author: | Filed under: iOS, Xcode | Tags: | 4 Comments »

When I started to play around with CoreData for the first time, I came across a strange error. The error description said “The model used to open the store is incompatible with the one used to create the store”. At first I just couldn’t figure out what the problem was. As soon as I tried to access the NSManagedObjectContext the app crashed.

The solution is really simple: Whenever you change something your CoreData data model, you have to delete the old compiled app from the simulator or device. CoreData uses SQLite3 which is nothing more than a file in your app’s filesystem. If you do not delete the old version of your app the file remains in the file system and does not reflect the changes that you made in the data model. The SQLite file will only be deleted when you delete the whole app.

So the golden rule is: Whenever you change something in the data model of your CoreData driven App, make sure to delete the old version of the app before compiling and running the new one.

How to avoid a blocked user interface during UIView animations with blocks

Posted: | Author: | Filed under: iOS | Tags: , , | No Comments »

When you use the new iOS 4 UIView animations with blocks you will notice that the animations will lock user interaction while they are running. With really short animations this might be uncritical but it is surely unacceptable when a user cannot scroll a UIScrollView because another UIView is being animated with a longer duration like 2 seconds.

The reason for this “frozen UI problem” lies in the default value for the options mask of UIView’s animateWithDuration: methods. Apparently the option UIViewAnimationOptionAllowUserInteraction is not part of the default options mask. Unfortunately only one of the three animateWithDuration: methods allows to set the animation options. So to avoid the freezing of the user interface use animateWithDuration:delay:options:animations:completion: like in this example where a view is faded out over the duration of two seconds without blocking the user interface:

[UIView animateWithDuration:2.0 
        delay:0.0 
        options:UIViewAnimationOptionAllowUserInteraction 
        animations:^(void){yourview.alpha = 0.0} 
        completion:^(BOOL completed){yourview.hidden = YES;}];

Set the height of a UIWebView to the height of it’s HTML content

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , | 19 Comments »

Sometimes you need to know the height of a html document that is loaded into a UIWebView. For example if you want to set the height of the UIWebView to the height of its content.

The logical way would be this:

1. add a Javascript function to the HTML that returns the height of the document.
2. add the call to this Javascript function to the UIWebViewDelegate’s method webViewDidFinishLoad:

This sounds easy, but if you look at the results you’ll realize that the values for the document height are not correct and pretty random.

The problem is: webViewDidFinishLoad: get’s called when the HTML is fully loaded BUT it still has to be fully rendered before you can determine its height!

So the call comes too early. You could delay the call but that’s not the way to go here as this is still unreliable and you want the height as soon as possible.

The solution is to revert the process. Instead of Objective-C asking the Javascript for the height, have the Javascript call Objective-C as soon as it knows the height of the document.

Here’s how to do it:

1. Add a Javascript function to the HTML document

Add this function either to the head or the body of your HTML

This function gets called as soon as the HTML document is fully rendered. It puts the height into an URL and sends a request with this URL.
If you are asking yourself why I use “ready://” instead of “http://”: I do this because sending a request is the only way, how the Javascript inside a UIWebView can send messages to the UIWebViewDelegate. So this is not a “real” HTTP Request. Instead you can use the URL scheme to make things easier on the Objective-C part (as you will see in step 2).

2. Intercept the request in the UIWebView’s delegate

A UIWebViewDelegate has a method, that get’s called everytime the HTML inside the UIWebView sends a request. Here’s what to do in this method:

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { 
  NSURL *url = [request URL]; 
  if (navigationType == UIWebViewNavigationTypeOther) { 
    if ([[url scheme] isEqualToString:@"ready"]) { 
      float contentHeight = [[url host] floatValue]; 
      CGRect fr = webview.frame; 
      fr.size = CGSizeMake(webview.frame.size.width, contentHeight); 
      webview.frame = fr; 
      return NO; 
    } 
  }
  return YES; 
}

Here you can see why I used the custom URL scheme “ready”. It makes it easy to identify my request. The URL that the javascript requested has the format “ready://1200” meaning that the HTML content is 1200px high. So I use the scheme “ready” to identify my request and the “host” part of the URL to sent the actual height. Then it’s easy to set the height of the UIWebView to the height of the HTML document’s content.

Return NO to stop the UIWebView from trying to load the request. Don’t forget to return YES for all other requests or the HTML content won’t even get loaded into the UIWebView in the first place.

Build an ad hoc distribution .ipa file for a project that includes static libraries (e.g. Open Ears)

Posted: | Author: | Filed under: iOS, Xcode | Tags: | No Comments »

When I wanted to generate an .ipa file for my latest project I ran into the following problem: my project uses the Open Ears library for speech recognition. Open Ears is included as a static library. When I tried to archive the app to share it with others I could not create the .ipa file. After clicking on “share” the option to save it as an .ipa file was grayed out with the message “No Packager exists for the type of archive”. So no chance to build the necessary .ipa file that is needed for ad hoc distribution.

The solution for this problem is quite simple but it took me some time to find it. The problem is that you have to make sure that the static libraries are included in the application binary. You achieve this by setting the “Skip Install” build setting to “YES” for the libraries: Right click on the library project icon (the blue one) and find the “Skip Install” property in the “Deployment category” in the build settings. Set it to “YES”.

Important: leave the “Skip Install” property of your main project at “NO”.

When you archive your project now, it should offer the save as .ipa option in the share section.

UIColor+colorWithHex: a category to get an UIColor from a hexadecimal integer value or string

Posted: | Author: | Filed under: iOS, Objective-C | 5 Comments »

I always find it really cumbersome to instantiate an UIColor object from a hexadecimal color value. To make life a bit easier I wrote this category for the UIColor class:

@interface UIColor (ColorWithHex) 

+(UIColor*)colorWithHexValue:(uint)hexValue andAlpha:(float)alpha;
+(UIColor*)colorWithHexString:(NSString *)hexString andAlpha:(float)alpha;

@end

@implementation UIColor (ColorWithHex)

+(UIColor*)colorWithHexValue:(uint)hexValue andAlpha:(float)alpha {
    return [UIColor  
                colorWithRed:((float)((hexValue & 0xFF0000) >> 16))/255.0 
                green:((float)((hexValue & 0xFF00) >> 8))/255.0 
                blue:((float)(hexValue & 0xFF))/255.0 
                alpha:alpha];
}

+(UIColor*)colorWithHexString:(NSString*)hexString andAlpha:(float)alpha {
    UIColor *col;
    hexString = [hexString stringByReplacingOccurrencesOfString:@"#" 
                           withString:@"0x"];
    uint hexValue;
    if ([[NSScanner scannerWithString:hexString] scanHexInt:&hexValue]) {
        col = [self colorWithHexValue:hexValue andAlpha:alpha];
    } else {
        // invalid hex string         
        col = [self blackColor];
    }
    return col;
}

@end

Because I often need to convert a hex string (e.g. from a parsed XML file) to an UIColor I added 2 methods to this category. You can now get an UIColor from a hexadecimal uint value or an NSString that can have the 3 following formats: “0xFFFFFF“, “#FFFFFF” or “FFFFFF“.

Just add this category to your project and you can use it like this:

UIColor *col1 = [UIColor colorWithHexValue:0xFFFFFF andAlpha:1.0];
UIColor *col2 = [UIColor colorWithHexString:@"0xFFFFFF" andAlpha:1.0];

You can download the category here.

Feel free to use it.

NSURLConnection: How to avoid blocked file loading during scrolling of UIScrollView or UITableView

Posted: | Author: | Filed under: iOS | Tags: , , | 10 Comments »

When you want to load a file (like a large image) into your app, you don’t want to block the user interface during the loading. It’s always a good idea to load large files asynchronously.

The easiest way to load a file in the background is via NSURLConnection.

NSURLConnection *connection = [[NSURLConnection alloc] 
     initWithRequest:request delegate:self startImmediately:NO];
[connection start];

However, if you use the default settings the connection object will be scheduled in the NSDefaultRunLoopMode. That means that the connection is only executing the request when the app’s run loop is in NSDefaultRunLoopMode.

Now, when a user touches the screen (e.g. to scroll a UIScrollView) the run loop’s mode will be switched to NSEventTrackingRunLoopMode. And now, that the run loop is not in NSDefaultRunMode anymore, the connection will not execute. The ugly effect of that is, that the download is blocked whenever the user touches the screen. And that can be a looong time when the user is scrolling a UITableView, because the download is stopped until the scrolling completely stops. And when the user continues to scroll, the download is blocked again.

Fortunately the solution to this problem is quite simple: You can schedule the connection in another NSRunLoopMode. When you schedule the connection in NSRunLoopCommonModes it will execute in all run loop modes (that have been declared as a member of the set of “common” modes, to be precise). That means that the connection is not only working in NSDefaultRunLoopMode but also in NSEventTrackingRunLoopMode (when the user touches the screen).

[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] 
     forMode:NSRunLoopCommonModes];

Bingo! The async file loading is not blocked anymore during user interaction.

NSXMLParserErrorDomain error 5

Posted: | Author: | Filed under: iOS | Tags: , | No Comments »

Trying to parse an XML file I came across this error message. According to the error message the parser could not parse the XML file so I suspected that my XML was malformed. But it wasn’t.

It turned out the problem was that I used the wrong method to load the XML file.

Instead of using:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"demo.xml" ofType:nil];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath]];

I tried to do this:

NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:[NSURL fileURLWithPath:@"demo.xml"]];

So the problem was that the Parser could not really load the XML. Strangely enough the parser loaded something because otherwise the parser’s init method should have returned nil. But whatever the parser loaded, it surely was not the right stuff. So be sure to get the XML file’s path from the main bundle and feed that to the parser’s init method.

UIFont: Problem when using more than 2 custom fonts of the same Font Family.

Posted: | Author: | Filed under: iOS | Tags: , | 4 Comments »

When you add custom fonts to your iPhone or iPad App you might experience the following problem: If you add more than 2 font variants of the same font family (e.g. “Normal”, “Bold” and “Extended”), only the last two font variants that you added to the project will be usable. The first variant (the one on top of your list) just won’t show up and is replaced by one of the two variants that were added last to the project.

The reason is quite simple: The iOS SDK apparently allows only 2 custom fonts of the same font family!

As a solution you could change the font-family of your font, so that not more than two font variants have the same font-family. Just to be clear: it’s not enough to change the file name of your font. You have to change all the references to the font-family inside your font files.

You can do this with with fontforge. Fontforge requires that you installed the X11 server on your system, but that should not be a problem as it is automatically installed when you install Xcode.

Make sure that you find all references to the font family name after you opened the font in Fontforge:

  1. Open your Font in Fontforge
  2. Goto ‘Element’ -> ‘Font Info’ and change the ‘Family Name’ field
  3. Goto ‘Element’ -> ‘TTF Names’ and change the fields ‘Family’ and ‘Preferred Family’
  4. Goto ‘File’ -> ‘Generate Fonts’ and save your edited font

With this little trick you should be able to use more than two variants of one font.

cocos2d: unofficial installer solves installation problems

Posted: | Author: | Filed under: cocos2d, iOS | Tags: , | No Comments »

When installing the cocos2d framework with the method described in Steffen Itterheims Book “Learn iPhone and iPad cocos2d Game Development” (using the sudo command in the Terminal) the installation did not work properly and threw some errors. When creating a new project in Xcode, the cocos2D templates just would not show up.

After some googling I found this “unofficial” installer that does the dirty Terminal work for you and that works like a charm.

Thanks Steffen for writing that installer!