2 // IASKAppSettingsViewController.m
3 // http://www.inappsettingskit.com
5 // Copyright (c) 2009-2010:
6 // Luc Vandal, Edovia Inc., http://www.edovia.com
7 // Ortwin Gentz, FutureTap GmbH, http://www.futuretap.com
8 // All rights reserved.
10 // It is appreciated but not required that you give credit to Luc Vandal and Ortwin Gentz,
11 // as the original authors of this code. You can give credit in a blog post, a tweet or on
12 // a info page of your app. Also, the original authors appreciate letting them know if you use this code.
14 // This code is licensed under the BSD license that is available at: http://www.opensource.org/licenses/bsd-license.php
18 #import "IASKAppSettingsViewController.h"
19 #import "IASKSettingsReader.h"
20 #import "IASKPSToggleSwitchSpecifierViewCell.h"
21 #import "IASKPSSliderSpecifierViewCell.h"
22 #import "IASKPSTextFieldSpecifierViewCell.h"
23 #import "IASKPSTitleValueSpecifierViewCell.h"
24 #import "IASKSwitch.h"
25 #import "IASKSlider.h"
26 #import "IASKSpecifier.h"
27 #import "IASKSpecifierValuesViewController.h"
28 #import "IASKTextField.h"
30 static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
31 static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
32 static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
34 static NSString *kIASKCredits = @"Powered by InAppSettingsKit"; // Leave this as-is!!!
36 #define kIASKSpecifierValuesViewControllerIndex 0
37 #define kIASKSpecifierChildViewControllerIndex 1
39 #define kIASKCreditsViewWidth 285
41 @interface IASKAppSettingsViewController ()
42 - (void)_textChanged:(id)sender;
43 - (void)_keyboardWillShow:(NSNotification*)notification;
44 - (void)_keyboardWillHide:(NSNotification*)notification;
47 @implementation IASKAppSettingsViewController
49 @synthesize delegate = _delegate;
50 @synthesize currentIndexPath=_currentIndexPath;
51 @synthesize settingsReader = _settingsReader;
52 @synthesize file = _file;
53 @synthesize currentFirstResponder = _currentFirstResponder;
54 @synthesize showCreditsFooter = _showCreditsFooter;
55 @synthesize showDoneButton = _showDoneButton;
57 #pragma mark accessors
58 - (IASKSettingsReader*)settingsReader {
59 if (!_settingsReader) {
60 _settingsReader = [[IASKSettingsReader alloc] initWithFile:self.file];
62 return _settingsReader;
69 return [[_file retain] autorelease];
72 - (void)setFile:(NSString *)file {
78 self.settingsReader = nil; // automatically initializes itself
81 #pragma mark standard view controller methods
82 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
83 if ([super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
84 // If set to YES, will display credits for InAppSettingsKit creators
85 _showCreditsFooter = YES;
87 // If set to YES, will add a DONE button at the right of the navigation bar
93 - (void)awakeFromNib {
94 // If set to YES, will display credits for InAppSettingsKit creators
95 _showCreditsFooter = YES;
97 // If set to YES, will add a DONE button at the right of the navigation bar
98 // if loaded via NIB, it's likely we sit in a TabBar- or NavigationController
99 // and thus don't need the Done button
100 _showDoneButton = NO;
103 - (void)viewDidLoad {
107 _viewList = [[NSMutableArray alloc] init];
108 [_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKSpecifierValuesView", @"ViewName",nil]];
109 [_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKAppSettingsView", @"ViewName",nil]];
113 - (void)viewDidUnload {
114 // Release any retained subviews of the main view.
115 // e.g. self.myOutlet = nil;
118 - (void)viewWillAppear:(BOOL)animated {
120 [_tableView reloadData];
121 _tableView.frame = self.view.bounds;
124 self.navigationItem.rightBarButtonItem = nil;
125 self.navigationController.delegate = nil;
126 if ([self.file isEqualToString:@"Root"]) {
127 self.navigationController.delegate = self;
128 if (_showDoneButton) {
129 UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
131 action:@selector(dismiss:)];
132 self.navigationItem.rightBarButtonItem = buttonItem;
133 [buttonItem release];
136 self.title = NSLocalizedString(@"Settings", @"");
139 [super viewWillAppear:animated];
142 - (void)viewDidAppear:(BOOL)animated {
143 [_tableView flashScrollIndicators];
144 // _tableView.frame = self.view.bounds;
145 [super viewDidAppear:animated];
147 [[NSNotificationCenter defaultCenter] addObserver:self
148 selector:@selector(_keyboardWillShow:)
149 name:UIKeyboardWillShowNotification
151 [[NSNotificationCenter defaultCenter] addObserver:self
152 selector:@selector(_keyboardWillHide:)
153 name:UIKeyboardWillHideNotification
157 - (void)viewWillDisappear:(BOOL)animated {
158 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
159 if ([self.currentFirstResponder canResignFirstResponder]) {
160 [self.currentFirstResponder resignFirstResponder];
162 [super viewWillDisappear:animated];
165 - (void)viewDidDisappear:(BOOL)animated {
166 [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
167 [super viewDidDisappear:animated];
170 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
171 return (interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
174 - (void)didReceiveMemoryWarning {
175 // Releases the view if it doesn't have a superview.
176 [super didReceiveMemoryWarning];
178 // Release any cached data, images, etc that aren't in use.
181 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
182 if (![viewController isKindOfClass:[IASKAppSettingsViewController class]] && ![viewController isKindOfClass:[IASKSpecifierValuesViewController class]]) {
188 [[NSNotificationCenter defaultCenter] removeObserver:self];
190 [_currentIndexPath release];
194 [_currentFirstResponder release];
195 _currentFirstResponder = nil;
197 self.settingsReader = nil;
207 - (IBAction)dismiss:(id)sender {
208 if ([self.currentFirstResponder canResignFirstResponder]) {
209 [self.currentFirstResponder resignFirstResponder];
212 self.navigationController.delegate = nil;
214 if (self.delegate && [self.delegate conformsToProtocol:@protocol(IASKSettingsDelegate)]) {
215 [self.delegate settingsViewControllerDidEnd:self];
219 - (void)toggledValue:(id)sender {
220 IASKSwitch *toggle = (IASKSwitch*)sender;
221 IASKSpecifier *spec = [_settingsReader specifierForKey:[toggle key]];
224 if ([spec trueValue] != nil) {
225 [[NSUserDefaults standardUserDefaults] setObject:[spec trueValue] forKey:[toggle key]];
228 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:[toggle key]];
232 if ([spec falseValue] != nil) {
233 [[NSUserDefaults standardUserDefaults] setObject:[spec falseValue] forKey:[toggle key]];
236 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:[toggle key]];
239 [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged object:[toggle key]];
242 - (void)sliderChangedValue:(id)sender {
243 IASKSlider *slider = (IASKSlider*)sender;
244 [[NSUserDefaults standardUserDefaults] setFloat:[slider value] forKey:[slider key]];
245 [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged object:[slider key]];
250 #pragma mark UITableView Functions
252 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
253 return [self.settingsReader numberOfSections];
256 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
257 return [self.settingsReader numberOfRowsForSection:section];
260 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
261 return [self.settingsReader titleForSection:section];
264 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
265 if (!_showCreditsFooter || section != [self.settingsReader numberOfSections]-1) return nil;
267 // Show the credits only in the last section's footer
268 UILabel *credits = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, kIASKCreditsViewWidth, 0)] autorelease];
269 [credits setOpaque:NO];
270 [credits setNumberOfLines:0];
271 [credits setFont:[UIFont systemFontOfSize:14.0f]];
272 [credits setTextAlignment:UITextAlignmentRight];
273 [credits setTextColor:[UIColor colorWithRed:77.0f/255.0f green:87.0f/255.0f blue:107.0f/255.0f alpha:1.0f]];
274 [credits setShadowColor:[UIColor whiteColor]];
275 [credits setShadowOffset:CGSizeMake(0, 1)];
276 [credits setBackgroundColor:[UIColor clearColor]];
277 [credits setText:kIASKCredits];
280 UIView* view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, kIASKCreditsViewWidth, credits.frame.size.height + 6 + 11)] autorelease];
281 [view setBackgroundColor:[UIColor clearColor]];
283 CGRect frame = credits.frame;
286 frame.size.width = kIASKCreditsViewWidth;
287 credits.frame = frame;
289 [view addSubview:credits];
295 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
296 if (!_showCreditsFooter || section != [self.settingsReader numberOfSections]-1) return 0.0f;
298 UIView* view = [self tableView:tableView viewForFooterInSection:section];
300 return view.frame.size.height;
305 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
306 IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
307 NSString *key = [specifier key];
309 if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
310 IASKPSToggleSwitchSpecifierViewCell *cell = (IASKPSToggleSwitchSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
313 cell = (IASKPSToggleSwitchSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSToggleSwitchSpecifierViewCell"
315 options:nil] objectAtIndex:0];
317 [[cell label] setText:[specifier title]];
319 id currentValue = [[NSUserDefaults standardUserDefaults] objectForKey:key];
322 if ([currentValue isEqual:[specifier trueValue]]) {
324 } else if ([currentValue isEqual:[specifier falseValue]]) {
327 toggleState = [currentValue boolValue];
330 toggleState = [specifier defaultBoolValue];
332 [[cell toggle] setOn:toggleState];
334 [[cell toggle] addTarget:self action:@selector(toggledValue:) forControlEvents:UIControlEventValueChanged];
335 [[cell toggle] setKey:key];
338 else if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
339 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
342 cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
343 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
345 [[cell textLabel] setText:[specifier title]];
346 [[cell detailTextLabel] setText:[[specifier titleForCurrentValue:[[NSUserDefaults standardUserDefaults] objectForKey:key] != nil ?
347 [[NSUserDefaults standardUserDefaults] objectForKey:key] : [specifier defaultValue]] description]];
350 else if ([[specifier type] isEqualToString:kIASKPSTitleValueSpecifier]) {
351 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
354 cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
355 cell.accessoryType = UITableViewCellAccessoryNone;
358 cell.textLabel.text = [specifier title];
359 id value = [[NSUserDefaults standardUserDefaults] objectForKey:key] ? : [specifier defaultValue];
361 NSString *stringValue;
362 if ([specifier multipleValues] || [specifier multipleTitles]) {
363 stringValue = [specifier titleForCurrentValue:value];
365 stringValue = [value description];
368 cell.detailTextLabel.text = stringValue;
369 [cell setUserInteractionEnabled:NO];
373 else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
374 IASKPSTextFieldSpecifierViewCell *cell = (IASKPSTextFieldSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
377 cell = (IASKPSTextFieldSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSTextFieldSpecifierViewCell"
379 options:nil] objectAtIndex:0];
380 cell.textField.textAlignment = UITextAlignmentLeft;
381 cell.textField.returnKeyType = UIReturnKeyDone;
382 cell.accessoryType = UITableViewCellAccessoryNone;
384 [[cell label] setText:[specifier title]];
385 [[cell textField] setText:[[NSUserDefaults standardUserDefaults] objectForKey:key] != nil ?
386 [[NSUserDefaults standardUserDefaults] objectForKey:key] : [specifier defaultStringValue]];
387 [[cell textField] setKey:key];
388 [[cell textField] setDelegate:self];
389 [[cell textField] addTarget:self action:@selector(_textChanged:) forControlEvents:UIControlEventEditingChanged];
390 [[cell textField] setSecureTextEntry:[specifier isSecure]];
391 [[cell textField] setKeyboardType:[specifier keyboardType]];
392 [[cell textField] setAutocapitalizationType:[specifier autocapitalizationType]];
393 [[cell textField] setAutocorrectionType:[specifier autoCorrectionType]];
394 [cell setNeedsLayout];
397 else if ([[specifier type] isEqualToString:kIASKPSSliderSpecifier]) {
398 IASKPSSliderSpecifierViewCell *cell = (IASKPSSliderSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
401 cell = (IASKPSSliderSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSSliderSpecifierViewCell"
403 options:nil] objectAtIndex:0];
406 if ([[specifier minimumValueImage] length] > 0) {
407 [[cell minImage] setImage:[UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:[specifier minimumValueImage]]]];
410 if ([[specifier maximumValueImage] length] > 0) {
411 [[cell maxImage] setImage:[UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:[specifier maximumValueImage]]]];
414 [[cell slider] setMinimumValue:[specifier minimumValue]];
415 [[cell slider] setMaximumValue:[specifier maximumValue]];
416 [[cell slider] setValue:[[NSUserDefaults standardUserDefaults] objectForKey:key] != nil ?
417 [[[NSUserDefaults standardUserDefaults] objectForKey:key] floatValue] : [[specifier defaultValue] floatValue]];
418 [[cell slider] addTarget:self action:@selector(sliderChangedValue:) forControlEvents:UIControlEventValueChanged];
419 [[cell slider] setKey:key];
420 [cell setNeedsLayout];
423 else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
424 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
427 cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
428 [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
431 [[cell textLabel] setText:[specifier title]];
433 } else if ([[specifier type] isEqualToString:kIASKOpenURLSpecifier]) {
434 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
437 cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
438 [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
441 cell.textLabel.text = [specifier title];
442 cell.detailTextLabel.text = [[specifier defaultValue] description];
445 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
448 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[specifier type]] autorelease];
450 [[cell textLabel] setText:[specifier title]];
455 - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
456 IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
458 if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
465 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
466 IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
468 if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
469 [tableView deselectRowAtIndexPath:indexPath animated:NO];
471 else if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
472 IASKSpecifierValuesViewController *targetViewController = [[_viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
474 if (targetViewController == nil) {
475 // the view controller has not been created yet, create it and set it to our viewList array
476 // create a new dictionary with the new view controller
477 NSMutableDictionary *newItemDict = [NSMutableDictionary dictionaryWithCapacity:3];
478 [newItemDict addEntriesFromDictionary: [_viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex]]; // copy the title and explain strings
480 targetViewController = [[IASKSpecifierValuesViewController alloc] initWithNibName:@"IASKSpecifierValuesView" bundle:nil];
482 // add the new view controller to the dictionary and then to the 'viewList' array
483 [newItemDict setObject:targetViewController forKey:@"viewController"];
484 [_viewList replaceObjectAtIndex:kIASKSpecifierValuesViewControllerIndex withObject:newItemDict];
485 [targetViewController release];
487 // load the view controll back in to push it
488 targetViewController = [[_viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
490 _currentIndexPath = indexPath;
491 [targetViewController setCurrentSpecifier:specifier];
492 targetViewController.settingsReader = self.settingsReader;
493 [[self navigationController] pushViewController:targetViewController animated:YES];
495 else if ([[specifier type] isEqualToString:kIASKPSSliderSpecifier]) {
496 [tableView deselectRowAtIndexPath:indexPath animated:NO];
498 else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
499 IASKPSTextFieldSpecifierViewCell *textFieldCell = (id)[tableView cellForRowAtIndexPath:indexPath];
500 [textFieldCell.textField becomeFirstResponder];
502 else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
503 IASKAppSettingsViewController *targetViewController = [[_viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
505 if (targetViewController == nil) {
506 // the view controller has not been created yet, create it and set it to our viewList array
507 // create a new dictionary with the new view controller
508 NSMutableDictionary *newItemDict = [NSMutableDictionary dictionaryWithCapacity:3];
509 [newItemDict addEntriesFromDictionary: [_viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex]]; // copy the title and explain strings
511 targetViewController = [[[self class] alloc] initWithNibName:@"IASKAppSettingsView" bundle:nil];
513 // add the new view controller to the dictionary and then to the 'viewList' array
514 [newItemDict setObject:targetViewController forKey:@"viewController"];
515 [_viewList replaceObjectAtIndex:kIASKSpecifierChildViewControllerIndex withObject:newItemDict];
516 [targetViewController release];
518 // load the view controll back in to push it
519 targetViewController = [[_viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
521 _currentIndexPath = indexPath;
522 targetViewController.file = specifier.file;
523 targetViewController.title = specifier.title;
524 targetViewController.showCreditsFooter = NO;
525 [[self navigationController] pushViewController:targetViewController animated:YES];
526 } else if ([[specifier type] isEqualToString:kIASKOpenURLSpecifier]) {
527 [tableView deselectRowAtIndexPath:indexPath animated:YES];
528 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:specifier.file]];
530 [tableView deselectRowAtIndexPath:indexPath animated:NO];
536 #pragma mark UITextFieldDelegate Functions
538 - (void)_textChanged:(id)sender {
539 IASKTextField *text = (IASKTextField*)sender;
540 [[NSUserDefaults standardUserDefaults] setObject:[text text] forKey:[text key]];
541 [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged object:[text key]];
544 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
545 [textField setTextAlignment:UITextAlignmentLeft];
546 self.currentFirstResponder = textField;
550 - (void)textFieldDidBeginEditing:(UITextField *)textField {
551 if ([_tableView indexPathsForVisibleRows].count) {
552 _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[_tableView indexPathsForVisibleRows] objectAtIndex:0];
554 // this should never happen
555 _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
556 [textField resignFirstResponder];
560 - (void)textFieldDidEndEditing:(UITextField *)textField {
561 self.currentFirstResponder = nil;
564 - (BOOL)textFieldShouldReturn:(UITextField *)textField{
565 [textField resignFirstResponder];
569 #pragma mark Keyboard Management
570 - (void)_keyboardWillShow:(NSNotification*)notification {
571 if (self.navigationController.topViewController == self) {
572 NSDictionary* userInfo = [notification userInfo];
574 // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0
575 NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
576 if (!keyboardFrameValue) {
577 keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"];
580 // Reduce the tableView height by the part of the keyboard that actually covers the tableView
581 CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds;
582 CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] keyWindow]];
583 CGRect frame = _tableView.frame;
584 frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute);
586 [UIView beginAnimations:nil context:NULL];
587 [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
588 [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
589 _tableView.frame = frame;
590 [UIView commitAnimations];
592 UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
593 NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];
595 // iOS 3 sends hide and show notifications right after each other
596 // when switching between textFields, so cancel -scrollToOldPosition requests
597 [NSObject cancelPreviousPerformRequestsWithTarget:self];
599 [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
603 - (void) scrollToOldPosition {
604 [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
607 - (void)_keyboardWillHide:(NSNotification*)notification {
608 if (self.navigationController.topViewController == self) {
609 NSDictionary* userInfo = [notification userInfo];
611 [UIView beginAnimations:nil context:NULL];
612 [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
613 [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];
614 _tableView.frame = self.view.bounds;
615 [UIView commitAnimations];
617 [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];