The Dangling Pointer

Sh*t my brain says and forgets about

Scrolling to hidden table view cells

I’m currently using BSKeyboardControls to add that fancy Next/Previous/Done buttons to the top of the keyboard on the iPad. I have noticed, however, that while scrolling through the fields in a UITableView, the cursor would sometimes disappear and the keyboard would be detached (typing does nothing).

I threw some logging together and make a discovery – scrolling to a UITableViewCell far off the screen that is UIView.hidden == YES doesn’t allow subviews to receive the becomeFirstResponder message.

[gist 5100167]

And the logs: [gist 5100147]

The solution (the only one I could come up with) is to fire off a message to the text field after a short delay to become the first responder. This seems to solve the problem.

[gist 5100164]

Adding the fancy search magnifying glass to your UITableView

So you’re working on a search screen or popover with UITableViewController and you’re wondering how to get that fancy search magnifying glass in your search results, right?

Search

In my example, I have my table view sourced by a simple array of data. This may not be the case for you, but you get the idea from the example. Simply add the constant UITableViewIndexSearch as the first section header title – UITableView will understand to replace the {search} text from that constant with the locale-specific image indicating search.

[gist 3226609]

Then, you want to show the search bar when that icon is selected. Normal behavior when selecting an index in the right hand gutter is to scroll to that section header. The search magnifying glass, however, signifies the search bar section. The key is that you have to use a UISearchDisplayController and tell the table view to scroll up to the search bar’s rectangle:

[gist 3226628]

Pretty simple! This feature isn’t well documented in the Apple API but it’s quite handy for making your app feel native.

Bringing in your own hardware to work

As a consultant, I have to move around to various companies as part of my job. Sometimes I am able to stay with the same client for months, sometimes weeks. The one thing that continues to be an issue wherever I go is the availability of a suitable development environment for me to start working on day one. I typically bring in my own laptop so that I can get started as quickly as possible. This is only if the client allows it.

I’ve worked at a number of fortune 500 companies, and typically the answer to the “may I use my own hardware?” question is a solid NO. An exception to that answer was my last client – they are a large company with nearly $19 billion in revenue and yet they have an open policy about outside hardware. They outwardly tell people (at least in the IT areas) that you (employees/consultants) can bring in faster computers and better hardware than we can probably provide. Have at it.

The risk of this position is that of viruses and loss of corporate intellectual property via those personal machines being brought in. The reality of the decision is, it’s not much different than having VPN from home. I, as a developer, need a high quality workstation with lots of RAM. Developing server-based applications requires a lot of resources when testing in a development environment. When asking for a machine less than a year old with 8GB of RAM, I got the typical pushback from desktop support telling me that I shouldn’t be running server applications on a workstation. What? That’s my JOB.

One of my coworkers, a consultant, had been there over a year and already brought in his own monitors and a workstation. I followed suit and brought in my own laptop and monitors. Our productivity skyrocketed having the right equipment for the job. Yes, it’s frustrating not getting the equipment you should expect to do your job. Corporate red tape frequently stifles innovation.

There are positives and negatives for letting people bring in their own hardware. Implement controls to prevent unauthorized connections into corporate file shares and move on is what I say. Think of it this way – you’re paying top dollar to get me in as a consultant. Wouldn’t you want me to be the most effective I can be?

Spring 3.1’s PropertyPlaceholderConfigurer

Spring 3.1 brought a lot of good changes to the framework but with any version change, behaviors can be different. Spring does a good job documenting most of this API changes but there is one that I apparently missed or underestimated the impact of.

Property files can be “imported” into the Spring context so that the values can be inserted into configuration using ${ } with the property value inside of the brackets. This is a handy feature that I use frequently in my Spring projects. The PropertyPlaceholderConfigurer has been replaced by PropertySourcesPlaceholderConfigurer in Spring 3.1. One minor difference between them is the latter has the ability to read @Value annotations for direct injection of property values. The other side effect is that the 3.1 class puts system properties ahead of your property file’s values. 3.0, on the other hand, let your property file win if a name matched one as a system parameter.

I ran into this problem because my 3.0 configuration had a replacement parameter for a data source name injected by JNDI. The JNDI name is different between environments at my client, so I had to parameterize it. In Spring 3.0, my configuration was as so:

[gist 1855160]

You’ll notice the context:property-placeholder element has a single properties file. The values read in are:

[gist 1855426]

and they are used in my JNDI configuration:

[gist 1855169]

When I switched to 3.1, I updated the XML namespace to point at 3.1. This changes things behind the scenes to go to PropertySourcesPlaceholderConfigurer. If you want to retain the original 3.0 behavior, you can keep the XMLNS at 3.0 or add system-properties-mode=”FALLBACK”.

[gist 1855156]

I suspected something was happening with the behavior when I saw in the log files that the data source couldn’t be cast as a string. I pulled out my hair trying to figure it out and ended up renaming pavDataSource to PavDataSource and it worked fine. This problem came back up deploying to another environment having the lower case name and I have no ability to change it. Switching back to 3.0 behavior fixed this because I suspect a system parameter was being created with pavDataSource as the name.

[gist 1855239]

Confusing and frustrating as hell, but the simple mode switch made it right as rain.

Maven & Eclipse target folder + validation stabbyness

Does using Maven with Eclipse make you stabby when you spend an hour waiting for Eclipse to finish validating files? You may not realize it, but Eclipse is indexing/validating files inside of your project’s target folders! Yes kids, this is annoying and time consuming. The target folders also end up showing results in the “Open Type” and “Open Resource” windows. I’m sure we’ve all experienced the duplicate results when searching for a class or resource.

Why does it do this? Eclipse has a feature built in that will, by default, exclude folders that have been marked as “derived”. Normally folders like your source output folders (bin for example) are marked for you. Well, Eclipse doesn’t automatically mark the target folder as “derived” so it treats it like a regular folder. Right click your target folder in Eclipse and click the check box for “derived” – magically the folder is now excluded even from the Open Type and Open Resource windows.

But wait.

Next time you run a Maven “clean”, the target folder is deleted and the check box reset for derived. How do you avoid that? In your parent project (or current project if you have a single module) add the following to your plugin definitions:

[gist id=1801819]

This will force Maven to use version 2.4.1 of the “clean” plugin – if you’re using Maven 3 you can probably remove the version line. After version 2.3 of the plugin, excludeDefaultDirectories became available. This little gem will only delete the target content and leave the folder alone for you.

Enjoy!

Resizing a UITextView automatically with the keyboard

One of the biggest UI no-nos you can perform in iOS is creating a UITextView that assumes the size of the keyboard on the screen before the keyboard even shows up. You should be dynamically resizing your text views by looking at the dimensions of the keyboard, and not assuming you know the dimensions.

Why? Try enabling a Japanese keyboard and see how your app performs. You’ll notice that the keyboard is taller than the English keyboard – it’s to give room to a typeahead area for creating the glyphs. Now how does your app behave?

There is a quick and easy way you can resize automatically. In my example, I have a new view with a nearly full screen-sized text view showing. When the user taps into the text view, the text view is then resized to leave a bit of gap around it and the keyboard displays underneath.

The text view without a keyboard

blanktextview

standardkeyboard
japanesekeyboard

After the user taps in the text view, the keyboard appears. An example of an English keyboard is at left and a Japanese keyboard at the right. Notice any difference? What should happen is your app just figures out how much space you have left after the keyboard is displayed and resize accordingly. If you switch between keyboards, you have to account for visual changes as well. Here is the code I wrote to handle this properly:

- (void)viewWillAppear:(BOOL)animated {
[textView setText:_notesText];

// Register notifications for when the keyboard appears
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)keyboardWillShow:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:YES];
}

- (void)keyboardWillHide:(NSNotification*)notification {
[self moveTextViewForKeyboard:notification up:NO];
}

- (void)moveTextViewForKeyboard:(NSNotification*)notification up:(BOOL)up {
NSDictionary *userInfo = [notification userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardRect;

[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];

[UIView beginAnimations:@"ResizeForKeyboard" context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];

if (up == YES) {
CGFloat keyboardTop = keyboardRect.origin.y;
CGRect newTextViewFrame = textView.frame;
originalTextViewFrame = textView.frame;
newTextViewFrame.size.height = keyboardTop - textView.frame.origin.y - 10;

textView.frame = newTextViewFrame;
} else {
// Keyboard is going away (down) - restore original frame
textView.frame = originalTextViewFrame;
}

[UIView commitAnimations];
}

The view controller adds an observer for when the keyboard shows up and disappears. When the keyboard is coming up, we record the original frame of the text view – this makes it easier to restore the text view when the keyboard goes away. In this specific instance, the keyboard cannot be dismissed since enter is set to put carriage returns in the text box.

Fixing Layer Transparency Issues in Xcode

If you’re looking to get higher frame rates and general application performance tweaks from your iOS application, you may need to take a look at transparent settings on your subviews. Any time you set a subview to be transparent, the OS has to blend multiple layers together to figure out the end flattened result. This blending takes CPU cycles and can impact performance of your app – especially in something as simple as a UITableViewCell.

Luckily there is an easy way to find these problems using Xcode’s companion tool, Instruments.

One of the instrumenting tools you can use on an app deployed to a device is Core Animation. The Core Animation instrument has a bunch of fancy switches you can flip.

coreanimation

That will turn on a visual indicator to show where multiple transparent layers exist on the screen. For example, the main table view of my app MigraineDiary shows the following:

screenshotwithouttransparenciesscreenshotwithtransparencies

To get your app into Instruments, simply select the “Profile” menu item under “Product”. This will build your app and launch Instruments. Make sure you have your device selected and not the iOS Simulator. Any areas that are heavily colored in red should be reviewed by you to ensure you’re not setting layers transparent that don’t need to be. This includes using [UIColor clearColor]!

You’ll notice transparency indicators in red on system components like the tab bar and nav bar. There isn’t much you can do about these, so you can really just ignore them. If you’ve overridden behaviors, however, you may wish to review your code to make sure you’re not breaking something.

Google Data API

I’ve started to mess around with the Google Data APIs recently to help support my Migraine Diary iOS application. Specifically I want users to be able to export their journal entries into a Google Spreadsheet rather than just a plain CSV file. I am using the Objective-C client that Google built and have come across a number of issues or gotchas.

  • The Objective-C client is badly documented – a number of Google Data API calls have zero examples and translating REST calls into Objective-C classes is a challenge.
  • OpenAuth 2 is surprisingly easy to use
  • Google provides very little troubleshooting assistance even with their client
  • Objective-C is very new compared to the Python, .NET and Java clients

That being said, I am making progress. I’ll most likely share my finalized code to create a simple spreadsheet here so others have a jumping off point.

Adium always presenting Apple Quarantine Message

I’ve been getting a warning dialog on my Mac (Lion 10.7) when opening Adium. It recently updated to the newest beta and is presenting the following quarantine message every time I open it –

“Adium.app” is an application downloaded from the Internet. Are you sure you want to open it?

Every other time I have gotten the quarantine message, it clears itself after the first launch. This time it did not. I found a quick and easy way to remove the quarantine extended file attribute:

sudo xattr -d -r com.apple.quarantine /Applications/Adium.app

Relaunch Adium one more time with the “first time you’ve opened” warning and you’re set!

Missing AccessibilitySettingsLoader bundle with iOS 5.0.1 debugging

After upgrading my iPhone 4 to iOS 5.0.1, Xcode has been giving me the following error message when debugging my Migraine Diary app remotely:

warning: Unable to read symbols for /Users/aaron/Library/Developer/Xcode/iOS DeviceSupport/5.0.1 (9A405)/Symbols/System/Library/AccessibilityBundles/AccessibilitySettingsLoader.bundle/AccessibilitySettingsLoader (file not found).
warning: No copy of AccessibilitySettingsLoader.bundle/AccessibilitySettingsLoader found locally, reading from memory on remote device.  This may slow down the debug session.

I tried deleting the directory and having Xcode pull down the symbols off of the phone again.  No success.  I ended up copying the contents of the main SDK bundles into the one in my local library.  Solved it.  Not sure if this is the right solution but it works.  I vaguely remember this happening last time Apple released a small point release.

So copy the following directory:

/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/System/Library/AccessibilityBundles

to:

/Users/[your login name]/Library/Developer/Xcode/iOS DeviceSupport/5.0.1 (9A405)/Symbols/System/Library/

Page 26 of 30

Powered by WordPress & Theme by Anders Norén