Tuesday, February 2, 2010

Basic Cappuccino Tutorial


I tried to give a lunchtime talk at NSConference today and it turned out to be a disaster :) All the stuff I wanted to show didn't work right, I wasn't prepared well enough, the projector was too dark and the resolution was too low. Oh well. So I'll just write down what I wanted to show:

I won't tell you how to set up the Cappuccino tools. Just look here and you'll be up and running.

Ok, first we want to do a simple NibApplication. You can actually design your Cappuccino UI in Interface Builder. So, run

capp gen --template NibApplication basicCapp
cd basicCapp

Then turn the xib file into a cib file that Cappuccino understands:

nib2cib Resources/MainMenu.xib Resources/MainMenu.cib

Now open index.html and you should see this:

Let's make the UI a little more interesting. Open MainMenu.xib in Interface Builder. Remove the slider and add a button to make it look like this:


In the Library Classes tab, add on outlet for the text field to the AppController and a "sayHi:" action to it:
Connect the nameTextField outlet to the text field and the button's action to the sayHi: action. Save the file.

Open the AppController.j file in the basicCapp directory and add the outlet and the action to make it look like this:


Now run nib2cib again:
nib2cib Resources/MainMenu.xib Resources/MainMenu.cib

And open index.html. You should be able to enter a name and click "Say Hi" and should be greeted by an ugly JavaScript alert box. Hurray!

Let's add some more stuff to the Nib. Open it again in Interface Builder and add another window to it. Set it to be visible at launch. Add a split view to it, set the resizing mask and add a text field to the left view and a webview to the right one. Connect the "takeStringURLFrom:" action of the text field to the webview. It should look something like this:
Run nib2cib again and open index.html. You should see 2 windows and be able to enter a URL in the textfield and have it load when you press enter:
Great! Now let's add a custom Framework and add it. Open MainMenu.xib in Interface Builder again and add yet another window to it. Add a text field and a custom view to it so it looks like this:


Add a "customView" outlet of type "NSView" to the AppController (in IB) and another action called "setLocation:" (don't forget the colon!). Connect it all up (AppController's customView outlet to the custom view and the text field's action to AppController's setLocation:). Save it and run nib2cib again.
Now get MapKit from github and copy it to the Frameworks directory inside the basicCapp dir:
cd basicCapp/Frameworks
git clone http://github.com/jfahrenkrug/MapKit.git

Now make AppController.j look like this:



Now you should have a 3rd window when you open index.html again. It should look like this and should let you add markers for locations that you search for:


See? It's all pretty easy! Now go ahead and build something great :)

Monday, January 25, 2010

Announcing CPVideoKit for Cappuccino

I've written and published CPVideoKit today, a Cappuccino framework for embedding and controlling YouTube videos. It's a very early version and I plan to add Vimeo and search support.

Here's the Code: http://github.com/jfahrenkrug/CPVideoKit

Here's the Code for the Demo project: http://github.com/jfahrenkrug/CPVideoKitDemo

And here's a short video showing it off:



Enjoy!

Tuesday, January 12, 2010

Invalidate Single URLs in Rack::Cache/Radiant::Cache

I've recently encountered a problem with Rack::Cache: I have a Radiant app with some custom extensions and some of those extensions build pages that are expensive to build. So far I could only purge the cache completely by removing the tmp/cache directory. That's not a nice solution if all I want to invalidate is one page. So after some digging I came up with this tiny script:



It allows me to explicitly purge certain URLs from my Rack::Cache based Radiant::Cache. Please note that this only works if you use the Radiant::Cache standard options. Otherwise you'll have to tweak the metastore that should be used.
Some further reading: http://github.com/svenfuchs/rack-cache-purge.

Thursday, December 24, 2009

Double Click and NSCollectionView

I needed to capture double clicks on NSCollectionViewItems in an app I'm working on. This is unfortunately not something that's supported out-of-the-box. I found this post on CocoaDev helpful. However, since quite a few people will run into this issue, I want to explain step by step how to get this working with as little code as possible. We basically just need one custom NSView subclass and a NSCollectionViewItem subclass and a few connections in IB.
As a basis, we'll use Apple's demo app IconCollection. Download it and open it in XCode.

  1. Open MyViewController.h and add a delegate outlet to the IconViewBox. This will be necessary so we can pass the double click event on to NSCollectionViewItem and from there to the controller. It should look like this:

  2. Open MyViewController.m and alter the hitTest method to allow clicks and add a method to capture the double click to IconViewBox's implementation:

  3. Build and run. When you double click on items, you should see a log message in the console.
  4. Open IconViewPrototype.xib in IB and connect the View's delegate outlet with "File's Owner":









  5. Save IconViewPrototype.xib.
  6. Add a new file called IconCollectionItem (both the .h and the .m).
  7. IconCollectionItem.h:

  8. IconCollectionItem.m:

  9. Open IconViewPrototype.xib again and change the File Owner's class to "IconCollectionItem". Save.
  10. Open Collection.xib and also change the Collection View Item's class to "IconCollectionItem". Save.
  11. Build and run. When you double click now, you should see two log messages in the console.
  12. Finally, we want to be notified of the double click in our controller. So open up MyViewController.h and add a doubleClick method to the @interface:

  13. Open MyViewController.m and add the method implementation:

  14. Build and run: When you double click, the event gets passed on all the way to the controller and you'll see a log message on the console telling you which icon you have double clicked on. That's pretty nifty.
  15. Fin.
So to recap: Build an NSView subclass to capture the double click on an item (already existed in the IconCollection app in the form of the IconViewBox class), that NSView subclass needs a delegate outlet that we connect to the custom NSCollectionViewItem subclass (File's Owner in most cases) and finally the NSCollectionView's delegate outlet has to be connected to your view controller (already existed in the IconCollection app as well). 

If you've come up with a simpler or more elegant way to solve this, please let me know! 

Monday, October 5, 2009

CPOperation: NSOperation for Cappuccino/Objective-J

Update: CPOperation is now an official part of Cappuccino (yay!).

This a just a short public service announcement: I've finished porting NSOperation, NSOperationQueue, NSInvocationOperation and NSBlockOperation to Objective-J. I've called it "CPOperation" and you can get it on github. You can learn something about unit test in Objective-J there as well, because I wrote tests for all the classes.

Enjoy!

Tuesday, September 29, 2009

NSPopUpButton with Fixed Values and Bindings

NSPopUpButton can be challenging to use. It would be quite desirable to have a very easy way to add an NSPopUpButton for fixed values, like "male" and "female". Unfortunately, that doesn't exist (at least not to my knowledge). That's why I'll show you really quick how to implement this with IB, bindings and some code.


  1. Create a plain old new Cocoa application in XCode and call it NSPopUpDemo.
  2. Open NSPopUpDemoAppDelegate.h, and add these two ivars:


    NSArray *genders;
    NSString *selectedGender;
    IBOutlet NSArrayController *arrayController;
    And add this property:


    @property (nonatomic, copy) NSString *selectedGender;


  3. Switch to NSPopUpDemoAppDelegate.m and add this at the top:


    @synthesize selectedGender;
    and add this to applicationDidFinishLaunching:


    genders = [[NSArray alloc] initWithObjects:[NSDictionary dictionaryWithObjectsAndKeys:@"male", @"name", @"m", @"value", nil],[NSDictionary dictionaryWithObjectsAndKeys:@"female", @"name", @"f", @"value", nil], nil];
    [arrayController setContent:genders];
  4. Open MainMenu.xib
  5. Drag an NSArrayController onto the nib and an NSPopUpButton onto the window.
  6. Control-drag from the App Delegate to the Array Controller and connect the arrayController outlet.
  7. Open the inspector for the NSPopUpButton. Bind it's content to Array Controller's arrangedObjects, it's content objects to Array Controller's arrangedObjects.value and it's content values to Array Controller's arrangedObjects.name:












  8. Build and Run, you should be able to select male or female.
  9. Open MainMenu.xib again and drag an NSTextField to the window. Bind it's value to the App Delegate's selectedGender:





  10. Also open the Inspector for the NSPopUpButton and bind it's selectedObject also to the App Delegate's selectedGender.
  11. Build and Run again. Now you should be able to enter "f" in the text field and the pop up should change to "female" and vice versa.
  12. And you're done!

Enjoy the bliss of KVC!

Monday, September 28, 2009

Fun with AU Lab

The speech synthesizer and the AU Lab application might be hidden gems on your Mac! Why not have some fun with them?



Here we go:
  1. Download a long text file, for example this one.
  2. Open Terminal.app and run
    say -n : -f 28500-8.txt
    The "-n" option will redirect the speech output through AUNetSend.
  3. Open /Developer/Applications/Audio/AU Lab
  4. Click Next, Next, Done.
  5. Select "Edit" > "Add AudioUnit Generator..."
  6. Select "AUNetReceive" from the Generator drop down and click OK.
  7. Click Connect. You should hear the book being read.
  8. Now play with the different effects in the "Effects" drop down in the "Output 1" section. AUBandpass, AUDistortion and AUMatrixReverb are fun.
Have fun!