Tuesday, May 25, 2010

How to Create Custom Map Annotation Button

Custom accessory button for MKPinAnnotationView right side, create inside MKMapViewDelegate protocol mapView:viewForAnnotation method:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
[button setImage:[UIImage imageNamed:@"myButton.png"] forState:UIControlStateNormal]; 
view.rightCalloutAccessoryView = button;
Usually sample codes use UIButtonTypeDetailDisclosure as right button, but you can't use that unless you really show additional details. For anything else, you must use a custom button.

Thursday, May 20, 2010

How to sort NSMutableArray alphabetically

NSMutableArray contains dynamic info objects, which are removed and added by user. Removal is easy, but addition should be done in alphabetical order.
NSInteger myCompare(id item1, id item2, void *context)
{
    return [(NSString *)((MyItem *)item1).title
      localizedCaseInsensitiveCompare:
      (NSString *)((MyItem *)item1).title];
}

- (void)addItem:(MyItem *)aItem
{
    [self.myList addObject:aItem];
    NSArray *temp = [NSArray arrayWithArray:self.myList];
    self.myList = [NSMutableArray arrayWithArray:
      [temp sortedArrayUsingFunction:myCompare context:NULL]];
}
Might be more fun to write your own Yet Another Search And Insert In Right Location algorithm, but this was fast to code, safe implementation and guaranteed to work.

What are your priorities: writing YASAIIRL algorithms or other features?

How to reload current UIViewController

Sometimes you might need to reopen currently open UIViewController. There might be a UITableView, but doing just [self.myTable reloadData] might not be enough. Try this:
[self viewWillDisappear:NO];
[self viewWillAppear:NO];
Of course this will work only, if those methods contain operations you need to do while closing and opening your view.

Friday, May 14, 2010

What would Steve do?

Developer is not "The User". He is biased towards his own comfort zone and all designs, decisions and recommendations are from technical implementation point of view:
  • Feature is boring to do: users wouldn't like it.
  • Feature is difficult to do: users wouldn't like it.
  • Feature changes architecture: users wouldn't like it.
  • Feature removes "my code": users wouldn't like it.
The question you really have to ask: What would Steve do?

Tuesday, May 11, 2010

Refactor or Die (or burden of maintenance)

Agile development says when something is done, it stays done. Don't touch it again! On the other hand refactoring is an extremely important part of agility. How do you combine these, possibly conflicting requirements?

Refactoring is a planned action. When you develop something, it is done as well as it can be at that specific moment. It is scheduled, planned, designed, implemented, tested, documented and released as well as needs to be at the specific moment. When it's done, it's really done.

Developing a new feature, additional requirements or fixing bugs might bring in new information, which makes it possible to improve something already done. Make it more generic, robust or faster. Search for reusing something old.

Refactoring is a fact of life. Experiment, seek alternatives, gain more experience, receive more detailed information, learn from own mistakes or from experts who have made more mistakes than you. Use this for your advantage. Refactoring is not experimenting, but cleaning up afterwards to the most suitable solution.

Don't refactor for the sake of refactoring. If something works, don't touch it without really good reasons. Developing something twice is quite ok, requires less time than refactoring. More safe. Refactoring has to be an improvement.

When you find need for a third similar object/module, then take a moment to consider advantages and dangers of refactoring. Will the be more similar cases in future, how critical areas are you dealing with, how stable have they been bug and feature wise. Would refactoring be worth the risk and investment of time and effort?

Don't refactor for fun. Only when needed, understanding risks.

Monday, May 10, 2010

UIWebView with Custom Background Image

Taking UIWebView into use is pretty easy, customizing is a bit more complicated. Here's how you can define your own image as background.

NSString *myHtml = [NSString stringWithFormat:
                    @"<html><head>"
                    "<style type=\"text/css\">"
                    "body{"
                    "background-image:url(%@);"
                    "}"
                    ",</style>"
                    "</head>"
                    "<body>Hello world</body></html>", imgName];

// baseURL for background image, otherwise nil
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[self.myWebView loadHTMLString:myHtml baseURL:baseURL];

Tuesday, May 4, 2010

SVN Rollback is Backwards Merge

While debugging interesting side-effects, I decided to take a "shortcut" and rollback project sources to some earlier version. Just checking when the weird behaviour start to happen.

Found out that SVN rollback is actually a backwards merge - with the usual, expected and dreaded merge conflicts.

Successful rollback does not necessarily produce compiling code. You got to do rollback - to same version - couple times to catch all merge conflicts. Seems to depend on order of files.

Looking for SVN replacement.

Change of Blog Scope

Since iPhone is not everything I do, I have decided to start writing short reminders about other areas, too.

Just FYI, nothing to worry about.

Monday, May 3, 2010

How (not) to Name Your Class API

Basically something you should NOT do: don't start your own API calls with "set" or "get", just in case.

By default - invisible from developer eyes - iPhone SDK @synthesize creates setters and getters for the selected class instance variables. If part of your API allows changing these values somehow, you might accidentally overwrite a default setter or getter.

Perfectly legal thing, something you migth want to do on purpose. Very difficult to debug, if you did that by mistake.

Instead of setVariable you could use changeVariable.

How to Create Delayed Tab Change

Not sure, if it's a good idea, but this is how you create "smooth" tab view changes:
#pragma mark - TabBar controller delegate
- (void)tabBarController:(UITabBarController*)tbc
  didSelectViewController:(UIViewController*)newSelection
{
    [UIView beginAnimations:@"myTabFade" context:nil];
    [UIView setAnimationDuration:0.5];
    for (UIViewController* vc in tbc.viewControllers)
        vc.view.alpha = (vc==newSelection) ? 1 : 0;
    [UIView commitAnimations];  
}
User will notice something happened as expected and your app will look either stylish or slow, depending on your graphics, UI design and overall implementation.

Be careful, style is not an easy thing to achieve!