Block Syntax is Ridiculous

I love blocks and hate block syntax. It seems so arbitrary!

Consider literal Blocks

^returnType(Type param1, Type Param2) {
Code;
};

Vs. Block Variable Definitions

returnType(^variableName)(Type, Type)

Vs. Block Parameter Definitions

methodName:(returnType(^)(Type,Type))parameterName

What’s going on with the ^ operator, which lives outside parentheses in a block literal but inside in a block parameter?

Why does the parameter name go at the end of the block type declaration, while the variable name goes right in the middle of the type declaration–unlike every other C variable you’ve defined?

Update, as Peter Hosey helpfully pointed out:

And WHY does the return type go outside the ^ in a parameter and variable definition but inside the ^ in a block literal?

It’s like Apple did everything in their power to make sure it was inconsistent. I’m almost surprised that the parameter list isn’t nil terminated when you pass in parameters, but maybe they didn’t think of that.

CGGeometry.h Silliness

Given a rectangle whose edges can be specified by the lines y = 10, y = 50, x = 5 and x = 25:

Q1, Q2: What should the Max and Min X be?
Q3, Q4: What should the Max and Min Y be?

If you’re using the CGRectGetMax family of functions, the answers are:

A1: CGRectGetMaxX = 25
A2: CGRectGetMinX = 5
A3: CGRectGetMaxY = 50
A4: CGRectGetMinY = 10

That seems logical, however this is contrary to the documentation which states that CGRectGetMaxY returns the TOP EDGE of the rectangle, which should be Y = 10 when you’re using UIKit.

But no, the top edge is 50 when whenever the user space is AppKit or Quartz, which was most of Apple’s history up to this day.

Sigh.

Disappointed in XCode/clang today.

The following line of code doesn’t compile:

UIButton *searchBtn = [[UIButton alloc] initWithFrame:{CGPointZero, image.size}];

But this line does:

UIButton *searchBtn = [[UIButton alloc] initWithFrame:(CGRect){CGPointZero, image.size}];

My computer knows who my mother and father are, where I live and where I go to work. Why can’t my computer detect properly typed struct literals when they’re input to strongly typed struct parameters?

Such a fail.

Responding to the Tab and Shift + Tab Keys on iOS 5 & iOS 6, with an External Keyboard

Today I want to solve a problem a lot of people have seemingly given up on.

Innumerable iOS developers have discovered that it is easy to implement field advancement for the iPhone and iPad, so long as it is based on the Return key, by implementing the UITextFieldDelegate method textFieldShouldReturn:. But they also discovered that it’s impossible to alter, and frustrating to experience, how iOS responds to the Tab key.

Most users don’t have a tab key, but all developers have a tab key in their iOS Simulator. Much of the time (if you stick to using UITableViews with editable cells), it will work fine. But if you stray away from single UITableViews of cells, you’re going to have a bad day.

Let me give you a quick example: I have four UITextFields on screen for entering simple pieces of data, and they all use my RootViewController as their UITextFieldDelegate. Every time I demo my app from the simulator I hit the tab key to advance my fields without thinking, and I’m tired of it!

Here is what happens.

Tab Order (Expected):
  • First
  • Second
  • Third
  • Fourth

Obvious, right?

Tab Order (Actual):
  • First
  • Fourth
  • Second
  • Third

So frustrating.

If you’ve been in my shoes, you have by now figured out that there is no way to directly access the keys that a user is typing via iOS, unlike AppKit. If you debug deep enough you’ll notice that all key press events become GSEvents whose implementation is private, and if you tried to access them successfully Apple would likely reject your app. GSEvents are totally hidden and undocumented, and there’s not even a hint of API guidance for responding appropriately to hardware keyboards at all. Nothing in UITextInputDelegate seems to provide any access to the tab key, let alone shift+tab, yet UIKit clearly knows! It works in Safari and it works in Apple’s contacts App, and it works when you stick to plain UITableViews.

This is very inconvenient, since many users use external bluetooth keyboards and it makes us developers look bad when our apps misbehave for reasons beyond our control.

What I’m going to do now is show you how to define an arbitrary tab order for iOS fields by exploiting knowledge of the underlying implementation of tab key input.

First let’s do something easy and handle textFieldShouldReturn:, just in case you’ve never run across this before. People on Stack Overflow like tagging views with integers, but I like arrays. In my case the fact that my views are retained by the array is not significant; in your case it might matter.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.viewOrder = @[self.firstField, self.secondField, self.thirdField, self.fourthField];
    self.uikitObservedOrder = @[self.firstField, self.fourthField, self.secondField, self.thirdField];
    self.currentTextField = nil;
}

-(BOOL)textFieldShouldReturn:(UITextField*)textField {
    if ([self.viewOrder containsObject:textField]) {
        UIResponder *nextField = [self.viewOrder objectAtIndex:([self.viewOrder indexOfObject:textField] + 1) % self.viewOrder.count];
        [nextField becomeFirstResponder];
    }
    else {
        // Unknown field, just resign first responder.
        [textField resignFirstResponder];
    }
    return NO;
}

OK, now we have a simple method that will advance the current responder in a manner of our choosing when they press the return key, and wrap around to the first element from the last element. In fact, if we had other UIResponders besides these text views, we could add them into the viewOrder array to specify the tab order across the whole view controller. Now let’s handle tabs.

Through my own research, I discovered that when you press the tab key:

  1. UIKit rapidly queries all of the text fields on screen with textFieldShouldBeginEditing: to see which fields can be tabbed into.  (Update: It also queries the hidden status of the fields and will notice if they are hidden to exclude them from step 2.)
  2. UIKit chooses a target responder with some non-obvious, nonvolatile internal algorithm and repeats the query to the target responder.
  3. If the target responds YES again, then the target field begins editing.
  4. If the target responds NO the second time, then nothing happens as a result of the tab but you enter a new state where tabs are restricted.
  5. If tabs are restricted, then when you tab UIKit will still query all your fields to see if they are restricted, but it’s only looking for the Target Field to return YES. The results of the other queries appear to be discarded.

My methodology is straightforward:

  1. I keep track of the calls to textFieldShouldBeginEditing: and almost always return YES.
  2. I only return NO when I know that UIKit has examined all of my fields once without any text field entering edit mode, and only if UIKit’s arbitrary order didn’t already pick the field that I want UIKit to pick.

More specifically, by examining which target field UIKit picked and comparing that against the observed arbitrary tab order, I can determine whether the fields are advancing forwards (tab) or backwards (shift + tab). I then call becomeFirstResponder on the next or previous field from the currently selected field. (I also clear up any tracking data immediately before calling becomeFirstResponder, to avoid going infinitely recursive.) Whenever textFieldDidBeginEditing: is called, I also make sure to clear all my tracking data.

static int flags = 0; // Arbitrary method of keeping track of a few fields
- (void)recordTextFieldEntered:(UITextField *)textField {
    // If all four fields haven't been visited, mark the current field as visited.
    if (flags != 0xf) {
        int flag = 1 << [self.viewOrder indexOfObject:textField];
        flags |= flag;
        self.uikitExpectedField = nil;
    }
    // Else we have recorded the field UIKit expects
    else {
        self.uikitExpectedField = textField;
    }
}

- (BOOL)didBypassUIKitTab {
    if (self.uikitExpectedField) {
        int advancement = [self.uikitObservedOrder indexOfObject:self.uikitExpectedField] - [self.uikitObservedOrder indexOfObject:activeField];
        advancement += self.uikitObservedOrder.count; // Just to make sure it's a positive number

        UITextField *tabbedTextField = [self.viewOrder objectAtIndex:([self.viewOrder indexOfObject:activeField] + advancement) % self.viewOrder.count];
        if (self.uikitExpectedField != tabbedTextField) {
            flags = 0;
            self.uikitExpectedField = nil;
            [tabbedTextField becomeFirstResponder];
            return YES;
        }
    }
    return NO;
}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    [self recordTextFieldEntered:textField];
    BOOL bypassed = [self didBypassUIKitTab];
    return !bypassed;
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    flags = 0;
    self.uikitExpectedField = nil;
    activeField = textField;
}

I tried this out and it worked perfectly on both iOS 6 and iOS 5.1, in the simulator and the device. It is perfectly extensible to other types of UIResponders, though the choice of using integer flags to keep track of what’s been added is weak. Adding to an NSMutableSet would be a better alternative and could handle any number of fields.

Hope this works for you!

[Update]

One little addendum. UIKit skips hidden/unavailable fields and your implementation of didBypassUIKitTab should skip hidden fields as well. This is accomplished with a for loop, testing the fields in the order of advancement until you find one that isn’t hidden. If you don’t find a visible field at all (unlikely, but who knows!), you should probably reset everything and resign first responder status.

Follow-up: UITableViewController

In my last post I described a way of getting UITableViewControllers to instantiate via nib files, but it was pretty sub-optimal. I may have discovered a solution.

I noticed that I had another UITableViewController with the same construction that didn’t exhibit the same problem! In fact, the two UITableViewControllers were essentially clones of one another, but one worked in another didn’t.

I tried restarting Xcode.
I tried a clean and build.
Finally, I reset the iOS Simulator.

Ta-da! Suddenly, everything started working.

So “Apple’s documentation should work” is the moral of the story. Perhaps a second moral is: “the simulator is untrustworthy.”

NSInternalInconsistencyException and UITableViewController

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UITableViewController loadView] loaded the &quot;LibraryViewController&quot; nib but didn't get a UITableView.'

This is what I’m staring at right now, puzzled. I’ve already created a LOT of UIViewControllers and a LOT of UITableViews, and they’re always very straightforward.

  1. Define a new UIViewController’s .h and .m files.
  2. Create a nib file.
  3. Set the custom UIViewController to the File’s Owner.
  4. Add a view.
  5. Link the .view attribute from the UIViewController to the view.

Admittedly I don’t create many UITableViewControllers since they seem to do nothing that a normal UIViewController doesn’t do, but this is something of a learning experience right now.

Still, how complicated could it be to create a UITableViewController? This is supposed to be the easy way to create apps quickly, if you believe Apple.

So I created a UITableViewController and a .xib, set the UITableViewController as the File’s Owner in the xib, added a UITableView, set it as the view (curiously there’s no tableView outlet, but surely because it’s redundant, right???) and I should be good to go! What else could there be to do?

The UITableViewController class creates a controller object that manages a table view. It implements the following behavior:

  • If a nib file is specified via the initWithNibName:bundle: method (which is declared by the superclass UIViewController), UITableViewController loads the table view archived in the nib file. Otherwise, it creates an unconfigured UITableView object with the correct dimensions and autoresize mask. You can access this view through the tableView property.
  • If a nib file containing the table view is loaded, the data source and delegate become those objects defined in the nib file (if any). If no nib file is specified or if the nib file defines no data source or delegate, UITableViewController sets the data source and the delegate of the table view to self.

I’m doing everything Apple says to do, but it still doesn’t work! At runtime, I get the NSInternalInconsistencyException and it blows up.

I might never have solved this problem, and just dropped back to a UIViewController that implements UITableViewDelegate and UITableViewDataSource–like I always do–except I noticed this post on Stack Overflow.

Absurd as it may sound, apparently the answer is to create a nib file for your UITableViewController like such:

  1. Set File’s Owner to NSObject.
  2. Create a UITableView in the Interface Builder.
  3. Link the DataSource & Delegate to the File’s Owner in Interface Builder like you would normally.
  4. Don’t link File’s Owner to the UITableView.
  5. Implement -(void)loadView;
- (void)loadView {
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:0];
    self.view = views.lastObject;
}

After all of that, it will finally work.

Ugh. What a pain, and definitely not what Apple told me I’d have to do.

So I’m left wondering. What’s the point of UITableViewController? Apple makes it sound like they’re better in UIStoryBoards. Maybe I’ll continue this experiment in a further post.