Tagged: programming

Use more of your brain while programming by using less mouse

The Book

Back in April, I finally got around to read ‘Pragmatic Thinking and Learning: Refactor Your Wetware‘ after heard about it from various co-workers. I won’t go into the details of the book itself as this is not a book review. Needless to say I highly recommend it to everyone, even to non-technical people.

One interesting topic (out of many in the book) Andy Hunt touches upon is what he calls L-mode and R-mode (L as in linear and R as in rich) of brain processing. Other similar and familiar, but not same, terms most people heard about include Left-Right brains, and Conscious-Subconscious.

Continue reading

Advertisements

UITableView scrolling performance gotcha

After a few months of .NET reporting/SSIS development work, I’m back to an iPhone project this week. One enhancement I added yesterday was a better formatted table section title in a UITableView. Before, the section title is either a bunch of unformatted (also incorrectly by locale) dates (e.g. 2009-09-30), or times (e.g. 14:58) straight from the data source. The enhancement/bug fix is to format the date or time to be locale aware so the title would either be “Wed Sep, 30 2009” or “2:58 PM” if you are in the US.

Pretty straightforward I thought, and after a couple of trips to NSDateFormatter and use the output in UITableView’s titleForHeaderInSection:section method, it was all working very well in the simulator. That was until I put the app onto my iPhone for some real in-device testing.

The scrolling performance in the table was horrible! My first thought was that it had to do with the background view I added to the custom table cell view for colouring the table cell background. But after nearly an hour of debugging through the code I still couldn’t find anything wrong.

Turns out that the titleForHeaderInSection:section method is not just called once per controller instantiation. It is called once per table cell display!

Once I moved the code to format the section title into viewDidLoad and cached a copy of the nicely formatted titles in an array, the scrolling is back to normal speed.

Objective-C discourages good OO design/code?

I started learning Objective-C when Apple released the iPhone SDK over a year ago, and started programming in it seriously at the beginning of this year. While there are many things I like about Objective-C as a OO language, there is one thing that continuously bother me.

One of the four main tenant of object-oriented design is Encapsulation. Meaning, the inner working of an object is hidden from public view.

In Objective-C, an instance method can be declared in the implementation file (.m file) in the following ways:

  • Implement the method without declaring it in the header file. This is (almost) equivalent to private method in C#/Java.
  • Declare the method signature in the header file, and implement the method in the .m file. This is like declaring a method public in C#/Java.

So how does this discourages me from writing good OO code with respect to encapsulation?

If I choose the first option, I have two choices. Either I implement the method before its first usage which does no good with readability re Uncle Bob’s Clean Code‘s newspaper metaphor, OR implement it after and put up with the compiler warning about the method call may not exist.

To get the freedom of placing the method anywhere in the .m file, I have to choose the second option and declare the method signature in the header file. The downside of this is that now the method is exposed as part of the class public interface and break encapsulation. (Yes, I know that the method can still be called without the header file declaration. Again, a compiler warning greets you.)

All three options are undesirable to me. It is really a case of pick my poison! Right now, I choose option one and put the method before first usage. Readability suffers because I like reading methods after the usage but at least the header file is clean and represents the intended public interface.

Update: Martin Pilkington makes a suggestion to me via Twitter. I’ll have to try it out and see.

Update #2: Someone else on Twitter also suggests using Extension to solve this issue. The Apple’s documentation here (at the end of the page) shows how an extension of a class can be used to define private method, separated from the main class interface definition.

My initial feel? Pretty inelegant workaround to an inherited problem of Objective-C legacy linkage to C. No thanks, I’ll stick with declaring private methods before usage.

UIScrollView and Multi-Touch zooming

Let's say you have a nice, big, hi-res photo you want to show to your user on the iPhone. No doubt you want to let your user zoom in to see details of the photo and scroll around, just like the built-in Photos.app.
Most of you would probably come up with something akin to the following structure using Interface Builder:

UIView

|
–UIScrollView
|
–UIImageView
If you are coding the UI by hand, the top level UIView would probably be omitted. This may or may not be relevant to the issue you'll be encountering. See my note at the end.
Now that the view structure is setup, you will probably discover that double tapping to zoom in/out is not implemented out of the box by Apple (surprise!).
"Ok", you thought to yourself, "I just need to implement the touch events in the controller and it will all work."
So you go ahead and added touch event code to the controller class, probably something like this:

– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

UITouch *touch = [touches anyObject];

NSInteger tapCount = [touch tapCount];

if (tapCount == 2) {

[scrollView_ setZoomScale:zoomScale animated:YES];

}

}

When you run the code, you'll find that the touchesEnded method never get called!
Turns out this is actually by design from Apple. touchesEnded and other touch event methods are no-op methods by default. My guess is that this is probably a performance related design decision.
Regardless of why, here is what you need to do to 'fix' this. First, create a new class that inherits from UIScrollView. Then in this new subclass, implements the touch event method that you want to use and passes on the event to the next responder in the list. Like this:

– (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{

[super touchesEnded:touches withEvent:event];

[self.nextResponder touchesEnded:touches withEvent:event];

}

This will pass on the touch event to the parent UIView in the hierarchy and thus calls the touch method in the controller.
Problem solved!
(One thing I haven't tried is to removed the top level UIView from the hierarchy, and link the UIScrollView to the view outlet in the controller. My hunch is that this may eliminate the need to subclass UIScrollView.)

Read and post comments

|

Send to a friend

Inoperative Cancel button in UIActionSheet

Let’s say you want to use UIActionSheet to show three buttons to the user with a cancel buttons in a UIView, which itself is managed by a UITabBarController:

Your code would probably look like this:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@”Action Title” delegate:self cancelButtonTitle:@”Cancel” destructiveButtonTitle:nil otherButtonTitles:@”Option 1″@”Option 2″@”Option 3″nil];

actionSheet.actionSheetStyle = UIActionSheetStyleDefault;

[actionSheet showInView:self.view];

[actionSheet release];

And you’ll also probably find that all the 3 option buttons works, but the Cancel one doesn’t!
It is because the UIView which the UIAlertSheet belongs to is behind the UITabBarController, and the TabBar’s hitTest method gets called before the UIAlertSheet’s.
To fix this, it is just a simple matter of using the view from the UITabBarController in the showInView method. Like this:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@”Action Title” delegate:self cancelButtonTitle:@”Cancel” destructiveButtonTitle:nil otherButtonTitles:@”Option 1″@”Option 2″@”Option 3″nil];

actionSheet.actionSheetStyle = UIActionSheetStyleDefault;

UIApplicationDelegate *appDelegate = (UIApplicationDelegate *)[[UIApplication sharedApplication] delegate];

UITabBarController *tabBarController = appDelegate.tabBarController;

[actionSheet showInView:tabBarController.view];

[actionSheet release];