Tuesday, December 28, 2010

What to Do with UIRequiresPersistentWiFi

Last night I was watching a streamed TV show (*) from great archives of national broadcasting company. At about 32 minutes it stopped! Couldn't restart the show, couldn't pause and restart, couldn't stop and restart, couldn't restart the application to restart the show.

Application was ok, responsing well. Just wouldn't play anything for me.

Wednesday, December 22, 2010

Local Static Variable Lifetime Expectation in Objective-C

Something I learned today, worth remembering for performance reasons. Static variable defined inside a method is actually a global variable, which is visible only inside that method!

What this means is that you can increase your method performance by declaring variable only once and reuse it when needed next time.

Tuesday, December 21, 2010

Localization Tips

Experimenting with Localization, wondering why doesn't it work like in books? Me, too!

You should have one folder per language, e.g. "en.lproj" for English and "fi.lproj" for Finnish. This folder should contain one language specific file, which contains localized text strings:
"myString1" = "Hello, World!";
...which you use in code:
NSLocalizedString(@"myString1", nil)

Data Formatters temporarily unavailable, will re-try after a continue

Mysterious crash happens, when I'm trying to display about 65MB image in iPhone4. Wonder what could be the problem... (hint: trying to display 65 MEGABYTE image file):
Program received signal:  “0”.
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Unknown error loading shared library "/Developer/usr/lib/libXcodeDebuggerSupport.dylib")

Monday, December 20, 2010

How to Detect iOS Version in Code

Bumbed into a case where code crashed iOS 4.0.2, but worked with iOS 4.2. Since crash happened because of missing NSString *const, couldn't use respondsToSelector to choose, which code to run. Decided to simply check what is current iOS version:
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
if (version > 4.0)
{
    // iOS 4.2 specific code
}
else
{
    // iOS 4.0.2 specific code
}
Please note how three part version number gets converted to float:
NSString *s = [[UIDevice currentDevice] systemVersion];
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
NSLog(@"Version: %@ and %f", s, version);
NSLog(@"Version: %@ and %f", @"3.1.3", [@"3.1.3" floatValue]);

Version: 4.0.2 and 4.000000
Version: 3.1.3 and 3.100000
I'm not really happy with the solution, but it works. Can live with that.

Update:

Always run code in real device! My development device iPhone4 with iOS 4.1 returns "4.1" as string, but that gives 4.0999999 as float value !!!

Friday, December 10, 2010

Where is iPhone Simulator NSTemporaryDirectory

Sometimes I need to save temporary files. The problem is cleaning up afterwards: application might crash during debug sessions leaving files, which are accessed on following launch thus messing up normal expected application behaviour.

iOS provides application specific temporary folder. The benefit in using this is that even if the worst case happens and your app fails to clean up temporary files for some reason (try to view 65 MB image, for example), application temporary folder is automatically cleaned out every 3 days. Your application is thus not able to leave behind "zombie" files, which would fill up device harddisk until all available space is used!

Wednesday, December 8, 2010

GDB: Program received signal: "EXC_BAD_ACCESS"

When your program crashes (not if, but when) with an error message...
GDB: Program received signal: "EXC_BAD_ACCESS"
...you know that there is most likely a memory leak somewhere. Your code just tried to access something, which was supposed to be there, which used to be there, which is going to be there, which should have been there - but right now is missing.

Thursday, December 2, 2010

How to Continue Music Play after Screen Auto-Lock

Your application is playing music - until screen auto-lock stops it.

This happens, because by default your application is using AVAudioSessionCategorySoloAmbient audio session category.

You could disable auto-lock, but that would decrease battery life. Might be better to continue music playing, regardless of auto-lock:
#import <AVFoundation/AVFoundation.h>

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:NULL];
Now your application works in same as iPhone music player (iPod). Please read more technical info about Audio Session Programming at Apple iOS Reference Library.

Sunday, November 28, 2010

Fonts in iPhone 4.2.1

I've always been intertested in "fonts", so one of the first things to do after installing iPhone 4.2.1 was to checked available fonts. Don't know about others, but I'm pretty sure Apple Color Emoji wasn't in previous iOS version! Now I'm wondering about the others...

Thursday, November 25, 2010

Accessing Unknown 'cornerRadius' Component of a Property

Wanted to create rounded corners for a component, done this before in another project. Suddenly it doesn't work any more:
button.layer.cornerRadius = 8.0f;
Forgot to include header:
#import <QuartzCore/QuartzCore.h>
Error code kind of hinted in that direction, I guess... Wasn't very obvious. Btw check that CoreGraphics.framework is already included in project.

Tuesday, November 23, 2010

UIActionSheet Last Button Not Responding

Your UIActionSheet's bottom button, most likely Cancel, is not responsing to tap.

Check whether there is a UITabBar or UIToolbar below it. The problem is that most likely you try to show actionSheet as subview of your current view, which starts only above tabs and toolbar. However since actionSheet is always shown attached to bottom of the screen, you will miss taps outside your view.

Xcode 3.2.5 Base SDK Missing

Downloaded Xcode 3.2.5 and iOS SDK 4.2, installed and opened an old project. Got error note Base SDK Missing, as expected. Nothing to worry about!

Select project, press Command-i to view project info, find Build - Architectures - Base SDK and select the new "Latest iOS (currently set to iOS 4.2)". Close info - and nothing happens!

Select Targets and your application, press Command-i to view target info. You might have overwritten default project settings by mistake earlier. Check what is Build - Architectures - Base SDK. Should be same as you just defined for project. Close info - and nothing happens!

Close Xcode, reopen your project - and Base SDK is set! Didn't really expect this kind of new features with the update...

Update: No need to close Xcode, just toggle some settings via "Overview" popup at top left corner. For example change "Active Configuration" to Release and back to Debug. Usually that's enough, though sometimes target device has changed, too. Verify all "Overview" selections after you've done this.

Sunday, November 21, 2010

How to Disable Copy, Paste and Select for UITextView

Need to use UITextView, but don't want Copy, Paste and Select feature. How to disable that? Tried overwriting canPerformAction:withSender but I got still a flash of quickly disappearing popup menu and selection.

Subclass UITextView and overwrite canBeforeFirstResponder to return always NO:
@interface MyUITextView : UITextView
@end
@implementation MyUITextView
- (BOOL)canBecomeFirstResponder
{
   return NO;
}
@end
Now you have completely hidden and disabled copy, paste and select for UITextView!

Friday, November 19, 2010

Why Doesn't Repeating NSTimer Invalidate

Most likely your timer is started more than once. If you can't find why that happens (or is done by iOS for unknown reasons), easy and safe fix is to invalidate your timer just before you create it:
[self.myTimer invalidate];
self.myTimer = nil;
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1
    target:self
    selector:@selector(updateTime)
    userInfo:nil
    repeats:YES];
You can call [nil method] without problems in iOS.

Tuesday, November 16, 2010

Code Sign error: Provisioning profile can't be found

Check what is Info.plist Bundle Identifier, go to Apple iOS provision portal and download related App ID provision file and install. Note that if you're not Team Admin, you have to ask someone else to download the file for you.

Actual error code shows also cryptic mobileprovision id, but that won't help you much:
Code Sign error: Provisioning profile '123456F8-1234-1234-ABCD-ABCDEFGH1234' can't be found

The executable was signed with invalid entitlements (install)

This is not the error with EXACTLY same error note you get during ad hoc release creation in Xcode (see here). This one comes, when you try to install that "successfully" created ad hoc release into device.

OPEN, no idea what went wrong or how to fix. Yet.

Code Sign error: The identity 'iPhone Developer' doesn't match any identity in any profile

Your developer certificate is missing, download and install from apple dev website:
  • Revoke, request new, remove old stuff from Keychain Access, install new
  • Create new Development Provisioning profile with new cert, download and install

Team Provisioning Profile - Invalid. Renew button disabled

Team Provisioning Profile - Invalid. Renew button disabled.

Open Xcode Organizer, find Team profile and press Renew button. Automatically downloads Team Provisioning Profile, but only for apps with * App ID.

Monday, November 15, 2010

Everything That Can Go Wrong With Xcode Build and Release

Right now I'm in the middle of serious mess up of my Xcode development environment, can't create ad hoc builds. No idea what triggered it, thus no idea what or how to fix. Since same error notes keep popping up time after time, I start documenting each. Maybe I'll slowly gather enough info to fix something.

Xcode error notes:
Useful articles:
Useful sites:
Have patience, try again, just keep bumbing your head into wall, no pain no gain, good luck, it works for me, wave the rubber chicken above monitor... yep, I know: none of it helps.

You have sympathy. It won't help either.

Build & Archive - Share Application - CSSMERR_TP_NOT_TRUSTED

Build & Archive - Share Application and both Email and Save fail without any visible error note. You seem to have been simply ignored. Try again to be totally ignored.

Possible error notes you can find from Console application - Databases searches - Console Messages:
  •     Command /usr/bin/codesign failed with exit code 1
  •     CSSMERR_TP_NOT_TRUSTED
Open Keychain Access, go to "login" keychain "Certificates" catogory. Open each development related certificate (Apple WorldWide Developer Relations Certification Authority, iPhone Developer, iPhone Distribution) and make sure their Trust details are "Use System Defaults"

Command /usr/bin/codesign failed with exit code 1

Select your project root in Xcode, command-i for Info, go to Configurations tab, delete old "Ad Hoc" configuration, duplicate Release and rename as "Ad Hoc". Rebuild and i installs and runs on connected device.

Problem might have been due space in name (even when it used to work earlier).

The executable was signed with invalid entitlements

Try to create ad hoc release in Xcode, got error note:
The executable was signed with invalid entitlements.

The entitlements specified in your application's Code Signing Entitlements file do not matchc those specified in your provisioning profile (0xE8008016). [OK]
Case 1: Go to Provisioning Portal - Provisioning - Distribution, find your project Distribution Provisioning Profile, Edit - Modify (do whatever and revert if everything already is right) - Submit - Download and double-click the file at your dev machine. Clean and rebuild.

Case 2: make sure you have Entitlements.plist file in your project, containing just one prorerty called get-task-allow". This has to be off. Add name of this file into your project ad hoc release Code Signing Entitlements.

Thursday, November 11, 2010

How to Undo Past Last Point

This is one of those things you do only once, thus easily forgotten. Did it for "my" machine, but now I'm a contractor using customer machine.

So how do I get rid of this annoying warning note, when I want to undo after saving a file:
You are about to undo past the last point this file was saved. Do you want to do this?
Open Terminal window and type:
defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO
Restart Xcode and you're fine. Until next machine.

How to Center UIImage On-Screen

Created dynamically an UIImageView with small UIImage at top left corner. Wanted it centered, but UIImage automagically resized UIImageView, which was by default stuck at that forementioned top left corner.
UIImage *image = [UIImage imageNamed:@"small.png"];
self.imageView = [[UIImageView alloc] initWithImage:image];
There are several ways to fix this, some more complicated while some more difficult. Common thing is that they contain a lot of code to infest with bugs. However simple problems require simple solutions:
self.imageView.center = self.view.center;
[self.view addSubview:self.imageView];
I'm too embarrassed to tell how much time I "invested" until I got to this solution. Let's just say that I learned a lot. Btw this code does not rotate. There are several ways to fix this...

How to Define UITableViewCell reuseIdentifier for loadNibNamed

When you create UITableViewCells, it's important to use reuseIdentified. This lets iOS recycle your table cells, thus improving performance and minimizing memory use. Using it is pretty simple:

static NSString *idCell = @"MyCellId";
cell = (UITableViewCell *)[aTableView dequeueReusableCellWithIdentifier:idCell];
Try to reuse an already existing, but not currently used, cell created earlier with given identifier "MyCellId". If such isn't found, create a new one:
if (cell == nil)
{
    static NSString *nibcell = @"MyCell";
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:nibCell owner:self options:nil];
    cell = (UITableViewCell *)[nib objectAtIndex:0];
}
This code requires a separate stand-alone XIB, which contains nothing but your UITableViewCell. But how do you define the reuseIdenfier? That property is read-only and created when table cell is created.
reuseIdentifier
A string used to identify a cell that is reusable. (read-only)
@property(nonatomic, readonly, copy) NSString *reuseIdentifier
Open your XIB in Interface Builder, select UITableViewCell object, open "Table View Cell Attributes" (press command-1) and write your hardcoded magical string "MyCellId" into "Identifier" field. Use the same string in your code as reuseIdentifier.

Warning: you should NEVER access objects inside XIB (NIB) by magical index into array! Apple might change "something" in next Xcode release and thus break your code. You should use IBOutlets. You have been warned.

...yes, this is one of those sad "don't do as I do, but as I tell you to" cases...

Thursday, October 28, 2010

EGOTableViewPullRefresh for multiple UITableViews

Wanted to add a "Pull To Refresh" feature similar to Tweetie2, so i looked around to see if there was any reusable code (screenshot from Zattr).

Cocoanetics (ex-Dr. Touch) has a good article explaining"pull to refresh" internal workings, well worth investing the time and effort to read. Recommended! However he's solution was not reusable for me, since I don't have UITableViewController. Fortunately he referenced Enormego's solution using UITableViews, including a walk-through with changes.

Wednesday, October 20, 2010

How to Make Round Corners for UITableViewCell

Sometimes you need round border around one or more UITableViewCell. Or maybe a button look-a-like without going through the trouble of actually making one. Here's a quick way:

First of all, you MUST include QuartzCore header, else meet some interesting error notes:
#import <QuartzCore/QuartzCore.h>
Then add this code after your cell initialization routine, usually at cellForRowAtIndexPath:
CALayer *layer = cell.layer;
layer.borderColor = [[UIColor blueColor] CGColor];
layer.borderWidth = 3.0f;
layer.cornerRadius = 20.0f;
Like many things in iOS SDK, you get to a certain point with relative ease - and this is about it. If you want to have more control over your "bubble button" you either dig deep into Quartz sample code or implement something which looks same in a completely different way.

For example I replaced this (got a bit further with carefully positioned UIViews inside UITableViewCell) with hardcoded bitmaps and scaling them nicely.

Thursday, October 14, 2010

How to Control UISearchBar Background Color

When you have a list, you want to search it. How would you do that? No, don't code it by yourself. Done it, removed it.

By default each UITableView comes with search support via a built-in UISearchDisplayController. You just add and connect UISearchBar to your table in Interface Builder and provide some delegate methods to make it work. Pretty easy:
self.searchDisplayController.delegate =
self.searchDisplayController.searchBar.delegate =
self.searchDisplayController.searchResultsDataSource =
self.searchDisplayController.searchResultsDelegate = self.mySearchObject;
Controlling how your UISearchBar looks like is a bit more difficult. If you want to show search box and some other component(s) side by side, you can do it by putting UIToolbar at back and a short UISearchBar on top of it. By default their backgrounds are different, looking pretty ugly together. Like a hack, what it really is.

You can fix that by doing two things. First get rid of default background:
for (UIView *view in self.searchBar.subviews)
{
   if ([view isKindOfClass:NSClassFromString
     (@"UISearchBarBackground")])
   {
     [view removeFromSuperview];
     break;
   }
}
...and the second thing? Something that will be extremely hard to find out, unless you get it right by accident? You have to set UISearchBar style Black Translucent!

Sad to think how many hours I wasted experimenting Things That Will Not Work, just to find out that kind of miniscule solution. Also bet that this will not work one year from now...

Wednesday, October 13, 2010

Custom Data Formatter for NSIndexPath

Have been using UITableViews for a long time, always complained to myself why cannot I see section and row right away in debugger or variable popup. Something like with CGRect or CGSize. Well, this morning I was doing something completely different. Suddenly solving NSIndexPath debug problem was the much easier task:
section={(int)[$VAR section]}, row={(int)[$VAR row]}
Start debugging anything with a local NSIndexPath variable, even a temporary dummy one. Put breakpoint right after it. When debugger stops, double click on debugger window summary row for NSIndexPath variable and paste the string above there. Seems to stick even after restarting Xcode.

Now I can continue fixing The Real Problem.

Monday, October 11, 2010

How to Find Out Width of Truncated UILabel Text

Found out my tip "How to Float an Icon after UILabel" worked only in positive scenarios. If your text string is longer than UILabel width, icon could have been pushed out of sight. In worst case nobody would ever have seen the icon!
// Title
label = (UILabel *)[cell viewWithTag:1];
label.text = @"This is a very long title which is too long";

// Icon
image = (UIImageView *)[cell viewWithTag:10];
image.image = [UIImage imageNamed:@"logo.png"];
Some measurements of different methods, just to check what is available:
CGSize frame1 = label.frame;
// 205x21
CGSize frame2 = [label sizeThatFits:label.frame.size];
// 291x19
CGSize frame3 = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size];
// 163x19
CGSize frame4 = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
// 203x19
CGSize frame5 = [label.text sizeWithFont:label.font forWidth:label.frame.size.width lineBreakMode:label.lineBreakMode];
// 203x19
Next icon positioning. Here you should have calculations to compensate for different icon sizes (in case all of your icons are not same size):
// Icon positioning
CGSize frame = image.frame;
frame.origin.x = label.frame.origin.x + frame5.width + 4;
image.frame = frame;
Ok, now that's perfect (until next defect report)!

Thursday, October 7, 2010

How to Get Rid of Keyboard with a Tap on Background part2

There was a nice way to remove keyboard by using UITextView, but that has stopped working with iOS4. At least I got such a defect report for beta application. This time I will remove keyboard using UIButton, let's see how long that will work:
- (IBAction)removeKeyboard
{
    [textField1 resignFirstResponder];
    [textField2 resignFirstResponder];
}
Open your view in Interface Builder, create new custom full screen UIButton to cover everything, select UITextFields and UIButtons (etc.) one by one and "Send to Front" via Layout menu. Connect UIButton "Touch Up Inside" to your callback and that's it.

Nice, easy, simple, fast - what more can you ask for!

Wednesday, October 6, 2010

How to Float an Icon after UILabel

Want to place a floating icon right after a variable lenght text string - using Interface Builder? Wonderful helper that IB tool or so I thought...

First create a UITableViewCell template with IB, put there a fixed lenght UILabel followed by a UIImageView. No problems, their values are easy to setup at cellForRowAtIndexPath with view tag id numbers:
UILabel *label = nil;
UIImageView *image = nil;
CGRect frame;

// Title
label = (UILabel *)[cell viewWithTag:1];
label.text = @"Title text";
But how to "float" that image right after text string? UILabel is fixed sized, so that won't help. Have to check the size of label.text instead:
// Icon
image = (UIImageView *)[cell viewWithTag:10];
image.image = [UIImage imageNamed:@"logo.png"];

// Icon positioning
frame = image.frame;
CGSize f = [label sizeThatFits:label.frame.size];
frame.origin.x = label.frame.origin.x + f.width + 4;
image.frame = frame;
That's it, very simple! Just beware that your tag numbers MUST be unique.

I had a problem that Interface Builder wanted to set UITableViewCell tag same as my image tag. No errors, no warnings, no indications of problem - except that I couldn't get my image frame no matter how much I banged head into brickwall! Removing tag didn't help, it just automagically came back. Ouch!

Tuesday, October 5, 2010

How to Disable UITabBarItems but Only Temporarily

User is allowed to use only one UITabBar item, until he has done something e.g. made a selection on list. Other tabs are available after that. Access to tabs can be blocked again e.g. if user chooses to delete current "entry".

Tab access control is done in AppDelegate, since it owns UITabBarController and UITabBarItems. Just set up delegate handling - and that's the key: either there is a delegate to block tabs or there is none for free access:
@interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{
   UITabBarController *tabBarController;
}
...
- (BOOL)tabBarController:(UITabBarController *)aTabBarController
shouldSelectViewController:(UIViewController *)viewController
{
  if (
    ([aTabBarController.viewControllers objectAtIndex:1] == viewController) ||
    ([aTabBarController.viewControllers objectAtIndex:2] == viewController)
  )
  {
    // Disable switch to tab 1 and 2
    // Check: otherViewController to enable
    // Check: SomeViewObject to disable
    return NO;
  }
  else
  {
    // Tab ok at index 0
    return YES;
  }
}
Access control can be enabled in any other class/file:
- (void)blockTabAccess
{
  MyAppDelegate *app = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
  app.tabBarController.delegate = app;
  [app SwitchToTab:kTabFirst];
}
Access control can be disabled in any other class/file:
- (void)releaseTabAccess
{
  MyAppDelegate *app = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
  app.tabBarController.delegate = nil;
  [app SwitchToTab:kTabSecond];
}
Some consider it poor design to access AppDelegate directly from "anywhere" and further more to use it to control application state. My defense are the classic a) it's easy b) it's fast c) it works and d) sometimes central control place is a good thing.

Btw this works only as long as you don't need UITabBarControllerDelegate for any other purpose. You have been warned.

Thursday, September 30, 2010

How to Create Default.png

Every released application must have a Default.png file, which is presented during startup. It is supposed to present the default startup view - but empty.


Since all applications are unique (even the thousands of fart apps), there is no single way to create a default.png. However here's two tips, which I use:

1) If your startup view contains a list, create empty list by defining number of sections as 0 (zero):
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // For Default.png creation
    return 0;
}
2) If your application has UITabBar, clear tab titles:
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // For Default.png creation
    for (UITabBarItem *item in self.parentViewController.tabBarController.tabBar.items)
        item.title = @"";
}
In general Default.png should not contain any text strings, since one day you might translate your application to other languages. That day you don't want to flash different language strings during app launch.


...oh, and how to create the actual file? Fix your app to present an "empty screen", build and launch debug version in your handset and take a screenshot in your real device by pressing "Power Button" at top and "The Other Button" at front at the same time. Finally open iPhoto in Mac OSX machine and transfer the pic from iPhone...

Tuesday, September 28, 2010

Note about MKAnnotation Protocol implementation

Sometimes you just have to take things as they are given, just like that. For example adding MKAnnotation protocol support all you have to do is create an instance variable called "coordinate":
@interface MyPointItem : NSObject <MKAnnotation> {
    NSString *title;
    NSInteger distance;
    CLLocationCoordinate2D coordinate;
}
As Apple documentation says:
"An object that adopts this protocol must implement the coordinate property."
There you are. As long as you remember, WHY you have a variable called "coordinate" INSTEAD OF "location". If you don't remember and apply Natural Naming to your code, your application will start crashing horribly for mysterious reasons...

Tuesday, September 21, 2010

How to Hide UITabBar

Sometimes you need to show a single stand-alone UIView without letting user change tabs. For example there is a list, where you provide an info view using a disclosure accessory button. The question is then: how to hide UITabBar:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
    InfoViewController *info = [[InfoViewController alloc] init];
    info.hidesBottomBarWhenPushed = YES;
    [self.navigationController pushViewController:info animated:YES];
    [info release];
}
 Pretty simple, when you find the right location to add right line(s) of code.

<Error>: CGImageCreateWithImageProvider: invalid image size: 0 x 0

There seems to be a bug in iOS 4.0 built-in MapKit, which causes error message in both simulator and real device:
<Error>: CGImageCreateWithImageProvider: invalid image size: 0 x 0.
This happens when I open UIViewController, which contains MKMapView (all created with Interface Builder). Since user location is not needed at this moment, I just unchecked "Shows User Location" checkbox on Map View Attributes.

Warning disappears and I'm back on Zero Warning policy!

Friday, September 3, 2010

How to Create Human-readable Geo Coordinate Strings

How to create human-readable coordinate strings, a code snippet that I wrote long time ago. Released here as a reminder for The Next Time (there's always a next time):
CLLocationCoordinate2D loc = mapView.userLocation.coordinate;

BOOL loc_NS;
if (loc.latitude > 0)
{
    loc_NS = YES;
}
else
{
    loc_NS = NO;
    loc.latitude *= -1;
}

int lat_d = floor(loc.latitude);
loc.latitude -= lat_d;
loc.latitude *= 60;
int lat_m = floor(loc.latitude);
loc.latitude -= lat_m;
loc.latitude *= 60;
int lat_s = floor(loc.latitude);

BOOL loc_WE;
if (loc.longitude > 0)
{
    loc_WE = NO;
}
else
{
    loc_WE = YES;
    loc.longitude *= -1;
}

int lon_d = floor(loc.longitude);
loc.longitude -= lon_d;
loc.longitude *= 60;
int lon_m = floor(loc.longitude);
loc.longitude -= lon_m;
loc.longitude *= 60;
int lon_s = floor(loc.longitude);

location.text = [NSString stringWithFormat:@"Location: %d°%d′%d″%@ %d°%d′%d″%@",
    lat_d, lat_m, lat_s, loc_NS ? @"N" : @"S",
    lon_d, lon_m, lon_s, loc_WE ? @"W" : @"E"];
Based on Wikipedia article about Geographic coordinate conversion.

Thursday, August 26, 2010

How to Remove Unused Parameter Warning

No Warnings Policy requires sometimes forcing a compiler warning to be ignored. One such case is when you are given a function you absolutely must use, but don't need the parameters:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
#pragma unused(application)
    [window addSubview:viewController.view];
    [window makeKeyAndVisible];
}
There are several ways to ignore warnings, I found #pragma to be a clean solution.

Wednesday, August 25, 2010

grep Regular Expressions and Internationalization

Wanted to find all meaningful text strings in an iPhone project, preparing for a proper internationalization:
grep -r '@"' * |grep -v -E 'NSAssert|NSLog|@""'
Forgotten this so many times, it's better to write it down: how to search for several patterns at the same time using grep on Mac OS X command line (bash shell). Could use also command line parameter -P (Perl Regular Expression) in this case, but -E (Extended Regular Expression) is more generic.

...so why wasn't internationalization done in the first place, when things were still simple? Because in certain kind of experimental projects you need to get results fast and early to decide whether it's worth continuing at all. If available time is used for "doing things right", there won't be any (good enough) results and The Project Will Be Terminated.

Just a question when do you want to invest your time: early or later. Have to pay the price anyway. Usually the longer you wait, the higher the price will be. On the other hand at that time you might have afford it.

Multiple Info.plist files and UIApplicationExitsOnSuspend

I have three Info.plist files: one for release (Info.plist), second for release testing (Info_release.plist) and third for testing (Info_test.plist).

In other words, I generate three separate applications from same sources so that all can be installed in a device at the same time. Therefore the content of plist files are identical, except CFBundleIconFile and CFBundleIdentifier.

When looking at Info.plist in XCode I can see "Application does not run in backgr" with a checkbox, while the two other plist files show UIApplicationExitsOnSuspend. Open all three files as source code or text and they contain exactly the same code:
<key>UIApplicationExitsOnSuspend</key>
<true/>
Weird... still everything seems to work ok. Just a note. Btw official documentation is available here.

Tuesday, August 24, 2010

How to Show Text Left and Right in UIWebView

When you need to show text on same line at left and right in UIWebView, this is one way:
NSString *myHtml = [NSString stringWithFormat:
    @"<html><body>"
    "<span style='float:left'>%@</span>"
    "<span style='float:right'>%@</span>"
    @"</body></html>",
    @"I'm left", @"I'm right"];
   
[self.cellWebView loadHTMLString:myHtml baseURL:nil];
Can't use div, since it seems to force a linebreak by default.

Monday, August 23, 2010

How to Get Share Buttons into Blogger

Blogger supports Share Buttons - officially.

Unofficially you might have to do some manual tweaking to make it actually work: Solution: Blogger Share Buttons not showing up? and reverse style.

Thursday, July 22, 2010

How to Solve Ad Hoc release "value not permitted by a provisioning profile"

Ever since I submitted an application to App Store I've had lots of problems creating Ad Hoc release. Though first fix was simple, it's not a complete solution.

Error messages are:

Thu Jul 22 11:46:34 jouni-iDevice installd[643] : entitlement 'keychain-access-groups' has value not permitted by a provisioning profile

Thu Jul 22 11:46:34 jouni-iDevice installd[643] : entitlement 'application-identifier' has value not permitted by a provisioning profile
...
Thu Jul 22 11:47:06 jouni-iDevice SpringBoard[28] : Application 'Zattr' exited abnormally with signal 11: Segmentation fault

This time the solution was found in TUAW article "devsugar: A better way to share ad-hoc builds".

Summary: instead of normal Build use Build and Archive, find your application, find the last date, press Share Application button and - The Critical Phase - verify your identity is your Ad Hoc Distribution Provisioning Profile. Save locally and install e.g. via iPhone Configuration Utility.

...suspect Ad Hoc release problem is caused by having two different provisioning profile files using same application id. I generate three applications (development, release test, public) from same sources with some compile time flags and different provisioning profiles and app ids. Guess code signing identities got mixed.

Warning - No location found for "MyFile.m:42"

Debugger Console might show you something like this:
Warning - No location found for "GuestLoginNote.m:33"
This is most likely a forgotten breakpoint in a file which doesn't exist any more. To get rid of the warning, please go to XCode menu Run - Show - Breakpoints. Locate the problem breakpoint and delete it.

Tuesday, July 20, 2010

The Curse of Version Control

Version control is a MUST for any developer - but that doesn't mean you have to use an external version control system. The importance is in the idea and attitude instead of using any specific tool.

Last week we got a harddisk crash, removing SVN version control and TRAC project management from service. Regardless of that - and summer vacations causing delay in repair - work must go on. Was shocked to realize how much I had started to rely and depend on SVN as part of normal development cycle. Suddenly I was on loose ground!

Version control without a version control tool requires certain kind of routines:
  • Think before you start doing anything
  • Complete your thinking before you start doing anything
  • Test your thinking before you start doing anything
  • When thinking is done, start doing - don't procastinate

This time it's all about you. It's all in the mind. It's personal.


  • Split the work into small enough parts which you can design, implement, test and release during one cycle
  • Never start anything you can't complete during same session
  • Make regular backups of your progress, even local ones
  • Document what you plan to do and how you think you did it
  • Automate anything you can
The less you have to keep in your mind, the better you can focus. The better you can focus, the faster you get results. The faster you get results, the easier it will be to fix them.

Thursday, July 15, 2010

How to Accept Self-Signed Server Certificate

We have public server, test server and release test server. Likewise I generate three different binaries, each connecting to different server. The problem is that test servers have self-signed certificates and iPhone fails by default in NSURLConnection sendSynchronousRequest. The error note is:

ERROR CODE -1202 = The certificate for this server is invalid. You might be connecting to a server that is pretending to be “xx.xx.xxx.xxx” which could put your confidential information at risk.
The solution with iOS4 is this one line:
#if (defined(TEST_VERSION) || defined(RELEASE_TEST_VERSION))
    [NSURLRequest setAllowsAnyHTTPSCertificate:YES
                                       forHost:[url host]];
#endif
    [NSURLConnection sendSynchronousRequest:request 
                          returningResponse:&response
                                      error:&error];
Please note that the specific line is undocumented class method and thus not used in official release, only for testing with simulator or ad hoc releases.

To remove compiler warning add this at the beginning of your file:
#if (defined(TEST_VERSION) || defined(RELEASE_TEST_VERSION))
@interface NSURLRequest (DummyInterface)
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host;
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host;
@end
#endif
More info at Dr. Touch article: Ignoring Certificate Errors on NSUrlRequest.

iOS5 update: this code might (or maybe not) help a little from my blog article Server API change from HTTP to HTTPS

Random Crashes while Debugging - by Debugging

Sometimes the problem is that there is no problem. Trying to debug it more can make the situation even worse - while the real problem was caused by debugging in the first place!

If you have mysterious freezes while debugging UITableView, try reducing the amount of logging. Fixed one such problem by NOT sending any NSLog messages from cellForRowAtIndexPath delegate callback. Seems like too many log messages messed up internal iOS timing.

Wonder how many times I have to re-iLearn this iLesson!

Tuesday, July 6, 2010

UITextField textFieldShouldBeginEditing called twice in iOS4

When you tap UITextField, you get two textFieldShouldBeginEditing calls. This looks like a bug in initial iOS4 release. Workaround is to use a BOOL flag to decide what you should do with that call:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
    if (self.allowEdit)
    {
        [self askUser];
        return NO;
    }
    return YES;
}

- (void)askUser
{
    if (self.alertVisible)
        return;    
    self.alertVisible = YES;
    
    UIAlertView *alert =
    [[UIAlertView alloc] initWithTitle:@"Please verify"
                               message:@"Do you want to edit?"
                              delegate:self
                     cancelButtonTitle:@"Cancel"
                     otherButtonTitles:@"Yes",
     nil];
    [alert show];
    [alert release];
}

- (void)alertView:(UIAlertView *)alertView
    clickedButtonAtIndex:(NSInteger)buttonIndex
{
    self.alertVisible = NO;
    if (buttonIndex == 1)
        [self doSomething];
}
The bug will (should) be fixed in some future iOS4 release, so it's more safe to isolate your work-around inside your own routines as much as possible to minimize possible unwanted side-effects.

Friday, July 2, 2010

Entitlement 'keychain-access-groups' has value not permitted by a provisioning profile

You build and install application you are developing and fail with a flash without any obvious error note. Using iPhone Configuration Utility you're device's Console contain something like this:
Fri Jul 2 10:26:23 jouni-iDevice installd[6313] : entitlement 'keychain-access-groups' has value not permitted by a provisioning profile
Fri Jul 2 10:26:23 jouni-iDevice installd[6313] : entitlement 'application-identifier' has value not permitted by a provisioning profile
The reason is that you have previously installed your application from App Store and overwriting app binary with a developer version is not enough.

Quick fix is to delete the application from device, then build and install again.

Thursday, July 1, 2010

Class does not implement the 'NSStreamDelegate' protocol

Got this warning after upgrading to Xcode 3.2.3. To fix it add NSStreamDelegate into your class interface:
@interface MyClass: NSObject {}
@interface MyClass: NSObject <NSStreamDelegate> {}
NSStreamDelegate is defined for Cocoa, but not Cocoa Touch. iPhone stream:handleEvent: is an informal delegate method.

On Mac OS X 10.6 SDK (used by iPhone OS 4) delegate methods are generally defined in formal protocols rather than informal protocols as before, which means you have to declare the protocol conformance in the class interface of the delegate. More info at Mac OS X SDK release notes.

How to target pre-iOS4 devices with latest XCode

Had a moment of confusion when upgrading to XCode 3.2.3. That version seems to support only iOS3.2 (iPad) and iOS4. Tried using previous XCode 3.1.3 for developing iOS3 software, just to get frustrated by non-backwards compatibility issues.

Use latest XCode 3.2.3. Set project Base SDK as iPhone Device 4.0 and iPhone OS Deployment Target as iPhone OS 3.0 - or anything as far backwards as iPhone OS 2.0.

Thursday, June 24, 2010

Another reason for "Undefined symbols referenced from"

Failed to add a static library into my iPhone application. Doing everything I've done earlier, searching for new tips on internet, recreating static library project dozens of times - and still:
Undefined symbols:
"_xmlDocSetRootElement", referenced from:
+[CXMLNode(CXMLNode_CreationExtensions) documentWithRootElement:] in libTouchXML.a
ld: symbol(s) not found
collect2: ld returned 1 exit status
Finally found out that even though my version of TouchXML links to dynamic libxml2, I need to link libxml2 with application, too! Expected that once would be enough. Now wondering whether it really will be linked twice.

Unexpected File Type 'wrapper.pb-project' in Frameworks & Libraries build phase

This is a XCode project dependency analysis warning and most likely means your project is missing something you expected to be there. There are similar warning notes for both build and link phases. More info at "Xcode's Plugin Interface".

warning: skipping file '/Users/jounimiettunen/svn_iphone/project/trunk/protobuf/ProtocolBuffers.xcodeproj' (unexpected file type 'wrapper.pb-project' in Frameworks & Libraries build phase)

It means that building or linking something failed, in this case ProtocolBuffers static library. Possible reasons are missing "Linked Libraries" and/or "Direct Dependencies" definitions or that building those failed.

Possible Fix: make sure your target (Info - General) has correct setup. If you're using Apple SDK 4.0 add -ObjC into "Other Linker Flags" and also -all_load to force loading of everything. Make sure you do this for both project and target. More info at Mac OS X Reference Library "Technical Q&A QA1490": Building Objective-C static libraries with categories.

Tuesday, June 22, 2010

Dereference of null pointer

XCode Build and Analyze command finds several "Deference of null pointer" warnings in some 3rd party sources.

Pointer with a possible value NULL is used like it always points to a valid memory area:
#include <stddef.h>
int *p = NULL;
*p;
Trying to use a null pointer *p will cause undefined behaviour, depending on compiler and operating system. Reading from it might return garbage values, writing to it might corrupt memory somewhere else. Null pointer errors have also been used to go around security systems. Protected mode OSes usually stop the application trying to do such things, causing application to crash instead of the OS.

How to fix: check pointer value before trying to use it. Code safe.

Thursday, June 17, 2010

Blogger and Google Analytics

Google Analytics for Blogger has been under private beta since April 2008, so don't hold your breath any more. What you can do is the "old fashined" way of Copy & Paste Google Analytics script at the end of your Blogger template.

Just remember: when you change blog template, you lose tracking right away. Immediately.

Apple AppStore status: Rejected

Some application submission status change emails go to "support" address only, not to iTunes Connect account holder.

There's excuse for everything, I can think of a dozen right away. Still find it rather strange that some emails go to account holder (review started) while other not (rejected). Makes one wonder many a things. Processes and people, for example. Theory and reality vs. achieved results. Investing time in right places.

Reason of Rejection: does not work with soon-to-be-released OS update.

Guess it's time to download beta SDK. Makes one wonder a few other things, too, like importance of backwards compatibility and legacy support. Seen different solutions for it, but kind of like Apple approach. Good for business, too.

Btw the content of "reject" email was just excellent. There was everything I could ask for and then some. Definitely time invested in right place!

Monday, June 14, 2010

Learn iPhone Programming by Example

Best way to learn is by example, at least for some people. Haven't checked these myself, but like the idea: 35 open source application.

ManiacDev.Com: 35 Open Source iPhone App Store Apps – Updated With 10 New Apps!

Looking at titles and descriptions, there are several I wish to check later. Might even use a few!

Thursday, June 10, 2010

Apple AppStore submit observations

Read docs a dozen times, but somehow missed these:
  • Keywords can be max 100 characters - including spaces and commas
  • Select "None" for first rating to get immediately 4+ rated
  • CFBundleShortVersionString in the Info.plist file must be a period-separated list of at most three positive integers. You cannot use e.g. "1.0.0 (1)" format, except in ad hoc releases
  • Large icon filename must end with .jpeg .jpg .tif .tiff .png. It's not about file format, but filename
  • Upload screenshots in reverse order, the first shall be last

Monday, June 7, 2010

How to Make Instruments Show Your Own Code

Debugging memory leaks with Instruments. Great tool, but would be more useful, if I could see there references to my code.

Turned out it's a known iPhone OS 3.0 defect, almost two years old! No idea why it hasn't been fixed by Apple. Maybe old SDKs are not supported on purpose, which does make some business sense.

How to fix: Go to your Xcode project, select project info, go to Build tab and search Base SDK. Change your project Base SDK to 3.1. I had there 3.0, which seems to be a known defect.
  • Build - Clean All Targets - select all - Clean
  • Build - Build and Analyze
  • Run - Run with Performance Tool - Leaks
  • Stop
  • Open popup item with application name - Launch Executable - Choose Executable
  • Browse to your Build directory and select your application
  • Record (the red Dot button)
It's not enough to start your application from Instruments, but you also have to choose the file. Stop and Record is not enough.

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!

Thursday, April 29, 2010

Multiline UIButton Title

Some things are really easy with iPhone SDK - eventually. For example if you want to have a non-custom UIButton with multiline title:

Write button title in "Title" field. When you want to have a linebreak, press Alt-Return. Then set "Line Breaks" as Character Wrap or Word Wrap. Done!

(Using Interface Builder)

Monday, April 26, 2010

How to Get Rid of Keyboard with a Tap on Background

There is a screen with a couple of UITextFields and UIButtons. When user taps on UITextField, keyboard popups. Very nice of iPhone SDK to provide such a feature! Each UITextField and UIButton is a clickable object with easy callbacks.

But how do you get rid of keyboard?

Should you have a special "Please hide keyboard" button or tap into accelerometer to notice when user gets desperate enough to start shaking the device? There's an easier way...

Add a full-screen empty non-editable UITextView over the whole screen. Select each clickable object e.g. UITextField, UIButton or other UITextViews and use Interface Builder menu item Layout - Send to Front to place them on top of your full-screen UITextField.

When user clicks on anywhere except your clickable objects, keyboard disappears. What a relief!

Please note that, if you leave any non-clickable objects e.g. UILabel on top of your full-screen UITextView, clicking on those spots will not hide keyboard.

Friday, April 23, 2010

UITableView doesn't scroll

This makes no sense, but seems like (in Interface Builder) your UITableView must have Scroll View section item Delay Content Touches checked.

Otherwise your table doesn't scroll by touch events.

Why doesn't my graphics change when I edit them

Problem with branching and merging is that graphics files might change physical location. Xcode won't complain anything at all, as long as it can find some copy of the file.

Check if you have several copies of same file e.g. under project root and inside classes folder. Find out which one is being used, remove the other one(s) and fix version control to contain only one copy of the file.

Agree common location for all graphics files, regardless if they are used only within one of several modules in project.

Thursday, April 15, 2010

Problems removing breakpoints

If Xcode refuses to remove breakpoints, first close simulator and then save the source code file. After that Xcode starts to co-operator and nicely removes the breakpoint.

Tuesday, March 30, 2010

Graphics ok in Simulator, not found in Real Device

This is one of the ways that simulator and real device differ: check the names of your graphics files.

Filenames are case-sensitive in real device, not in emulator.

SpringBoard failed to launch application with error code: 7

Set up a system where same sources can produce two different applications, one for release and another for testing. The requirement was they have to be able to exist at the same time in device.

Added a second ad hoc distribution provisioning profile with new bundle identifier. Somehow simulator failed to run the application(s) any more.

Close simulator, go to Xcode menu and select "Empty caches..." menu item. Might as well "Clean All Targets" just in case.

Monday, March 22, 2010

Property 'username' attribute in 'ConnectApi' class continuation does not match 'ConnectApi' class property

Want to declare external API as readonly, while internal has to be writable. Done this before, but right now something doesn't work.

Turned out that the reason is missmatch in unwritten default values. The property types must match in both external and internal declaration, including the non-written default values. This is the correct version:
@interface ConnectApi : NSObject
{
    NSString *username;
}
@property (nonatomic, copy, readonly) NSString *username;
...
@interface ConnectApi ()
@property (nonatomic, copy, readwrite) NSString *username;

Friday, March 19, 2010

Accessing Unknown 'backgroundColor' Component of a Property

Embarrassing problem. Forgot to import the header file or add class forward declaration. Somehow you'd expect those to happen automatically, don't you think?

Code works as-is right after import has been done. Just adding this reminder for google to find next time.

Tuesday, March 16, 2010

Changing Name of "Edit" UIBarButtonItem

If you use default UIToolbar "Edit" button provided by Interface Builder, you cannot change the name from code. You have to change UIBarButtonItem identifier to "Custom" and title as "Edit". Then add this delegate code:

- (IBAction)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [self.myController.myView setEditing: !self.myController.myView.editing animated:YES];
    if (self.myController.myView.editing)
    {
        self.editButton.title = @"Done";
        self.editButton.style = UIBarButtonItemStyleDone;
    }
    else
    {
        self.editButton.title = @"Edit";
        self.editButton.style = UIBarButtonItemStyleBordered;
    }
}
Don't forget to hardcode button width at 43 pixels, otherwise change of button title will shift position of following items on toolbar. That 43 pixels is the default width of standard Edit button and wide enough to show longer "Done" text string.

Monday, March 15, 2010

Interface Builder cannot change UIView color

If you fail to change UIView color in Interface Builder, it could be because Emulator is blocking your changes. Close emulator and try again.

Thursday, March 4, 2010

Unversioned Directory Already Exists in SVN Repository

SVN error note "Failed to add directory 'trunk/build': an unversioned directory of the same name already exists" means that your locally ignored directory has been added under version control. Maybe someone else did it, maybe you did it initially before deciding to ignore.

The fix is to remove those directories from version control. There's nothing you can do locally to fix it, you have to access the main, common, shared repository in network. Remove the directories, do refresh and updates.

Reminder how to ignore directories locally: go to folder (trunk), which contains directory (build) you want to ignore. Do command line "svn propedit svn:ignore" and make sure your list contains all the directories to be ignored.

If that command doesn't work (bash), define your editor as "export EDITOR=emacs" or whatever is your favorite editor. Control-x Control-c exits from emacs, in case you get stuck.

svn status, svn update and svn cleanup might help, too. Remember to read SVN manual BEFORE using any of those commands. You have been warned, all caused problems are own own responsibility.

Monday, March 1, 2010

How to Create a Black Statusbar

When you open MainWindow.xib in Interface Builder, you can find "Window - Status Bar" and "MainViewController - Status Bar". Setting either one or both as "Black" doesn't seem to have any effect on the actual application. Your statusbar remains gray.

So how do I get a black statusbar?

Open your application .plist file, click on plus icon (+) to create a new row and write "Status bar style" as name and select "Opaque black style" as value.

Those are "key = UIStatusBarStyle" and "string = UIStatusBarStyleOpaqueBlack if you're more familiar with that style.

Thursday, February 25, 2010

How to Disable UIWebView Selection

You could do this by running a short javaScript line at webViewDidFinishLoad, but I find this more elegant: define UIWebView properties in CSS.
NSString *myHtml = [NSString stringWithFormat:
   @"<html><head>"
   "<style type=\"text/css\">"
   // Disable Open/Copy/Cancel alert with long link selection
   "* {"
   "-webkit-touch-callout: none;"
   // Disable selection/Copy of UIWebView
   "-webkit-user-select: none;"
   "}"
   ",</style>"
   "</head><body>%@</body></html>", message.body];
[myWebView loadHTMLString:myHtml baseURL:nil];
As a bonus I left there also how to disable link selection popup.

Monday, February 22, 2010

How to Clear UITabBar badgeValue

It's delightfully easy to add a badgeValue into a UITabBarItem, but I couldn't find how to remove it. Turned out it's as easy as adding one:

if (count)
    self.parentViewController.tabBarItem.badgeValue = @"!";
else
    self.parentViewController.tabBarItem.badgeValue = nil;
Thanx Apple for these delightful surprises!

Tuesday, February 16, 2010

Why viewWillAppear Wasn't Called?

Have several views defined as XIB, work just fine as standalone views and as main views under UITabBar. However they stopped working when added them as subviews under main view in UITabBar (don't ask...)

The problem was that viewWillAppear wasn't called automatically, because I added the views manually under another view. Apple documentation suggests calling the missing function manually - and yet again everything works just fine!

self.myController =
[[MyViewController alloc]
initWithNibName:@"MyViewController" bundle:nil];
[self.mainView addSubview:myController.view];
[self.myController viewWillAppear:YES];

Wednesday, February 10, 2010

How to Make UIWebView Open target="_blank" Links

There are several ways to do this, here's mine.
NSString *js = @"\
var a = document.getElementsByTagName('a');\
for (var i=0; i<a.length; i++) {\
    a[i].removeAttribute('target');\
}\
";
[self.myWebView stringByEvaluatingJavaScriptFromString:js];
Possible advantage: reduces size of HTML document.

How to Do Totally Custom Titlebar

iPhone SDK does not support a "titlebar" with custom background image, fully visible title text and titlebar buttons.

You can create semi-transparent UINavigationBar or UIToolbar (style Black Translucent) to see custom background (UIView just below), but your image is still dimmed. You can tune Alpha value between 1 and 0, but at the same time your title text and buttons visibility change, too.

Another solution is to create a custom setup, which just looks like a titlebar:




Inherit your own MyToolbar class from UIToolbar. Place on it 3 UIBarButtonItems: one for left side button, one Flexible Space UIButton in middle and one for right side button. The middle flexible sized button should push your other button to correct locations. If not, change it to fixed size and reside as you wish. Your number of button can also differ, naturally. I used 5.

Here's the full source code for your class:

#import
@interface MyToolbar : UIToolbar {
}
@end

#import "MyToolbar.h"
@implementation MyToolbar

- (void)drawRect:(CGRect)rect
{
// Warning: Hardcoded for portrait size
UIImage *image = [[UIImage imageNamed:@"sample.png"] retain];
[image drawInRect:rect];
[image release];
}
@end

Place a UILabel on top of your toolbar, resize to fit the available space between other buttons. Change font e.g. Helvetica Bold size 18, color red, , shadow black and background transparent (opacity 0%).