Rejection
by jegeblad
Today Lifecards 1.4 got rejected because I use a "private API"; CGFontGetGlyphsForUnichars. I have been using that for 3 past versions, so I have no idea why they decided to reject it this time. It is a bit annoying, because there isn't really any way to draw characters on the iPhone other than CGFontGetGlyphsForUnichars and [NSString drawAt:] using UIKit.
The NSString functions are not thread-safe, because they are based on UIKit's current context and Lifecards is multithreaded now so that is no longer an option. I also cannot use NSString functions to generate PDF-files.
The most annoying bit is that it really is an undocumented function in a public framework. Not a private framework. If they had just included the function in their header file, there wouldn't be a problem.
The best part is that it took 10 days to get this feedback.
Now I will have to figure out how to fix this.
Thanks a lot Apple!
Licensing music
by jegeblad
While I've come to realize the last year, that there are plenty of places to pick up of artwork for you iPhone apps in the form of icons or photos, and just recently that you can get vector graphics from e.g.vectorstock.com, I didn't know there was also a place for music.
This week I was contacted by someone from Madacy Entertainment about licensing music for one of my apps (you'll never guess which one). I was dumb enough to tell him that there probably wasn't enough money in it for both of us, so I never got the licensing terms. However, if you are looking for music, I suggest you check out Madacy Entertainment www.madacyvideo.com.
... I cannot find their catalogue but it looks interesting, and in retrospect it was probably the way to go with music for Eyestorm instead of me spending days putting the pieces together.
You learn something new every day. I am still debating with myself if licensing artwork is a good deal or not, though.
Pumpkinizer 1 year after!
by jegeblad
A few weeks ago I got contacted by a representative from O2 -- The company that sells the iPhone in the UK. He wrote to me that they wanted to do some marketing of Halloween Pumpkinizer if that was alright with me. Of course it was.
I had actually abandoned the idea of updating Pumpkinizer, but I thought this might be a good opportunity to do so after all. I could even make a few extra $.
Reopening the old app I realized that it was quite horrible. I wrote it, heavily encouraged by my brother, under lots of time pressure over several evenings during a stressed visit to California last autumn. It took him many days to convince me to work on it, and looking at the end-result now, I probably shouldn't have done it.
I decided to devote 8 hours to perform a complete rewrite. I set a few rules for myself: Make a plan and follow it as much as possible. Reuse code from other apps if possible. Do not add new content except icons for the toolbar.
On toolbars
I am not a big fan of custom code, but I've never really liked Apple's UIToolbar and somehow I always end up developing my own custom. I have had different reasons for doing so. In the beginning I felt UIToolbar was too small (44 pixels). You cannot really add description for the tools -- It is either text or icons, however you can add balloons like Photogene does, or add the description to the icons either in the bitmap files or on the fly.
Lifecards has a context aware toolbar where the relevant portions scroll into place when needed and the icons rotate with the device orientation. I guess both things are more or less possible with UIToolbar. You can create a UIView wider than the screen, put the toolbar in it and scroll the relevant portions in and out. In general, since every part of UIKit is attached to a CALayer, I guess you can also rotate the individual tool buttons -- I've rotated UIWebviews and UITableViews simply by setting the layer transformation appropriately, so it must be possible.
With Pumpkinizer 2.0 I created a UIView wider than the screen and added UIButtons for each tool in it. The extra width allows me to have two sections in the toolbar -- One for the photo and one for the text. The appropriate section scrolls into place by animating the UIView. Using UIButtons gives me the ability to highlight a button when touched.

To add toolbar descriptions, I hacked a bit. All the buttons are transparent and the icons are just drawn in the parent UIView. This felt like the easiest (but not prettiest) way to add tool descriptions. I also added a glossy look to the toolbar by drawing a png with alpha-channel ion top of the toolbar once everything else has been drawn.
I am still debating with myself over whether it made sense not to use Apple's toolbar this time around. The only reason I have is that adding tool-descriptions was just much easier this way.
A year ago, I built the toolbar with a CALayer with a delegate that handled all the drawing and custom handling of touches. A bit messy! But back then I felt that was the way...
Reusing code
I stole bits from Lifecards; Font, color and alignment panels for the text. Sharing facilities; Facebook, Twitter, email. Everything could just be copied over and worked almost instantly. They use protocols and delegates, so wiring up everything was just a matter of implementing the delegate methods -- A year ago I sent messages between objects without structure. The advantage of protocols is that you know instantly what to implement in the delegate. The process confirmed that the components had been correctly designed for reuse.
Handling text
The old Pumpkinizer was "Wizard" based. I.e. position photo -> write text -> preview -> save. With the new one I decided to integrate the text positioning more, so that once the user taps on the text tool, the view scrolls to the position of the text. I still think Apple's text drawing functions are somewhat limited, so I stole our custom textformatter from Lifecards -- Again a bit of code reuse.

Instructions
Instructions were missing in the original Pumpkinizer. This time around, I used a webview -- Something I felt was a massively heavy solution a year ago but I have become accustomed to it now, and it is an easy way to add formatted text.
Session saving
The old Pumpkinizer didn't save the current state if you exited it. The new one does. That means that if you get interrupted by a phone call, or something else, you can just return and continue working from where you left off.
Hints
I decided to add hints. A reviewer of Lifecards noted the hints in the top of the screen. I practically stopped implementing them in Lifecards, but I decided to experiment a bit here. Basically, I have a set of actions that will trigger a hint text in the top of the screen. The hint itself is implemented as a UILabel which is animated when triggered -- Couldn't be easier.
Conclusion
Despite the code reuse I still ended up spending about 10 hours in total. 2 more than I had planned for. The update was written in airports and other odd places and at weird times of day, so it is a bit hard to add it all up. I ended up changing the contents slightly, because my brother sent me some last minute images to use, so that took me a couple of extra hours. If it hadn't been for that, I would have finished in less than the planned time.
I think, it is always fun to revisit old projects and think about how you would do things differently today. It puts the experience you've gained into perspective. Apparently, it really does take some time to get used to a new platform! I don't know what happened to the O2 marketing, but I am crossing my fingers that something will happen.
Objective C protocols in C++
by jegeblad
One of the things I have grown to like in Obj. C is protocols. E.g.
@protocol FontSubViewDelegate
-(void) setFont:(NSString*) fontName;
-(void) openFontPanel;
-(void) changeTextSize:(double) textSize;
@end
An Obj. C class conforms to a protocol by something like this:
@interface RootViewController : UIViewController < mainviewdelegate , UINavigationControllerDelegate, UIImagePickerControllerDelegate, UIActionSheetDelegate, FontSubViewDelegate, ColorToolbarDelegate, AlignmentViewDelegate, ToolbarDelegate>
...
mainviewdelegate>
It can get a bit messy. RootViewController conforms to 8 protocols here.
I like protocols, because it is a nice way to tie objects together in a "call-back" fashion. I've been using a similar strategy in C++ for many years now, but there it always ended up looking dirty. I would often create a functor:
struct FontSubViewCallBack {
virtual void operator()(NSString* fontName) = 0;
}
And then when I needed the functor-like class I would create a small local class which I would pass onto the function that needed the callback:
void RootViewController::doSomeFontStuff ()
{
struct LocalCallBack : public FontSubViewCallBack {
RootViewController * root;
void operator()(NSString* fontName) {
root->setFontName( fontName );
}
..
};
LocalCallBack cb(&this);
iNeedCallBack(cb);
};
Here the LocalCallBack forms a connection between my RootViewController class and the part that needs the callback. In reality we don't need the LocalCallBack. We can just let RootViewController inherit from FontSubView:
RootViewController : public WhateverClass, FontSubView {
...
void setFont(NSString* fontName);
void openFontPanel();
void changeTextSize(double textSize);
}
and implement the pure virtual functions directly in RootViewController.
There is nothing ground-breaking in this. It is just a different way to think; We take advantage of the fact that C++ classes can inherit from multiple classes and let the protocol be a pure virtual class. It doesn't even have to be pure virtual. If you have optional delegate functions you just give them an empty body.
I use many design patterns in general (e.g. singleton, factory, container, ...), but somehow I missed the delegate pattern in C++ or at least didn't know what to call it. I guess one of the advantage of learning a new friend is that you look at the ones you already know in a new light.
Strip Designer/Lifestrips rating
by jegeblad
The average rating of the current version of Strip Designer (Formerly Lifestrips)'s is 5 with a total of 7 people rating it. All of them with 5 stars.
The average rating of Strip Designer for all versions is currently 4.7 with 18 ratings (14x5 star, 3x4 star, and 1x3 star).
I have no idea why it is getting that high ratings, but I am very pleased.
It doesn't seem to affect the sales significantly. Strip Designer is doing pretty much the same as always. Positively, it never got the same amount of exposure as Lifecards (being on the New and Noteworthy), and I did not do the same amount of advertising and review requests of it. It still sells almost just as well as Lifecards. LIfecards has a much lower average rating though even though the two apps share code; In reality only the content differ, although I had to add extra code to the code-base for the speech balloon functionality.
I always figured that the main competitor for Strip Designer would be Comic Touch from Plasq (The OS X Comic LIfe developers), but I cannot find that app on the top-100 photography list.
Pamela Flora on the iPhoneography blog wrote a long and very positive review on LIfestrips some time ago:
http://www.iphoneography.com/journal/2009/8/3/iphone-app-review-lifestrips-by-pamela-flora.html. I didn't request it, so I was happily surprised.
From what I can tell the main feature requests for Strip Designer are:
- Ability to add more than 4 photos
- Better photo filters
- Nicer UI
- Ability to add/modify the templates
11/16/09 06:52:59 pm, 