November 5th, 2017 Comments off

Last November I purchased an Apple Watch Series 2. I previously posted “why I’m not getting an Apple Watch”, but the series 2 (and now 3) are a lot better at the fitness tracking, and the waterproof-ness means I can swim with it. I’ve been on a continuous streak since Dec. 31st.

Categories: Micro Blog Tags:

November 3rd, 2017 Comments off

Now that I have a micro blog, perhaps I’ll post more often. This is a test of the microblogging platform micro.blog.

Categories: Micro Blog Tags:

Why I cancelled my WATCH order

May 12th, 2015 Comments off

I did not order a watch at 3 in the morning. I wanted to see one in the store first. And I was instantly smitten: much nicer than I expected, and nicer to boot. So I ordered one, and thought about replacing my Pebble.

Delivery in June.

The notifications with the Taptic engine are much nicer that the Pebble. It actually respects the “do not disturb” settings that I set on my Phone. I can interact (albeit in a limited fashion) the watch, it’s very much interactive. Having Passbook on the watch is the best use of it yet (hello, TrainScan!). And the voice phone calls are just plain cool.

I did not try to get a Blue Developer watch. It’s just not my colour. But we got one for the office, to try out and perhaps prototype some interactions with our mobile apps. If I learned anything, it’s that you have to develop on real hardware, the simulator won’t cut it.

I wore that watch for a week, to see how I would like it. And it’s nicer than I expected: the fluoroelastomer band doesn’t scratch the palm rest of my MacBook Pro (1), the battery lasts all day without any kind of special management, and the taptic notifications to stand up are a lot less disturbing than I thought they would be.

That was another concern: when I’m “in the zone”, writing code, I want to be left alone and not knocked out of the zone. In practice, the discreet tap on the wrist was subtle enough to ignore and stay in the zone if I wanted to.

However, it wasn’t all roses and kittens. iMessage notifications were not always coming through, in particular from my wife, which was infuriating. I have to manually start my exercise programs, and even with two hours of bike riding a day it would only say “28 minutes out of 60, you missed your goal!”. Knowing my heart rate when I’m exercising is nice, but I exercised for years without it and don’t view it as indispensable.

This basic 42mm watch costs 519$ in Canada. When you add taxes, that’s 600$.

It’s a nice watch, but not worth that to me. My wrist is back to being free, for now.

Addendum: the more I think about it, the more it’s not a question of price. Even if it was 200$ I would not purchase it because I don’t see myself wearing it everyday. This watch just isn’t compelling enough to have earned a place on my wrist. A lot of what it promises (Pay, great 3rd party integrations like Dark Sky notifications) just aren’t available where I live.

(1) That was actually my main concern: other watches I tried had a metal clasp or nub that scratched the surface of the palm rest, making me remove them before sitting for work, which makes them a lot less interesting.

Categories: Development Tags:

Upgrading to Xcode 4.3

February 18th, 2012 5 comments

Xcode 4.3 was released on February 16th. It is now truly a Mac App Store application.

With Xcode 4.2, you downloaded an “Install Xcode” application from the Mac App Store, which in turn “installed” Xcode in the usual location, “/Developer”, which is a folder named “Developer” at the root of your hard drive.

Furthermore, if you wanted Xcode to automatically update (from the Mac App Store), you had to keep the “Install Xcode” application (1.8 GB) in your Applications folder, as that was what the the Mac App Store actually updated; you then had to run “Install Xcode” manually to update.

This has changed for the better.

Xcode.app now installs directly in the Applications folder. In fact, it will helpfully propose to move the older, obsolete installation to the Trash for you:

Xcode move to Trash

Xcode is also smaller, since a lot of (probably) lesser-used software has moved to optional downloads. Peter Hosey has a comprehensive list.

In my case, I had one more thing to do: install the command-line tools. They can be found in Xcode > Preferences > Downloads > Components. To get them working properly, here is what I had to do:

  1. sudo xcode-select -switch /Applications/Xcode.app
    This updates the command-line Xcode tools to point to the new location, and is only important if you are migrating from 4.2. A new install should work fine.
  2. Install autoconf. Some of my projects (and many unix-based ones) use autoconf to generate ‘configure’ files, and this tool is missing from the new install. It was present in earlier installs, so I have filed a radar.
    In the meantime, I was able to download the latest version (2.84 at the time of this writing) and install it with the usual ./configure; make; sudo make install dance
  3. Update any scripts that have a hard-coded ‘/Developer’ path.
    Since everything is in the ‘/Applications/Xcode.app’ bundle, the new path is ‘/Applications/Xcode.app/Contents/Developer’. If you don’t want to hard-code it (for instance, if you use Xcode 4.3 but your colleagues are still on Xcode 4.2), you can use the output of xcode-select --print-path to find the path to ‘Developer’ on any system. This works in Xcode 3 as well.

Xcode 4.3 requires MacOSX Lion 10.7.3.

Categories: iOS, MacOSX, Xcode Tags:

Xcode 4.2 and older iOS devices

December 18th, 2011 3 comments

If you want to build software using the new default templates on Xcode 4.2, you have some work to do for older devices (anything that can’t run iOS 5).

For some unknown reason, the variable $(ARCHS_STANDARD_32_BIT), which used to expand to armv6 armv7 now expands only to armv7.

Furthermore, there is a new entry in the default Info.plist which will prevent Xcode from even uploading the binary to your device.

Finally, there are issues with the compiler’s ‘thumbnail generation’ code ‘Thumb code’ generation in Xcode 4.2. You can disable that compiler option for armv6 builds in Xcode.


I have a 2nd generation iPod Touch that I use daily. It runs iOS 4.2.1, the latest it will ever run. This is essentially the same hardware as an iPhone 3G in terms of capabilities.

I also have a 1st gen Touch, but it’s getting used less and less as I target iOS 4 as the earliest capable version. I’m not going to iOS 5 yet because my main device is still iOS 4.

When I create a new project, I use iOS 5 as the SDK and iOS 4.2 as the deployment target. In theory, this allows me to target all devices that can run iOS 4, which includes my beloved Touch.

To make this work properly, you will want to do three things:

  1. In your Project’s Build Settings, locate the “Architectures” line. It should say $(ARCHS_STANDARD_32_BIT). Edit it to contain two entries: armv6 and armv7.
  2. In your Project’s Info.plist, locate the entry for “Required device capabilities” and delete it.
    If you expand it, you will see that it contains only armv7.
  3. Bonus! Thanks to @jamesthomson, there are issues with clang-llvm 3.0 on armv6 devices.
    In your Project’s Build Settings, locate “Other C flags” and add “-mno-thumb” to the armv6 architecture.

When radar comes back online, I’ll file the bug (which will undoubtedly be marked “Duplicate”)

Categories: Development, iOS, Xcode Tags:

iPad or Netbook?

September 6th, 2010 4 comments

[This is not as developer-oriented as my other posts, but many people asked me to share this story so here goes… —Philippe]

The Setup

We have three children in the house: twin teens and one pre-teen. We also have three usable (read: Intel-based) computers. I have an original MacBook Pro, my wife has a Mini and the kids share another (recent) Mac Mini.

Three kids on a computer was becoming a constant source of tension among them, which I wanted to alleviate. Their Mini is the most powerful of all our machines with a fairly decent video card, a large display and a Wacom Intuos 3 they use all the time in Painter and Acorn.

The Problem

The twins want their own “computer”. They want to be able to surf the web, type reports for class, chat with their friends and watch videos. And go on Facebook.

If money was no object, I would buy them each a MacBook. They know how to use it, won’t get viruses, won’t always come to me for questions, and all our current apps (Pages, iTunes, Firefox, Adium) will work just fine. Plus they would be able to sync their iPods before going to school without it being a dramatic production 5 minutes before they leave, because they must have the latest video on there for the bus ride.

But of course, money is an issue. Plus, at this rate, they won’t make it through University with the same MacBook because by that time, the MacBook will be 7 years old. So I was looking at buying 4 MacBooks over the next 3-4 years, which is less than ideal.

Enter the iPad

Looking at their usage patterns, I figured that an iPad for each teen would cover 70-80% of their computing needs (maybe with a stand and an external keyboard, too). It seemed like an ideal solution.

Buy each teen an iPad and they can:

  • Surf the web, chat, write papers, watch (some) videos
  • Have a small, long-lasting-battery device they can take anywhere
  • Sync occasionally to the main computer, as needed

In return I get:

  • to buy two iPads for the price of one MacBook (a 50% saving!)
  • a lot less nagging until College
  • almost no maintenance
  • hopefully a lot less conflicts around the family computer
  • peace of mind (no webcam is a plus!)

So I bought one iPad and lent it to each child for her exclusive use, for a week. They would tell me if my assumptions were correct.

iPad Results

Being the new toy, the iPad got quite a bit of play at our house. Since it is small and portable, it can be used anywhere. I like it for books and rss feeds, my wife watches CrunchyRoll, YouTube and Facebook are perennial hits.

I also took the iPad for a week at WWDC as my note-taking, twittering machine and that was a smash hit, thanks to SimpleNote and Notational Velocity.

Overall, we all like the iPad. We really wished it had separate user accounts, or at least a “guest” mode so the one person who’s responsible for it doesn’t have to expose his or her mail, Twitter, Safari bookmarks, contacts, etc… to everyone else who wants to use the one iPad.

No Flash

The biggest problem with the iPad was the lack of Flash. I personally don’t care for Flash and its battery-sucking, lap-melting performance, but my daughters watch a lot of Japanese cartoons and play Flash games on Facebook, and those will take some time to transfer to the new world of iOS apps, HTML5 and H264.

A secondary concern was the iPad’s unitasking. I view it as a feature but they like to have on their screen an IM client, a YouTube video and some web browser / text editor / paint program. You can’t easily do that simultaneously on the iPad

Netbooks to the rescue!

I have an original OLPC and when I (quickly) got bored with it, my plan was to recycle it to my children as a small laptop. That experiment is mostly a failure du to the laptop’s anemic performance and inability to hold a decent charge. Seriously, its browser is slower than the Wii and forget about Flash!. However, it has been a good way to teach Python to my youngest daughter, and the laptop’s whimsical, kid friendly design is a hit wherever she takes it.

So you can understand my reluctance about netbooks. It was out of the question that I would buy my daughters a 600$ laptop: those are heavy, ugly monstrosities; try to get anything decent and you soon get within striking distance of a MacBook so why not get a MacBook?

We started talking about netbooks. My kids understood they were slower than the Mac Mini, and had a small screen and keyboard but those were not deal-breakers by any means. In fact, they prefer the small form factor because (duh!) it takes less space and is more “personal”, a feature they really appreciated in the iPad. The intimacy of this device is a feature all by itself.

Just as for the iPad, I decided to buy a small netbook as an experiment. My conditions were simple:

  • it cannot cost more than 400$
  • colors don’t matter. You’ll get to put stickers on it if we keep it (because in three years, when it’s really time for a MacBook, that thing will be old and no one will care about it)
  • it cannot run Windows. I do not want that administration task.

I found an HP Mini 110, which had the additional advantage of being a good candidate for a hackintosh, should I end up with this system. And the price was right at 280$.

Hardware-wise, the bilingual keyboard is surprisingly good. The trackpad is a little too sensitive and its buttons a bit mushy. I will probably buy an external, corded mouse in the next few days.

There are three USB ports, one SD card slot (perfect for their digital cameras) and one VGA-out port.

It comes with Windows 7 Starter, which is an restricted version of Windows 7. When I told my daughter that she could not even change the desktop wallpaper, she was appalled and took it as the best reason not to run Windows on this system…

As I was downloading Ubuntu Netbook (see below), she took the time to go through the first-run experience of Windows 7 because she knew it did not matter, we were going to erase it anyways. As a typical user, she was almost forced to install a ton of crapware programs and trial editions (Norton Antivirus? Really?). There was even a HP-branded user interface that installed itself in the bootloader! I kid you not! Getting rid of this abomination to be able to access the BIOS and boot from a USB key was not trivial.

Enter Ubuntu

Ubuntu Linux is the version I use at work. They have a netbook edition which is optimized for small screens and slower processors, so that is what I downloaded.

There’s even a manga comic, so that’s a big hit in this house!

You install the .iso on a USB stick (which is easiest to do under Windows than Mac, mind you) and from there, select the USB stick in your boot menu. I had to press Esc and then F9 on my HP Mini to get the menu, instructions which were on the computer’s screen. From there, follow the prompts and either run from the USB stick or install to the hard drive.

Ubuntu is available in many languages (from the same .iso), we chose to install French. It even recognized the French keyboard on the netbook, which was a concern of mine.

The overall look of Ubuntu is very good. Easily-readable text, good French translations and some obvious attention to details like rounded corners and fade-in, fade-out animations à la Growl.

Though it did download and install updates for over an hour, once we installed the Flash Player plugin all the sites that were previously inaccessible on the iPad became usable.

I was expecting everything to work, and everything did: the keyboard, trackpad, battery indicator, volume control, backlight control. It even goes to sleep when you close the lid!

I have not gotten the built-in speakers to work, but earphones work fine.

I only had one scare. My initial install failed to set up the wireless card. We knew it worked because Windows had detected it when we first booted, and I spent a few hours googling a solution and getting n00b-level advice on IRC.

The solution was simple: when installing, plug an ethernet cable into the netbook. As part of the install process, Ubuntu downloads additional files as needed. With this simple action, everything worked great.

Performance is very good. It appears subjectively faster than Windows 7 Starter on the same machine. I was worried about Flash performance but it is acceptable, definitely working for 360-480p video (the screen is 1024 x 600). Facebook games are fine.

Applications

We’re still evaluating this, but even though the big application is Firefox, we have found suitable replacements for Mail, IM and even homework. There are apps to manage your iPod which work fine with their Nanos.

They miss the “big” apps like Painter and Acorn (and to a lesser degree, Pages and iMovie) but that’s what the main family computer is for.

I have not worked out a backup strategy yet. I have a Time Capsule for the Macs in the house, which works with Time Machine. For their netbooks, I think a simple, cron-based hourly rsync of their home folder to a folder on on the family computer could do the trick.

Conclusions

They say that Linux is free only if your time is worth nothing, and that has some merit. But once it is running, it is very easy to use.

Ubuntu in particular is very discoverable. The netbook-specific version shows an attention to details like the small screen and the important applications, and that made all the difference. Even the application installer is easy to use. And it’s all free! Which amazes my daughters because I make my living writing software for money.

The Ubuntu Software Center in particular makes it trivial to discover, install and uninstall software. When she was not totally happy with the built-in Empathy‘s support for MSN Chat, my daughter found and installed amsn by herself. That is ease of use!

Aside from my one scare with the wireless card not working, the experience was very positive. In fact, I just bought a second, identical netbook (10$ less this time) and the install was seamless. We did not even boot into Windows.

The best part is I bought two netbooks for the price of one iPad. Another 50% savings! Even more if you account for the inevitable iPad apps like Pages and Keynote, and the external keyboard and dock. All of these easily add 100$ to the price of any iPad.

I always hope that my daughters will become proficient with computers, not because they know to “click there” to go to the Internet but because they have a higher-level understanding of how Operating Systems operate, regardless of MacOSX, Windows, Linux or more.

Because we have no idea what the computers from the future look like. And I want them to be ready.

Categories: MacOSX Tags:

Introducing PhFacebook, a Cocoa framework to Facebook’s API

August 29th, 2010 12 comments

For the upcoming version of iChibi, I needed a MacOSX interface to Facebook’s API.

There are several iOS libraries (FB Connect, ShareKit…) but I found only one that was MacOSX-specific: MBAbeFook, which implements the (deprecated) Facebook REST API.

This was a problem because:

  • I need extended permissions, and it’s a song-and-dance to get those using the REST API
  • The authentication UI from REST may present checkboxes to the user for extended permissions.
    Users don’t check boxes. They click “I agree”…
  • The REST API is deprecated.

So I wrote my own. PhFacebook is an embeddable MacOSX framework to easily access Facebook’s API.

  • Uses Facebook’s new ‘graph’ API internally, handles OAuth in a WebView for you and returns JSON strings.
  • Comes with a sample application to show you how to use it.
  • Supports extended permissions.
  • Localized in English and French.

Find it on github. MIT-licensed.

Categories: Development, MacOSX Tags:

Eight guys, four weeks, one dream

February 16th, 2010 2 comments

A software coop

At our local Cocoaheads chapter, we started dreaming in December… What if we pooled our resources and multiple talents to create a software “co-op”? Shared resources, low buy-in, extra exposure and marketing…

Over the holidays, Cocoaminded was born. We have a domain, so we must exist, right?

Early January, Mark had an idea. What if we all worked together on a small iPhone app to promote our other apps, and learn something in the process?

Introducing Tweety10

Mark’s idea was to expand on his HockeyTweet to create an Olympic-themed Twitter client. It would have a live newsfeed (from Twitter, of course!), medal count and schedule. It would use web services to serve the data

And just like that, we decided to start what became Tweety10.

The first 80%

We all got together on Sunday, January 10th at The Code Factory and brought our laptops. Mark even brought his 24″ display. At 9:30, we had a kickoff meeting where we decided that at 5:00, no matter what, we would all go home. And we would decide if the app was in a good enough shape to continue working on it, with the goal of submitting within two weeks to the App Store.

I was nominated as Project Manager, which means that I was responsible for the big picture, so to speak. Given my past experience, here is what I had planned:

  • Each feature of the application, for example the News view, the Medals view, etc… was written on a 3×5 index card. Features would be then assigned, and divided into subfeatures as necessary. For the News view, you had the actual view, the Controller (UITableViewController, in this case) and the model, along with the web service. We wrote those subfeatures on cards
    The idea was, whatever was on a card should represent about an hour’s worth of work. If it took more than an hour, you made a new card. When you were done, you asked for a new card.
  • Version control with Mercurial.
    I suggested Mercurial (hg) early on because it is fast, simple and distributed. It also has a fairly quick learning curve, meaning I would not have to spend a lot of time explaining it to everyone.
    Mercurial had a couple of small glitches that were due to my unpreparedness, but overall it was very satisfactory.
  • We would use BitBucket.org to store our common code repository.
    BitBucket has a free, private repository (and unlimited public repositories). It was extremely simple to create the private project and add the other seven developers as writers to the project. You can also assign read-only access if you want: it is simple and quick, much like Mercurial itself.
  • Starting from the skeleton application (a variant of the Tab Bar Application Xcode template), we would add source code and resources as needed, and push to the common repository as often as possible. Continuous integration was the word.

I spent my day making cards, troubleshooting issues, helping out with code problems and generally having a blast integrating all of the code into a build system on my own computer. It was exhilarating to keep feeding six smart people with enough information to keep them busy, while at the same time making sure that they are not stuck on a problem or bored and going off-spec. The index cards were a hit, and BitBucket performed admirably.

At the end of the day, we decided we had an app, and it was worth continuing towards our goal of putting an app in the App Store.

The other 80%

Since we would not work in the same area anymore, I immediately transcribed all the cards on a Wiki page in BitBucket. This allowed us to put our name next to a virtual card and claim it. We could add cards, and mark them as complete when the work was done.

We started using Skype’s Chat feature to create a private chat amongst all of us. The great thing was, you did not have to stay logged-in like you would for IRC. If you disconnected from the chat, as soon as you re-connected Skype updated your chat window and you could see all of the conversation that you had missed. This is a great feature.

Although it was not formal, it was very helpful to meet two to three hours every week for NSCoder Nights. It was a great way to gauge progress and help each other out with all the little things that stop us from time to time.

We finalized our artwork and installed localizations, keeping abreast of changes in localizable files as much as possible (this late, the changes were far between).

Real artists ship… then get rejected

We submitted to the app store a week before the Olympics were due to start, and after three days got rejected because we used the word “Olympics” in our keywords. Oops! I wonder how people who live on the Olympic peninsula feel… We removed the keyword, re-submitted and after another four days of waiting…

Tweety10 is available on the App Store. It’s free, give it a try!

Post-mortem

Having a real, in-person kick-off day for the project helped tremendously. This would not have happened otherwise. Most of our subsequent interactions were online via email, Skype or BitBucket and if we had not smoothed out the communication channels beforehand, the friction would have been too great.

Mercurial is a great version-control system. Once we got past the initial hurdles of merging (hg does not come with a built-in merge tool, you have to set it up) and pushing (Mercurial Keyring Extension is a must).

BitBucket, with its one free private project, was an awesome instrument in communication via code and issues. You get an email when an issue is created or updated, and you can resolve issues by pushing a change containing text like “Fixes #23”.

Finally, I was delighted to see that as a group, we worked really well together. I hope we all gained some valuable knowledge and experience with this project; I certainly did.

This was really a case of the whole being larger than the sum of its parts! I hope you will enjoy the app and the 2010 Winter Olympics in Vancouver, Canada.

Categories: Development Tags:

Multiple developers, one iPhone app

February 10th, 2010 2 comments

I am working on an iPhone project with seven other iPhone developers, and we all have individual development certficates (the project did not require a group certificate, and we all had ours already).

Since one has to sign code to run outside the simulator, we all edited the Info.plist’s Bundle Identifier to match our certificates (mine was com.casgrain, as you can guess).

This leads to a permanently-modified file in your local copy of the source repository, one that you have to ensure you do not commit. It’s not very neat, and it is error-prone. And if you want to genuinely modifiy the plist, you must discard your changes, do the modification, commit and re-do your changes. Not pleasant.

I wanted to use plist pre-processing to solve this issue, since it will replace environemnt variables in the plist before using it, for instance to sign code.

The goal is to replace this:

	CFBundleIdentifier
	com.myCompany.${PRODUCT_NAME:rfc1034identifier}

with this:

	CFBundleIdentifier
	${DEVELOPER_DOMAIN}.${PRODUCT_NAME:rfc1034identifier}

Pre-processing supports environment variables, but not any environment variable. If you define a variable in ~/.profile (if you use bash or a bash-compatible shell), this does not work.
You have to define the variable in ~/.MacOSX/environment.plist, a special plist that is read at login time.

To define this variable, follow these three simple tasks in Terminal. You only need to do them once.

  1. Create the `/MacOSX folder if it does not exist. If it is already present, this is harmless
    	mkdir ~/.MacOSX
    
  2. Add the environment variable
    	defaults write ~/.MacOSX/environment DEVELOPER_DOMAIN -string "com.yourdomain"
    
  3. Ensure that the plist is human-readable and not compressed XML, in case you want to edit it later with a text editor
    	plutil -convert xml1 ~/.MacOSX/environment.plist
    

You will need to log out and back in for those changes to take effect.

Now, do the repkacement above in your Info.plist, substituting “${DEVELOPER_DOMAIN}” for “com.myCompany” or whatever the reverse-domain was for your project.

With this modification, you should now be able to build for the device without changing the plist every time.

Categories: Development Tags:

Tap-enabled UITableView section headers (and footers)

January 31st, 2010 Comments off

Displaying ads

For a project I’m working on, I had the need to display inline ads within a UITableView of news.

My initial thought was simply to create a different kind of cell every 10 cells, and that cell knows how to display itself:

- (UITableViewCell*) tableView: (UITableView*) atableView 
  cellForRowAtIndexPath: (NSIndexPath*) indexPath 
{
  static NSString *NewsCellIdentifier = @"NewsCell";
  static NSString *AdCellIdentifier = @"AdCell";
  
  UITableViewCell *cell = nil;
    
  // Set up the cell...
  if (0 == indexPath.row % 10)
  {
    CMAdImageTableViewCell *adCell = 
      (CMAdImageTableViewCell*)[tableView dequeueReusableCellWithIdentifier: AdCellIdentifier];
    if (adCell == nil) 
    {
      adCell = [[[CMAdImageTableViewCell alloc] 
        initWithStyle: UITableViewCellStyleSubtitle 
        reuseIdentifier:AdCellIdentifier] autorelease];
    }
    /* more cell customization... */
    cell = adCell;
  }
  else
  {
    CMNewsTableViewCell *newsCell = 
      (CMNewsTableViewCell*)[tableView dequeueReusableCellWithIdentifier: NewsCellIdentifier];
    if (newsCell == nil) 
    {
      newsCell = [[[CMNewsTableViewCell alloc] 
        initWithStyle: UITableViewCellStyleSubtitle 
        reuseIdentifier: NewsCellIdentifier] autorelease];
    }
    /* more cell customization... */
    cell = newsCell;
  }
  
  [cell setNeedsDisplay];
  
  return cell;
}

This works. There is a CMAdImageTableViewCell created every 10 cells. And since the UITableView delegate can get the -(void) tableView: didDeselectRowAtIndexPath: message, you have a chance to intercept a tap on the ad cell and send the user to the ad’s website.

However, you have to do some bookkeeping. For instance, the total number of cells will be the sum of all the news cells and your ad cells, or [news count] + [news count] % 10. You also have to find the proper offset in your model when you get an indexPath, etc. Not difficult, but annoying.

Another issue is that TableView cells all look and behave the same. An Ad cell doesn’t say “I’m an ad”, it just says “I’m another cell”, by the way it scrolls. Your ad design will probably be different than your news design to tell them apart, but it would be nice to have another, subtle way to differentiate the ads.

Section Headers

UITableViews can be separated in sections, with an optional header and footer. These sections stay on screen, at the top and/or bottom, and are overlaid on top of the UITableView’s actual data. They can be semi-transparent, allowing to to see the table’s data underneath. You can see sections in action in many apps, including the Contacts application on your iPhone: notice the section headers “A”, “B”, “C”.. as you scroll down?

To create sections, just return the number of sections in your delegate’s -(NSInteger) tableView: numberOfRowsInSection:. Once you specify more than one section, you can set a section header’s appearance by responding with a UIView to -(UIView*) tableView: viewForHeaderInSection: in your UITableView’s delegate.

This is a full-fledged UIView, so you can do things like drawing with Core Graphics or adding a complete view hierarchy. For the simple case of an ad, if you already have a PNG with the ad to display, you can simply do:

- (UIView*) tableView: (UITableView*) tableView 
  viewForHeaderInSection: (NSInteger) section 
{
  /* assumes your tableview is 320 wide, makes a section header 80 pixels high */
  customView = [[[UIView alloc] initWithFrame: CGRectMake(0.0, 0.0, 320.0, 81.0)] autorelease];
 
  UIImageView *imgView = [[[UIImageView alloc] initWithImage: myPNGImage] autorelease];
  /* makes the views slightly transparent so you can see the cells behind them as you scroll */
  imgView.alpha = 0.7;
  customView.backgroundColor = [UIColor colorWithRed: 1.0 green: 1.0 blue: 1.0 alpha: 0.7];
  
  [customView addSubview: imgView];
  
  return customView;
}

I made the header with a semi-transparent white background so you can see a little bit of the underlying news as you scroll by, giving the overlay a nicer appearance. You can adjust it to suit your needs.

This is great. The ads stay on-screen, on top of the content, and they don’t interfere with it.

There’s only one problem…
You can’t tap on section headers. There is no delegate method to tell you what section header was tapped.

UIButton to the rescue

What if, instead of embedding a UIImageView, you embedded a UIButton in your customView? UIButton is a subclass of UIView, but it handles events and can dispatch on target-action. Plus, you can add an image to a button! Let’s try this:

- (void) headerTapped: (UIButton*) sender
{
  /* do what you want in response to section header tap */
}
  
- (UIView*) tableView: (UITableView*) tableView 
  viewForHeaderInSection: (NSInteger) section 
{
  customView = [[[UIView alloc] initWithFrame: CGRectMake(0.0, 0.0, 320.0, 81.0)] autorelease];
  customView.backgroundColor = [UIColor colorWithRed: 1.0 green: 1.0 blue: 1.0 alpha: 0.7];
 
  /* make button one pixel less high than customView above, to account for separator line */
  UIButton *button = [[[UIButton alloc] initWithFrame: CGRectMake(0.0, 0.0, 320.0, 80.0)] autorelease];
  button.alpha = 0.7;
  [button setImage: myPNGImage forState: UIControlStateNormal];
  
  /* Prepare target-action */
  [button addTarget: self action: @selector(headerTapped:) 
    forControlEvents: UIControlEventTouchUpInside];
  
  [customView addSubview: button];
  
  return customView;
}

The difference with UIImageView is the target-action method. We added a new target (self), with the method headerTapped: (defined above) for the event UIControlEventTouchUpInside. You can view all events handled by UIButton, but I recommend TouchUpInside because it is sent when the user lifts her finger while inside your button. If she slides out of the button the action is not sent and she can effectively “cancel” the button’s activation.

Voi|à! You now have a section header that responds to taps.

Notes

This code uses hard-coded numbers for example purposes. Use macros or consts in your own code, not magic numbers!

Also, creating custom UIViews is expensive. You don’t want to do it every time viewForHeaderInSection: is called as this will impact performance. Once you create a UIView, save it in a mutable array or dictionary and cache it for the next time you are called.

Categories: Development, MacOSX Tags: