Hacker News new | ask | show | jobs
Show HN: iOS Table View that automatically adjusts with keyboard show/hide (github.com)
19 points by bobspryn 5200 days ago
5 comments

During the code review (see below) I found that you are actually resizing the table view to accomplish the effect. This is (in my opinion) not the right way to do it. Here is a suggestion:

Instead of adjusting the frame of the Table View simply work with the contentInset property. This is how UITableViewController does its job.

Short Code Review if you don't mind: (I only want to help - not to be a jerk)

Line 35: Please remove your -init override. You are doing nothing there and if you want to have custom initialization use initWithCoder: and initWithFrame: instead of init

Line 61: id (star)appDelegate = [[UIApplication sharedApplication] delegate];

Please remove the "(star)"

Line 64: CGRect windowRect = appDelegate.window.bounds;

You are trying to get the window bounds. Why are you not using: CGRect windowRect = self.window.bounds;

Line 73: viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height);

Are you aware of: CGRectGetHeight(…) and CGRectGetWidth(…)? I am not saying you should use these functions instead but you could consider using them. I like them very much:

viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, CGRectGetWidth(windowRect), CGRectGetHeight(windowRect));

Line 79:     int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height;

Again: Consider the functions mentioned above. More importantly replace "int" with "NSInteger" - or even better with CGFloat.

Line 80:  if (remainder > 0 && !(remainder > frame.size.height + 50)) {

When using CGFloat you want to make sure to use 0.0 and 50.0.

Line 82:         float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

Why are you assigning the result of -doubleValue to a float?

When using CGFloat you want to make sure to use 0.0 and 50.0.

Actually, on iOS, CGFloat is 32 bits, so you should use '0.0f' and '50.0f'.

On the Mac, in, 64-bit, it's a 64-bit value, so your advice would be correct there.

It is slightly annoying there's no easy way to write a CGFloat literal in a cross-platform way apart from the overly verbose '((CGFloat)50.0)'.

You are - of course - correct. Did too much Mac development lately. :)
Cool thanks! I'll look into those changes. I built this when I was learning iOS on the fly a year or so ago, so I'm sure things aren't all done in the absolute best fashion. I'll learn from your suggestions. Thanks!
You should be setting the TableView's `contentInset` and `scrollIndicatorInsets` properties, rather than adjusting the TableView's frame.

Convert the keyboard frame to the superview's coordinate system using `-convertRect:fromView:`, e.g. :

    keyboardFrame = [tableView.superview convertRect:keyboardFrame
                                            fromView:tableView.window];
The bottom inset is then simply the difference between the TableView's height and the keyboard's Y coordinate.

You would also set those properties and scroll to the selected row in the same animation block.

I believe I started to try that method but ran into issues. Wasn't aware of the scrollIndicatorInsets though, so maybe that was the issue.

Would that method have any significant advantages? Less code? Would it avoid all the crazy frame calculations depending on the rotation?

Yes, `-convertRect:fromView:` replaces your frame calculations based on rotation.
Ah yes. I see now that I totally missed the fact that the keyboard rect also needed to be converted. Well that was a painful point to miss!

Cool. I'll give that a whirl. Those rotation bits were a biatch. Thanks for the pointers.

Hopefully this is useful to someone. Obviously this could also just be a little display helper class (composition > inheritance) or even a category, but this was my original implementation. Might update it in the future. All the crazy coordinate stuff was the painful part.
Isn't the same functionality already part of UITableViewController?
Yes, if the tableView is your main view. Otherwise you are left rolling your own solution. In the case of the iPad, the tableView is rarely my main view for my controller, so I needed a little more flexible solution.
Why don't you have simply two UITableViewControllers + a Split View Controller in your example? Because of the divider of the Split View?
Per iOS standard design, only one ViewController is supposed to manage a full screen (iPhone) or region of a screen (iPad eg. split view controller). That's because you want to keep the view hierarchy pretty straightforward, instead of taking the view portions of other view controllers and inserting them as subviews of another VC. (Makes you feel dirty). Suggest watching the WWDC 2011 video on Customer Container VC's for an explanation.

Also it would be quite a bit of overkill and extra communication and code to back every table with a full blown view controller.

Ah, and after re-reading your question. Yes this was a design decision, but I've had to use this same bit within a detail controller inside a split view when I've had more than one table view.

The reasoning here was that search results aren't going to come up that often, so we wanted the results out of the way most of the time to make a cleaner interface.

Ironically, the screencast doesn't appear to be working on iPad.
Hmm.. It should have. I'll check it out.
Should be working now, although they don't seem to do the typical fullscreen thing. They should be able to with the file type and specs, but oh well.