part of the groupelephant.com family
beyond corporate purpose

Category: Cocoa

Safe type casting in Obj-C

05 Feb 2016

Althought Swift is gradually taking over Objective-C, we still use Objective-C for most of our iOS apps. And recently we ran into the following problem.

Normally to safe-cast an object in Objective-C, you would write something like:

- (void)someAction:(id)sender {
    if ([sender isKindOfClass:[UIButton class]]) {
        ((UIButton *)sender).enabled = false;
    }
}

This is quite lengthy and cumbersome to write. So someone mentioned the following on our Slack channel:

We need dynamic_cast in Objective-C.

So, after a quick search on DuckDuckGo, we found an elegant solution on Stack Overflow.

Create a category for NSObject, named NSObject+Cast. In the header file place the following class method definition:

#import <Foundation/Foundation.h>

@interface NSObject (Cast)

+ (instancetype)cast:(id)from;

@end

And in the implementation file:

#import "NSObject+Cast.h"

@implementation NSObject (Cast)

+ (instancetype)cast:(id)from {
    if ([from isKindOfClass:self]) {
        return from;
    }
    return nil;
}

Now you can rewrite the example at the top as:

#import "NSObject+Cast.h"

//...

- (void)someAction:(id)sender {
    [UIButton cast:sender].enabled = false;
}

Look how elegant this is.

Appearance in iOS 5

27 Mar 2012

Since iOS 5 as a developer you can finally change the tint/design of some elements without much trouble. Changing the background of your UINavigationBar just became so much easier.

@selector(appearance)

The classes that support the UIAppearance protocol have access to an appearance selector. This returns the appearance proxy for the receiver.

It’s on this proxy that you can call selectors like setTintColor:, setBackgroundImage:forBarMetrics:, etc…

Here is a list of classes that are supported:

  • UIActivityIndicatorView
  • UIBarButtonItem
  • UIBarItem
  • UINavigationBar
  • UIPopoverController
  • UIProgressView
  • UISearchBar
  • UISegmentedControl
  • UISlider
  • UISwitch
  • UITabBar
  • UITabBarItem
  • UIToolbar
  • UIView
  • UIViewController

Change the navigation bar background

Here is a quick example how you can easily set the background image of a navigation bar for the whole application.

Add the following line of code to your application:didFinishLaunchingWithOptions: in your AppDelegate:

[[UINavigationBar appearance] 
		setBackgroundImage:[UIImage imageNamed:@"navbar.png"]
		forBarMetrics:UIBarMetricsDefault];

This will set the navbar.png image as a background on your navigation bar for all the navigation bars in your application.

Appearance depending on containment

But what if you want your popover to have the default blue UINavigationBar, but you just changed the appearance for the application. Than you can add this line of code:

[[UINavigationBar appearanceWhenContainedIn:
		[UIPopoverController class], nil] 
				setBackgroundImage:nil 
				forBarMetrics:UIBarMetricsDefault];

When the UINavigationBar is used inside a UIPopoverController, then the background image for the navigation bar will be nil, and therefore the default background will be shown.

You can use UIPopoverController, your custom UIViewController classes, etc… as the containment classes.

What selectors are available on a class

How to find out which selectors can be accessed by the appearance proxy? Just go to XCode and go to the header file of a class, for example UINavigationBar.

In this header file you find the methods supported by the proxy by searching for the UI_APPEARANCE_SELECTOR constant. This is located behind the method definition.

You notice that the UINavigationBar appearance proxy supports the following selectors:

  • tintColor
  • setTintColor:
  • setBackgroundImage:forBarMetrics:
  • backgroundImageForBarMetrics:
  • titleTextAttributes
  • setTitleTextAttributes:
  • setTitleVerticalPositionAdjustment:forBarMetrics:
  • titleVerticalPositionAdjustmentForBarMetrics:

Reference

Cocoapods

30 Jan 2012

Cocoapods

In all of our apps, whether they are web-apps or iOS apps, we make extensive use of different frameworks. These help us to save time by abstracting out stuff that’s needed in a lot of different projects.

We also maintain some of our own frameworks: * Tin Makes consuming webservices in iOS a lot easier. * Coby Adds some functionality we know and love from Ruby into several default Obj-C classes.

But these frameworks grow together with our projects, so copying these into several projects can become tedious and managing different versions would make things too complicated.

Ruby has Bundler to solve this problem. Just create a gemfile that lists every “gem” you use, Bundler will install these gems and keep track of the versions in the “Gemfile.lock”.

Now there is a similar system for Cocoa, called Cocoapods.

Installing cocoapods

Cocoapods runs on macruby, so installing it using RVM is a breeze:

$ rvm install macruby

Then simply setup cocoapods like this:

$ rvm use macruby
$ macgem install cocoapods
$ pod setup

And now we’re ready to start using Cocoapods.

Adding Cocoapods to a project

Using Cocoapods in a new project is quite similar to using Bundler in a rails project. We start by adding a Podfile which could look like this:

platform :ios

dependency "AFnetworking",
  :git => "git://github.com/AFNetworking/AFNetworking.git"

dependency 'Tin',
  :git => "git://github.com/Reprazent/Tin.git",
  :commit => "baf1c407f74dd6ca6c6c8c46bc0a3a565e79d3b6"

dependency 'coby',
  :git => "git://github.com/pjaspers/coby.git"

dependency "MagicalRecord"

The platform can either be :ios or :osx.

Note that, just like with Bundler, you can specify a git repository, and even a commit or tag to reference your dependency. This makes it a lot easier to fix versions and avoid breaking the project when updating the dependencies.

The dependencies referencing a git repository must contain a “.podspec” file. When no repository is specified, Cocoapods looks for the podspec in it’s own spec-repository. There’s already a bunch of frameworks that are available, and more are sure to follow.

Now we can enable Cocoapods for our project by running pod install for the first time.

$ pod install demoproject.xcodeproj

This will clone the git repositories of the pods specified in a new “Pods” directory in your project root. Just like Bundler it will create a “Podfile.lock” which holds the references to the current version of the pods and its dependecies. Together with a new workspace we can use to open our project in XCode. There we will see that there are actually two projects added to the workspace.

screenshot workspace

One containing your regular project, and one containing the pods, this makes it easier to view the source code of these pods while working.

When building your project your pods will be built into “libPods.a” before building the project itself.

screenshot libPods

Now you’re good to go. When adding new pods or updating existing ones in your podfile just run pod install again to update your “Podfile.lock” and adding or updating the Pods.

When multiple build configurations are needed, we also need to add these in the “Pods.xcodeproj”.

screenshot build configuration

You should also set “Skip Install” to yes in your build target in order to create archives for different build types.

screenshot skip install

thanks Verbeeckx for figuring these last two out

Creating your own Cocoapods

To be able to add our own frameworks into our own projects easily, we had to add a “.podspec” to the repos. These podspecs look very similar to the gemspecs in Ruby.

Pod::Spec.new do |s|
  s.name     = 'Tin'
  s.version  = '0.0.1'
  s.license  = 'MIT'
  s.summary  = 'Tin makes the internet easier in Cocoa.'
  s.homepage = 'https://github.com/pjaspers/Tin'
  s.author   = { 'pjaspers' => 'piet@jaspe.rs' }

  s.description = 'An optional longer description of Tin.'

  s.source   = { :git => 'https://github.com/pjaspers/Tin.git' }

  s.source_files = 'Classes',
                   'Classes/**/*.{h,m}',
                   "Tin",
                   "*.{h,m}"

  s.dependency 'JSONKit'
  s.dependency "AFNetworking", "~> 0.8.0"
end

In this podspec we set the source, where Cocoapods can find the repository to clone. We specify where the required classes can be found within the repository. We can also set several dependencies, specifying the tags that should be used.

Note that adding a dependency like this, it needs to be present in the spec repository of Cocoapods. When the correct tag or framework isn’t present in the repo yet, we can add it ourselves in the Podfile of the project we are using the framework in. In the case of Tin, we had to add “AFNetworking” separately in the Podfile because the highest available version from Cocoapods was 0.7.0.

I suspect Cocoapods will gain a lot in popularity in the future, because it makes adding dependencies to projects much easier. As it gains in popularity, more frameworks will become available.

I suggest you get over to the Cocoapods github and give it a try.

Creating a Quick Look plugin

27 Jan 2012

I’ve been searching for some decent documentation on how to create a Quick Look plugin. It took me several months before I finally found a way to develop the plugin. With many thanks to a soon to be released book by The Big Nerd Ranch.

For those wondering on what Quick Look does, here is Apple’s explanation.

In this blog post I’ll show you how to…

  • create a simple Quick Look plugin

  • support a custom file type

  • debug your plugin with Xcode

Setup your Xcode project

Now let’s start developing!

  • Create a new Quick Look project Quick Look Project

  • Change the extension for both GenerateThumbnailForURL.c & GeneratePreviewForURL.c to .m. (This allows you to use Objective-C code and frameworks in these files)

Support an extension

In this example I want to support the .10to1 extension so that I can preview this file as a text file.

Below is an example on what you have to add to the Info.plist to support the .10to1 extension.

<key>UTImportedTypeDeclarations</key>
<array>
  <dict>
    <key>UTTypeIdentifier</key>
    <string>be.10to1.quicklook</string>
    <key>UTTypeDescription</key>
    <string>10to1 document</string>
    <key>UTTypeConformsTo</key>
    <array>
      <string>public.data</string>
    </array>
    <key>UTTypeTagSpecification</key>
    <dict>
      <key>public.filename-extension</key>
      <array>
        <string>10to1</string>
      </array>
    </dict>
  </dict>
</array>	
<key>CFBundleDocumentTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>QLGenerator</string>
    <key>LSItemContentTypes</key>
    <array>
      <string>be.10to1.quicklook</string>
    </array>
  </dict>
</array>

Be aware, the default project template for a Quick Look plugin already has the following line of code included in the plist.

<key>CFBundleDocumentTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>QLGenerator</string>
    <key>LSItemContentTypes</key>
    <array>
      <string>SUPPORTED_UTI_TYPE</string>
    </array>
  </dict>
</array>

If you want to create a plugin for an existing UTI type, you just have to change SUPPORTED_UTI_TYPE string with for example public.png for a PNG image.

But if your plugin supports a non existing UTI type you have to define it in the plist under the UTImportedTypeDeclarations key. If you want to find out to what UTI type your file conforms to, just use the mdsl executable and you’ll find the UTI type(s) in key kMDItemContentTypeTree.

mdls FILENAME

More information can be found here.

Render the preview

This is the view shown after the space button is pressed. This will display the content of the .10to1 file.

  • Add the Cococa framework

  • Import <Cocoa/Cocoa.h> in the GeneratePreviewForURL.m file

  • Add some generation code inside the GeneratePreviewForURL function as done in this file

Render the thumbnail

This is the icon with a generated content that is shown in your Finder window.

  • Add the WebKit framework

  • Import <Cocoa/Cocoa.h> & <WebKit/WebKit> in the GenerateThumbnailForURL.m file

  • Add some generation code inside the GenerateThumbnailForURL function as done in this file

Debugging

But how do we debug a Quick Look plugin? You can’t just run it as an executable. Therefore we use qlmanage provided by Apple.

We first have to copy the plugin to ~/Library/QuickLook after a successfull build, otherwise your plugin will not run correctly with Xcode.

  • Add a New Copy Files build phase to your target

  • Select Absolute Path and enter ~/Library/QuickLook as the Subpath

  • Add the generated file to the files list

    Copy Files

Run the plugin using qlmanage.

  • Because you can’t select qlmanage in the Finder you have to copy the binary to your project root
cp /usr/bin/qlmanage PROJECT_ROOT/.
  • Edit your scheme and select the Run/Info tab

  • Select the qlmanage executable in your project root

    qlmanage

  • In the Run/Arguments tab you can add the folling Arguments Passed On Launch to render the preview:

-p dummy.10to1

Next up, run the application and the dummy.10to1 file will be used as the selected file.

To test the thumbnail generation you could to the same, add the same argument but with -t in the Arguments tab:

-t dummy.10to1

But this didn’t to the trick (I have no idea why…), so you can manually run this command in your Terminal in order to test the preview generation:

qlmanage -t dummy.10to1

The result

After all this we have created our Quick Look plugin.

If you want do distribute the plugin, just Archive & Save it.

Open a .10to1 file and enjoy the plugin! :)

The plugin

Some utilities

  • Reload the Quick Look generator list
qlmanage -r
  • List the UTI types for the given file, very handy when defining your supported UTI format
mdls FILE

Reference

Core Data Versioning

28 Nov 2011

Every time I have to do a small (read: very simple) migration in an iOS-project I have to dive into Google to find out how it works. I practically never get to it anymore, but I finally decided to write a post about it on this blog.

Migrating what and when?

I only use migrations and version models when an application is already used in production. Core Data will not automatically update its database when you change something in it. The app will just crash… And I don’t think that’s what the user wants. The app won’t always crash: sometimes the changes just aren’t executed and there will occur a problem when you try to call a method related to the changes.

A new version

Go to Editor > Model Version in order to create a new version of your Core Data Model. Choose a name and select the Model you want to use as the starting point, this will be the database you currently use in the production app.

A new version

Now that we’ve created a new version we can just apply some changes to it. It’s just a copy of our old xcdatamodel file.

The last thing we have to do is set this version of the Core Data Model as our default version. In your project navigator select the .xcdatamodeld container in which the 2 versions reside. Open the Utilities pane on the left and select the File Inspector. Here you can change your Current version of your model.

Set the default version

Simple mapping

The mapping model is the place where you tell Core Data how to handle model changes between 2 versions. You don’t always need a mapping model, when you add or remove a column/model for example. But when you want to do more complex migrations like renaming a column or filling the added columns with new values, then this is the way to go.

Add a new “Mapping Model” file to your project, and follow the steps to complete the creation.

Create a mapping model

In this model you can see all the Entity Mappings with the changes between the 2 versions.

Here is an example on how you rename a field (name to fullName) and connect the data between the 2 Core Data Models:

The mapping

After the migration the field will be renamed, and the data will be preserved.

Custom mapping

When we want to do a bit more advanced mapping, we’ll have to subclass NSEntityMigrationPolicy in order to get the result we want.

In the next example we will autofill the newly added field (companyName) with a default value ‘10to1’.

Create a new file called CustomPhotoMigration that subclasses from NSEntityMigrationPolicy and add the following code to the implementation class. You can choose the name of the file, doesn’t really matter.

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)instance
                                      entityMapping:(NSEntityMapping *)mapping
                                            manager:(NSMigrationManager *)manager
                                              error:(NSError **)error {
    NSArray *_properties = [mapping attributeMappings];
    for (NSPropertyMapping *_property in _properties) {
        if ([[_property name] isEqualToString:@"companyName"]) {
            NSExpression *_expression = [NSExpression expressionForConstantValue:@"10to1"];
            [_property setValueExpression:_expression];
        }
    }
    
    return [super createDestinationInstancesForSourceInstance:instance 
                                                entityMapping:mapping 
                                                      manager:manager 
                                                        error:error];
}

Once the file is created you’ll have to tell the mapping model to use our custom policy. Go to your .xcmappingmodel file and click on the entity mapping you would like to customize. In the Mapping Model Inspector pane on the right, just fill in the class name of your custom policy. In our case this will be CustomPhotoMigration.

The custom mapping policy

This should auto fill the companyName field on every existing record with “10to1” the next time you run the app.

For the record: you don’t have to run a script or something like that to perform the migration. Just run your application and it will be executed for you.

Creating UIColors with CSS strings

17 Jan 2011

Since Cocoa development is still a bit fresh for me, every little exercise I can get to enhance my skills is welcome. So when Piet suggested that somebody should write a Category to create UIColors from a CSS string, I saw it as a perfect challenge. I assume you know how to create Categories. If you don’t, Jelle has an excellent blogpost about them.

Prelude: a UIColor from a (hex) encoded integer

First step: see if I could create a color from a integer value. This had nothing to do with strings, but I figured it could come in handy later, and from a code point of view it’s just as easy to use as a string.

So, our input is an unsigned integer, and we get a UIColor as a result. The input integer is assumed to have the four color components in it: - alpha transparancy - red - blue - green

These are mapped like this in the integer:

    +-------+-------+-------+-------+
    | alpha |  red  | green |  blue |
    +-------+-------+-------+-------+
bit        24      16       8       0

If we omit the alpha part (that is, if the value passed is equal or smaller than 0xFFFFFF), we assume the user wanted a full solid color, and so we take the default value of 0xFF for the alpha component. Otherwise, we take the value in the upper 8 bits. This means that it’s impossible to a fully transparent color this way, but I don’t think that’s much of a problem. A solution could be adding an overload to force the alpha part from the value (this is left as an exercise for the reader).

Anyway, it sounds more complicated than it is. We just need a combination of the shift right operator >> and the AND operator &:

CGFloat red, green, blue, alpha;

red = ((CGFloat)((hex >> 16) & 0xFF)) / 255.0;
green = ((CGFloat)((hex >> 8) & 0xFF)) / 255.0;
blue = ((CGFloat)((hex >> 0) & 0xFF)) / 255.0;
alpha = hex > 0xFFFFFF 
            ? ((CGFloat)((hex >> 24) & 0xFF)) / 255.0 
            : 1;

return [UIColor colorWithRed:red 
                       green:green 
                        blue:blue
                       alpha:alpha];

Since the colorWithRed:green:blue:alpha: class method on UIColor - which is what we use to create the actual color - expects each component as a float value (ranging from 0.0 to 1.0), all that’s left is to divide each component by 0xFF to get to the correct float value. And that’s about it.

A UIColor from a CSS string

Now, we want to create a UIColor from a CSS like color string. I mean the hex encoded colors, not the named colors: #ffcc00 or #345a13 or even #ccc. Stuff like that. I also wanted it to take a bit further and allow for transparancy. Something you can’t do with CSS encoded colors, but I would be fairly easy to support.

So, how do we parse a string like that. There are several cases to consider: - a nil string - an empty string - a string with a # in front, but also without it - what about non-hex characters? - there’s a number of combination we can use regarding length: 6 and 8 characters are the most obvious, but also 3 and 4 are possible (#abc equates to #aabbcc). - but what when there’s less than 3 characters? - or 5. Or 7? - and what do we do when we have more than 8?

Let’s see how we handle those.

The braindead cases

First, the really simple cases: - nil or empty just returns black. - we strip the # in front if it’s there. In CSS this is necessary because it indicates a hex encoded color and not a color name. Since we don’t support color names, it’s of no use to us. - non-hex character are kept, and are skipped when converting the string to a number. This might not give the “desired” results, but at least it works.

The common cases

Then, the common cases are 6 or 8 characters. That’s either #abcdef or #abcdef01. The first case is just the three color components, the latter sports an additional alpha component (at the start). We grab each component as a substring, and convert each component to an actual integer value.

// six characters: #aabbcc, default alpha to 'FF'.
a = @"FF";
r = [css substringWithRange:NSMakeRange(0, 2)];
g = [css substringWithRange:NSMakeRange(2, 2)];
b = [css substringWithRange:NSMakeRange(4, 2)];

// eight characters: #22aabbcc
a = [css substringWithRange:NSMakeRange(0, 2)];
r = [css substringWithRange:NSMakeRange(2, 2)];
g = [css substringWithRange:NSMakeRange(4, 2)];
b = [css substringWithRange:NSMakeRange(6, 2)];

The shorthand cases

So far so good. What about the shorthand equivalents: #abc (meaning #aabbcc) and #abcd (meaning #aabbccdd). They’re quite easy too. Instead of grabbing 2 characters for each component, we just grab one and duplicate it so we get two. And then we convert those to an integer.

a = [css substringWithRange:NSMakeRange(0, 1)];
a = [a stringByAppendingString:a];
r = [css substringWithRange:NSMakeRange(1, 1)];
r = [r stringByAppendingString:a];
g = [css substringWithRange:NSMakeRange(2, 1)];
g = [g stringByAppendingString:a];
b = [css substringWithRange:NSMakeRange(3, 1)];
b = [b stringByAppendingString:a];

// the code for 3 characters is similar, of course.

Some special cases

Leaves us with some special cases. everything smaller than 3 characters is left-padded with zeroes and treated as a 3 character string afterward. With a fancy goto:

css = [css stringByPaddingTheLeftToLength:3 
                               withString:@"0" 
                          startingAtIndex:0];	
goto three;

Using goto: evil, I know. I considered using a loop and break statements in each if, but that was just plain ugly. I could also have split the handling of each case into it’s own method, and call these where appropriate. That would have been cleaner, but I felt the use of goto was okay for constructs like this. The intention of the whole block is clear enough, so I was okay with it.

The cases for 5 and 7 characters are similar. Just pad a zero in front and switch to the cases with either 6 or 8 characters. The final case, when we have more than 8 characters is pretty simple too: we just take the last 8 characters and discard everything else. That leaves use with 8 characters which we can process as before.

The actual processing

The leaves us with four components. We convert them into a float using scanf (don’t forget to prepend @"0x"!) and pass them to colorWithRed:green:blue:alpha:. And that’s it. Oh, scanf wants a c-string, so we have to ask the NSString instances for it. Don’t use the cString method, since it’s deprecated (because it doesn’t allow you to choose encoding).

// parse each component separetely. This gives more 
// accurate results than throwing it all together in 
// one string and use scanf on the global string.
a = [@"0x" stringByAppendingString:a];
r = [@"0x" stringByAppendingString:r];
g = [@"0x" stringByAppendingString:g];
b = [@"0x" stringByAppendingString:b];

uint av, rv, gv, bv;
sscanf([a cStringUsingEncoding:NSASCIIStringEncoding], 
       "%x", 
       &av);
sscanf([r cStringUsingEncoding:NSASCIIStringEncoding], 
       "%x", 
       &rv);
sscanf([g cStringUsingEncoding:NSASCIIStringEncoding], 
       "%x", 
       &gv);
sscanf([b cStringUsingEncoding:NSASCIIStringEncoding], 
       "%x", 
       &bv);

return [UIColor colorWithRed: rv / ((CGFloat)0xFF) 
                       green: gv / ((CGFloat)0xFF) 
                        blue: bv / ((CGFloat)0xFF)
                       alpha: av / ((CGFloat)0xFF)];

Easy as pie.

Conclusion

It’s quite a lot of code for a simple conversion. But each case is different, so it requires different processing. While this means more code, it also means optimized code for each case. But each case is pretty simple, so the whole is still readable enough.

You can see this in action in a simple iPhone project that features a textfield where you can enter the CSS color code, and this color is applied to a textview below. Nothing fancy, but it gets the point across. You can find the project on GitHub. The .h and .m file can also separately be found in a Gist.

Enjoy.

Custom delegates, the Apple way

03 Jan 2011

When you get into Mac or iOS development, you’ll soon be confronted with delegates. And after a certain period of time when you gain more experience, you’ll start creating your own custom delegate calls.

How Apple uses delegates

Let’s say we want to log the number of characters entered in a UITextView. We’ll have to assign a delegate to the UITextView instance and then implement the textViewDidChange method.

UITextView *textView = [[UITextView alloc] init];
textView.frame = CGRectMake(0, 0, 100, 30);
textView.delegate = self;
[self.view addSubview:textView];

This code snippet creates the UITextView somewhere in the viewDidLoad method from out main UIViewController. Next up: implement the textViewDidChange method.

- (void)textViewDidChange:(UITextView *)textView {
  NSLog(@"number of chars: %i", [textView.text length]);
}

This will log the number of characters entered every time you change something in the UITextView.

But there is a warning

The code above will work perfectly but there will be a warning.

Delegate warning

The warning thrown is this one: class ‘ExampleViewController’ does not implement the ‘UITextViewDelegate’ protocol. It means we have to follow the UITextViewDelegate protocol and set it in the header file like this:

The solution to remove the warning is fairly easy, just add the UITextViewDelegate protocol to the header file.

@interface ExampleViewController :
                   UIViewController <UITextViewDelegate>

To make a class conform to a protocol, you just have to add it in between the < and >.

Create your custom delegate call

Now what if you want a custom view class to call a method in your controller because you think the code functionality belongs to the controller. You’ll first create a property where you can assign the delegate:

@property (nonatomic, retain) NSObject *customDelegate;

Your delegate instance is now available in your class, so when you call the setColor: method on your instance, than you want to call the delegate’s changeColor: method if available in the customDelegate: instance.

You can find more info on the difference between id and NSObject on this blog. There reason I’m using NSObject is because it doesn’t generate warnings when using the respondsToSelector:.

In the code below we’ll first check if the method is available, and if so we call it and pass the the current color so the controller can do the rest.

- (void)setColor:(UIColor *)color {
  SEL method = @selector(changeColor:);
  if ([customDelegate respondsToSelector:method]) {
    [customDelegate performSelector:method
                    withObject:color];
  }
}

For those that don’t really know what selectors are, they can read it here

But I want a warning!

It can come in handy to throw a warning to notify the developer that he can or has to implement certain methods in order for the delegate to work properly.

You should first create a protocol that defines the @required and @optional methods that the delegate class can or has to implement. And you can do this in the header file (or in another file).

@protocol ExampleViewDelegate
@required 
- (void)changeColor:(UIColor *)color;
@optional
- (void)clearColor;
@end

The methods below the @required directive are all obligatory. The ones below @optional are not. Now this won’t throw any warning it’s just a preparation for what is coming.

Now you have to do a small modification to the @property where the customDelegate is defined. We have to tell the customDelegate that it should implement the ExampleViewDelegate protocol.

@property (nonatomic, retain)
                  id<ExampleViewDelegate> customDelegate;

When we now assign the delegate, a warning is thrown when the protocol is not implemented (cfr. How Apple uses delegates). So we just have to implement the protocol to stop the warning from appearing.

@interface ExampleViewController
                 : UIViewController <ExampleViewDelegate>

And when we don’t implement the changeColor: method, then another warning will arise, because this method is set to required in the protocol.

Now let’s go and delegate!

Reference

You can find a small project with the UITextView and the custom delegate examples on Github

Stop worrying about Core Data

10 Dec 2010

Core Data is one of the frameworks I find a bit scary to use when I’m developing iOS applications. I always felt like I was developing something that wasn’t really under my control. That was until Piet pointed out this framework to me: Mogenerator.

Draw your models

The first thing you should do is create your models in the XCode Modelling Tool. More information on this topic can be found here.

Here is an example of my Core Data model:

Core Data model

Let’s generate

When you created a model with some attributes, you can use mogenerator to generate your classes (or regenerate them, when you did some editing).

I pass two arguments to mogenerator to generate the files.

  • -m the location of your data model
  • -O the location of your model classes

Here is an example:

mogenerator -m TestProject.xcdatamodel -O Classes

This command generates all the classes for your models created with the modelling tool. Be aware: You still have to add them to your XCode project in orde to use them.

What is generated?

For each model four class files are generated. The 2 class files starting with a dash should never be modified. When you want to add custom methods/logix, you can do this in the other 2 generated classes. They extend from the dashed-classes.

Generated Files

Every attribute can now be accessed as a property on the object. This makes it very easy to assign new values.

Don’t forget that when you change your model in the modelling tool, that you’ll have to run the mogenerator command again in orde to regenerate the dashed files.

Some code

The code snippet below will create a Person object and assigns some values to the attributes. When everything is assigned the changes are saved.

// NSManagedObjectContext from somewhere in your system
NSManagedObjectContext *moc = ...

Person *p = [Person insertInManagedObjectContext:moc];

p.firstName = @"Zot";
p.lastName = @"Franske";
p.age = [NSNumber numberWithInt:25];

[moc save:nil];

Check these out!

Here are some links that helped me during development:

Creating a PDF map application

21 Oct 2010

For a recent project I had to create a small PDF ‘Google Maps like’ viewer for iOS. The viewer needs to handle zooming and dragging to navigate the PDF page. And for performance issues it’s best to load the PDF using tiles.

In some code snippets below, I’ll show you how it’s done. Make sure to check out Github for an example XCode project with comments for almost every line of code.

Setup

This is the setup I used in this tutorial:

  • I have a UIViewController with an attached XIB file.
  • I created a map UIView that is a subview belonging to the controller’s view (which is a UIScrollView).
  • I built a CATiledLayer that is added as a sublayer to the map view.
  • That CATiledLayer is responsible for the tiling of the PDF map.

Create the PDF

Here is how you create the PDF. First grab a NSURL instance with the PDF’s location, and then you create the CGPDFDocumentRef that’s needed to construct the CGPDFPageRef which represents a page.

NSString *filePath = [[NSBundle mainBundle] 
                          pathForResource:@"filename"
                          ofType:@"pdf"];
NSURL *pdfURL = [NSURL fileURLWithPath:filePath];
CGPDFDocumentRef *myDocumentRef = 
        CGPDFDocumentCreateWithURL((CFURLRef) pdfURL);
CGPDFPageRef *myPageRef = 
        CGPDFDocumentGetPage(myDocumentRef, 1);

Tile everything

I want the PDF to load like the Google Maps application is loaded. That’s why you have to use the CATiledLayer, this does everything. You can specify the size, level of detail (for vectorized PDF’s)… For more info check out the Apple Developer Reference

Don’t forget to set the delegate for the CATiledLayer, this makes sure the drawLayer method is called.

CATiledLayer *tiledLayer = [CATiledLayer layer];
tiledLayer.delegate = self;
tiledLayer.tileSize = CGSizeMake(200, 200);
tiledLayer.levelsOfDetail = 1000;
tiledLayer.levelsOfDetailBias = 1000;
tiledLayer.frame = 
        CGRectIntegral(CGPDFPageGetBoxRect(myPageRef, 
                kCGPDFCropBox));

Afterwards the tiled layer delegate will do the rest if you implement this method:

- (void)drawLayer:(CALayer *)layer 
        inContext:(CGContextRef)context {
  CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
  CGContextFillRect(context, 
                    CGContextGetClipBoundingBox(ctx));
  CGContextTranslateCTM(context, 
                        0.0, layer.bounds.size.height);
  CGContextScaleCTM(context, 1.0, -1.0);
  CGContextConcatCTM(context, 
        CGPDFPageGetDrawingTransform(myPageRef, 
                        kCGPDFCropBox, layer.bounds, 
                        0, true));
  CGContextDrawPDFPage(context, myPageRef);
}

It looks a bit low level but it’s fairly easy to get it to work.

Zooming

Now the last thing you have to do is to tell the UIViewController which UIView has to be zoomed. That is why you need to set the delegate of the UIScrollView. This makes sure the following methods is called:

- (UIView *)viewForZoomingInScrollView:
                (UIScrollView *)scrollView {
	return mapView;
}

Here you return the UIView that needs to zoom depending on the finger gestures.

References

  • You can find an example iPad project on Github.
  • Here’s an example project made by Apple.

I love Categories

24 Aug 2010

One thing I really love about Objective-C is Categories.

What are they?

Categories make it possible to add a method to a class without subclassing.

For example:

Let’s assume that you want to create a method isEmpty. And you want to find a clean way to ask the NSString instance for it.

Then we’ll create a category on the NSString class in which we define a method isEmpty that returns a BOOL value.

How to create them?

Create a header (NSString+UtilityMethods.h) and an implementation file (NSString+UtilityMethods.m).

You define the category and the method in the header file like this:

@interface NSString (UtilityMethods)
- (BOOL)isEmpty;
@end

And in the implementation file like this:

@implementation NSString (UtilityMethods)
- (BOOL)isEmpty {
  return [self count] == 0;
}
@end

Now you can use the isEmpty method on every instance of NSString. (Don’t forget to include the NSString+UtilityMethods.h file)

NSString *text  = @"";
if ([text isEmpty]) {
  text = @"Categories are awesome";
}
NSLog(text);

The above snippet will print out “Categories are awesome”.

Another example with UIColor

This is how I clean up my code with categories. I create a category on UIColor to add methods that return every different UIColor used in my application. This way I only have to modify the colors here in orde to change them in the app.

Header file:

@interface UIColor (ApplicationColors)
+ (UIColor *)backgroundColor;
+ (UIColor *)textColor;
@end

Implementation file:

@implementation UIColor (ApplicationColors)
+ (UIColor *)backgroundColor {
  return [UIColor redColor];
}
+ (UIColor *)textColor {
  return [UIColor whiteColor];
}
@end

Nice isn’t it?

Railscasts Xcode theme

25 Sep 2009

I use textmate for all things ruby, rails, txt, blog, xml, … and while doing so I enjoy using the railscasts textmate theme from Ryan Bates. Nice, clean and easy on the eyes.

Since I was never really happy with the standard Xcode themes I decided to port the textmate theme to an Xcode theme.

After days of work, or was it weeks, I got it right. I think.

No really, it just took about 5 minutes clicking around in the Xcode theme editor. You can find it here.

Enjoy!