Posts tagged ‘iPhone SDK’

UIPopoverController arrow displays incorrectly on iOS 7

This one still doesn’t make sense to me, but I need to put it to bed and move on. When retooling an app for the latest iOS SDK, the UIPopoverController arrow displays incorrectly on iOS 7 in certain instances.

In the case where I need to display a popover controller as the result of the user tapping on a cell in a UITableView, this code has always worked fine in my iPad applications:

[thePopover presentPopoverFromRect:cell.frame inView:theTable permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];

However, after I switched over to using the iOS 7 SDK, the results look like this now:

popover_arrow_incorrect

After a bunch of investigations, I decided to try this just for the heck of it:

CGRect rect = CGRectMake(CGRectGetMaxX(cell.bounds) * 0.9, CGRectGetMidY(cell.bounds), 1.0, 1.0);
[thePopover presentPopoverFromRect:rect inView:cell permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];

With this code change, it now it looks like this:

popover_arrow_correct

And I am happy once again. Although I would still like to know why the original code does not work, if anyone has any ideas please feel free to comment. As you can see from the first image, the popover is positioned correctly (notice the vertical placement of the two line table view inside the popover, it is showing the same in both examples), it is just the arrow that is drawn incorrectly.

BTW, Happy Birthday to Tim Kazurinsky, who was great in his all-too-brief stint on Saturday Night Live, and utterly fantastic in the Police Academy series of films.

Determine which UITableViewCell contains a specified UIControl

If you have UITextField controls inside of a table view cell, and you are hooked into one of the UITextFieldDelegate methods, then you might need to determine which UITableViewCell contains a specified UIControl inside of a delegate method.

The wrong way

Up until now, I have always taken the shortcut of just walking back to the grandparent of the control by doing something like this:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
    UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
 
    // do something here with the cell
 
    return YES;
}

However, when building and running under the iOS 7 SDK, this of course does not work any more.

The right way

After researching this phenomenon a bit more, I found a solution that does not rely on walking the view hierarchy, which by the way kids, is usually never a good idea. Here is a category that I built on top of UIView:

- (UITableViewCell *)findParentCellInTableView:(UITableView *)tableView
{
    UITableViewCell *cell = [self findParentUITableViewCell];
    return cell;
}
 
- (UITableViewCell *)findParentUITableViewCell
{
    if (self.superview && [self.superview isKindOfClass:[UITableViewCell class]])
    {
        return (UITableViewCell *)self.superview;
    }
 
    return [self.superview findParentUITableViewCell];
}

As a result, now my delegate call looks like this:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
    UITableViewCell *cell = [textField findParentCellInTableView:tableView];
 
    // do something here with the cell
 
    return YES;
}

Since I built this category off of UIView, it will work with most common items built on top of UIView or UIControl that you would put in a table view cell, such as UILabel, UITextField, UITextView, etc.

BTW, Happy Anniversary to The Beatles, who first visited the United States 50 years ago today.

EDIT: My first attempt to create the findParentCellInTableView method ended in disaster, as I forgot to take into account the fact that table view cells can scroll off the screen and possible get reused. As a result, I created a method that walks up the view chain more intelligently than simply going to the superview’s superview. As always, please let me know if you have any comments.

Don’t forget to account for new lines in your iOS NSRegularExpression

The NSRegularExpression class is handy for searching and parsing strings. That is, it is handy until it starts to fail at seemingly random intervals.

I had a regex that was working fine on my small set of initial test data, but when I expanded it to work on a slightly larger and varied set of data, all of a sudden the weather turned a bit cloudy.

After some investigation on Stack Overflow and diving through the Apple documentation, I realized that I should probably add the NSRegularExpressionDotMatchesLineSeparators option to my NSRegularExpression, and the skies turned bright and sunny again.

Here is an example of what I am talking about. In this instance, I am looking to obtain the text in an NSString called theString that is in between the sentinel literals of =BeginTag= and =EndTag=:

NSString *pattern = @"=BeginTag=(.*?)=EndTag=";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
    options:(NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators)
    error:nil];
[regex enumerateMatchesInString:theString options:0 range:NSMakeRange(0, [theString length])
    usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop)
    {
        NSString *s = [theString substringWithRange:[match rangeAtIndex:1]];
        NSLog(@"Enumerated matching string: %@", s);
    }];

If you leave off the NSRegularExpressionDotMatchesLineSeparators flag, and theString contains new line characters in between the sentinels, you end up with no text enumerated with the block.

BTW, Happy Birthday to Steven Spielberg, one of the true geniuses of our time.

How to build an app with iOS SDK 6.1 in Xcode 5

When you update to Xcode 5, you get all of the new Xcode and iOS 7 goodness, which is great if you are ready to handle that much goodness. If you have to maintain compatibility with older applications, it can be a problem as the iOS 7 SDK will wreak havoc with your application.

The easier path for now is to just set your project to specifically build against the iOS SDK 6.1 instead of just using the “Latest SDK” entry, and then to either copy or put a symbolic link in the Xcode 5 app bundle to the SDK living in the old Xcode 4.x bundle. Personally, I have found this Stack Overflow post to be handy:

How to point Xcode to my 10.6 SDK so it can be used as a “Base SDK”?

In addition, the answer by Rob Napier references a script that you can use to automatically put these symlinks back into the Xcode 5 bundle when you update the app:

Links Xcode SDKs from the /SDKs directory

EDIT on 12/21/2013: Keep in mind this all becomes a moot point when, on February 1, 2014, Apple will only allow apps to be published to the App Store that have been built with the iOS SDK 7.0 or newer (here’s the link to the article in the iOS Developer Center).

BTW, Happy Birthday to Jim Leyland, the baseball manager who I admired greatly from his time managing the Pittsburgh Pirates.

Getting a JSON string from an array of NSValue objects holding CGPoint structs

I am a big fan of SBJson, and let’s face it, who isn’t? (Thanks Stig!) It has served me well for a long time, and only occasionally do I have to do any real work when it comes to serializing and deserializing JSON.

My current needs required me to take an NSArray of NSValue objects that each hold a CGPoint value, and send that down the wire via JSON. Simply passing the array inside the proxyForJson method halts the processing, so as I looked into it further I discovered that I needed to set up a utility function to create an NSDictionary that could be handled by the SBJson framework. Here is the code that I am using:

+ (NSArray *)jsonCompatibleArrayOfCGPoints:(NSArray *)pts
{
    NSMutableArray *pointsArr = [NSMutableArray array];
    for (int idx = 0; idx < [pts count]; idx++)
    {
        CGPoint pt = [pts[idx] CGPointValue];
        [pointsArr addObject:@ { @"x" : [NSNumber numberWithFloat:pt.x], @"y" : [NSNumber numberWithFloat:pt.y] }];
    }
 
    return [NSArray arrayWithArray:pointsArr];
}

BTW, great sadness today as it is the anniversary of the untimely and far too early passing of Duane Allman, lost in a motorcycle accident this day in 1971.

Objective-C preprocessor paranoia

I am always deathly afraid of test code working its way into a release app. Usually for me this happens when I am trying to optimize a portion of the code by modifying something that is already there and working, which usually leads to something like this in my code during development:

//#define SAFE_OPTION_1
//#define RISKY_OPTION_2
#define DANGEROUS_OPTION_3

So finally I started to set it up so that I can verify that, at least for the archival build of the app, that the wrong option is not selected by using something like this:

#ifndef DEBUG_VERSION
 #if !defined(SAFE_OPTION_1)
  #error The release build of MyApp must have the SAFE_OPTION_1 option selected.
 #endif
#endif

This code will give me a build error if I forget to set the option back to the safe option, which is good.

Also, I wanted to make sure that one of the three options was selected, as commenting out all 3 options is not good, and may not necessarily generate any build errors on their own. So I then figured out that this would do the trick for me:

#if !defined(SAFE_OPTION_1) && !defined(RISKY_OPTION_2) && !defined(DANGEROUS_OPTION_3)
 #error Please select an option.
#endif

Now I get a build error when I accidentally have all 3 options commented out.

BTW, Happy 20th Birthday to the X-Files, which first aired on this date in 1993. I think it is about time for me to go back through and binge watch all 9 seasons of the show.

Centering the toolbarItems in a UINavigationController

When you add to the toolbarItems array of your UINavigationController, it puts the items left justified in the toolbar at the bottom of the screen. If you instead want the items to be centered in the toolbar, here is a way to do it…

NSArray *itemArray = @ [ @"Item 1", @"Item 2" ];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray];
segmentedControl.frame = CGRectMake(0.0, 0.0, 240.0, 30.0);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentedControl addTarget:self action:@selector(segmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *item = [[[UIBarButtonItem alloc] initWithCustomView:segmentedControl] autorelease];
UIBarButtonItem *flex1 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL] autorelease];
UIBarButtonItem *flex2 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL] autorelease];
self.toolbarItems = @ [ flex1, item, flex2 ];
[segmentedControl release];

The flex1 and flex2 items are just like adding the springs in Interface Builder, which space things out equally across the view. This code is not ARC approved as you can see from the autorelease and release methods, but if you need to use it in an ARC project you should be able to figure it out.

BTW, it looks to be a thin day for actor or musician birthdays, deaths, events, or holidays, so Happy Birthday to President Obama.

External full screen UIViewController

While trying to write an app that drives an external monitor hooked up to my iPad, I found that you need to set the wantsFullScreenLayout to your view controller in order for it to go edge to edge. No combination of constraints or struts and springs in Interface Builder or playing with the frame and bounds in code does the job.

As an aside, if you get the message “Could not change executable permissions on the application.” in Xcode when you try to run an app, it is most likely because there is already an app on the device or simulator that has the same bundle identifier that you have specified in your code. To solve, just remove the offending app from the device or simulator and run again, and it should work.

BTW, Happy 30th Birthday to the Nintendo Entertainment System, released 30 years ago today in Japan as the Famicom. Ice Hockey is still one of my all time favorite games.

UISearchBar gotcha

I sort of discovered something quite by accident today.

I have had a persistent crash problem in my app for a long time, so I put in some code from the QuincyKit so that I could get the crash logs from devices when they encountered a problem. Unfortunately, the crash logs that I have been getting have been 99% unhelpful, as the only mention of my app is the main.m line shown at the bottom of the thread 0 stack trace.

In diagnosing a problem in another app today, it occurred to me that the problem in one of my apps could be related. My users have a UISearchBar on top of a table view of items, and they can enter some text to narrow down the item list. I of course am hooking into the textDidChange method in the UISearchBarDelegate protocol. However, at the direction of a specific customer with specific instructions, I discovered that if the auto correction parameter is turned on for the search bar, and some text is entered that has a suggestion below it, then the act of selecting a row from the filtered table view will cause the text in the search bar to be replaced with the suggested text, and then hilarity ensues as the textDidChange is fired again and the program returns a nil record instead of the one I just selected from the table view, since the table view switched in the textDidChange method.

So I corrected this issue in the original app, and it got the wheels turning. So I tried doing much the same thing on a device in my other app, and sure enough, my exception breakpoint in Xcode fires! (I think I heard some angels singing right about this time.) And just for good measure, I set it up so that it does not crash anymore in that instance, it just sits there and does nothing, which apparently is better than a crash.

Needless to say, from now on I am going to set all of my UISearchBar items to have the auto correction parameter set to No.

BTW, happy birthday to Michael Anthony of Chickenfoot fame. Oh, and he was in some band called Van Halen for a while too.

EDIT: I knew that my Spidey sense was detecting something familiar when I was putting together this post. Maybe I should read my own blog every once in a while…

Search bar and correction don’t play nice together

WWDC 2013 wrap up

Well I have been back home for a few days now from the WWDC in San Francisco, and I have to say that it was a super busy week. The content was very solid, and the lines were massive, but all in all worth the time and expense.

BTW, Happy Birthday to Mia Sara, IMHO the real star of Ferris Bueller’s Day Off.