changed git call from https to git readonly
[atutor.git] / mods / atsocial_iphone_app / InAppSettingsKit / Controllers / IASKAppSettingsViewController.m
1 //
2 //  IASKAppSettingsViewController.m
3 //  http://www.inappsettingskit.com
4 //
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.
9 // 
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.
13 //
14 //  This code is licensed under the BSD license that is available at: http://www.opensource.org/licenses/bsd-license.php
15 //
16
17
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"
29
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;
33
34 static NSString *kIASKCredits = @"Powered by InAppSettingsKit"; // Leave this as-is!!!
35
36 #define kIASKSpecifierValuesViewControllerIndex       0
37 #define kIASKSpecifierChildViewControllerIndex        1
38
39 #define kIASKCreditsViewWidth                         285
40
41 @interface IASKAppSettingsViewController ()
42 - (void)_textChanged:(id)sender;
43 - (void)_keyboardWillShow:(NSNotification*)notification;
44 - (void)_keyboardWillHide:(NSNotification*)notification;
45 @end
46
47 @implementation IASKAppSettingsViewController
48
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;
56
57 #pragma mark accessors
58 - (IASKSettingsReader*)settingsReader {
59         if (!_settingsReader) {
60                 _settingsReader = [[IASKSettingsReader alloc] initWithFile:self.file];
61         }
62         return _settingsReader;
63 }
64
65 - (NSString*)file {
66         if (!_file) {
67                 return @"Root";
68         }
69         return [[_file retain] autorelease];
70 }
71
72 - (void)setFile:(NSString *)file {
73         if (file != _file) {
74                 [_file release];
75                 _file = [file copy];
76         }
77         
78         self.settingsReader = nil; // automatically initializes itself
79 }
80
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;
86         
87         // If set to YES, will add a DONE button at the right of the navigation bar
88         _showDoneButton = NO;
89     }
90     return self;
91 }
92
93 - (void)awakeFromNib {
94         // If set to YES, will display credits for InAppSettingsKit creators
95         _showCreditsFooter = YES;
96         
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;
101 }
102
103 - (void)viewDidLoad {
104     [super viewDidLoad];
105     
106     // Add views
107     _viewList = [[NSMutableArray alloc] init];
108     [_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKSpecifierValuesView", @"ViewName",nil]];
109     [_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKAppSettingsView", @"ViewName",nil]];
110
111 }
112
113 - (void)viewDidUnload {
114         // Release any retained subviews of the main view.
115         // e.g. self.myOutlet = nil;
116 }
117
118 - (void)viewWillAppear:(BOOL)animated {
119     if (_tableView) {
120         [_tableView reloadData];
121                 _tableView.frame = self.view.bounds;
122     }
123         
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 
130                                                                                         target:self 
131                                                                                         action:@selector(dismiss:)];
132             self.navigationItem.rightBarButtonItem = buttonItem;
133             [buttonItem release];
134                 } 
135                 if (!self.title) {
136                         self.title = NSLocalizedString(@"Settings", @"");
137                 }
138         }
139         [super viewWillAppear:animated];
140 }
141
142 - (void)viewDidAppear:(BOOL)animated {
143         [_tableView flashScrollIndicators];
144 //      _tableView.frame = self.view.bounds;
145         [super viewDidAppear:animated];
146
147         [[NSNotificationCenter defaultCenter] addObserver:self
148                                                                                          selector:@selector(_keyboardWillShow:)
149                                                                                                  name:UIKeyboardWillShowNotification
150                                                                                            object:nil];
151         [[NSNotificationCenter defaultCenter] addObserver:self
152                                                                                          selector:@selector(_keyboardWillHide:)
153                                                                                                  name:UIKeyboardWillHideNotification
154                                                                                            object:nil];         
155 }
156
157 - (void)viewWillDisappear:(BOOL)animated {
158         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
159         if ([self.currentFirstResponder canResignFirstResponder]) {
160                 [self.currentFirstResponder resignFirstResponder];
161         }
162         [super viewWillDisappear:animated];
163 }
164
165 - (void)viewDidDisappear:(BOOL)animated {
166         [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
167         [super viewDidDisappear:animated];
168 }
169
170 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
171     return (interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
172 }
173
174 - (void)didReceiveMemoryWarning {
175         // Releases the view if it doesn't have a superview.
176     [super didReceiveMemoryWarning];
177         
178         // Release any cached data, images, etc that aren't in use.
179 }
180
181 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
182         if (![viewController isKindOfClass:[IASKAppSettingsViewController class]] && ![viewController isKindOfClass:[IASKSpecifierValuesViewController class]]) {
183                 [self dismiss:nil];
184         }
185 }
186
187 - (void)dealloc {
188     [[NSNotificationCenter defaultCenter] removeObserver:self];
189     [_viewList release];
190     [_currentIndexPath release];
191         [_file release];
192         _file = nil;
193         
194         [_currentFirstResponder release];
195         _currentFirstResponder = nil;
196         
197     self.settingsReader = nil;
198         _delegate = nil;
199
200     [super dealloc];
201 }
202
203
204 #pragma mark -
205 #pragma mark Actions
206
207 - (IBAction)dismiss:(id)sender {
208         if ([self.currentFirstResponder canResignFirstResponder]) {
209                 [self.currentFirstResponder resignFirstResponder];
210         }
211         
212         self.navigationController.delegate = nil;
213         
214         if (self.delegate && [self.delegate conformsToProtocol:@protocol(IASKSettingsDelegate)]) {
215                 [self.delegate settingsViewControllerDidEnd:self];
216         }
217 }
218
219 - (void)toggledValue:(id)sender {
220     IASKSwitch *toggle    = (IASKSwitch*)sender;
221     IASKSpecifier *spec   = [_settingsReader specifierForKey:[toggle key]];
222     
223     if ([toggle isOn]) {
224         if ([spec trueValue] != nil) {
225             [[NSUserDefaults standardUserDefaults] setObject:[spec trueValue] forKey:[toggle key]];
226         }
227         else {
228             [[NSUserDefaults standardUserDefaults] setBool:YES forKey:[toggle key]]; 
229         }
230     }
231     else {
232         if ([spec falseValue] != nil) {
233             [[NSUserDefaults standardUserDefaults] setObject:[spec falseValue] forKey:[toggle key]];
234         }
235         else {
236             [[NSUserDefaults standardUserDefaults] setBool:NO forKey:[toggle key]]; 
237         }
238     }
239     [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged object:[toggle key]];
240 }
241
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]];
246 }
247
248
249 #pragma mark -
250 #pragma mark UITableView Functions
251
252 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
253         return [self.settingsReader numberOfSections];
254 }
255
256 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
257     return [self.settingsReader numberOfRowsForSection:section];
258 }
259
260 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
261     return [self.settingsReader titleForSection:section];
262 }
263
264 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
265     if (!_showCreditsFooter || section != [self.settingsReader numberOfSections]-1) return nil;
266     
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];
278     [credits sizeToFit];
279     
280     UIView* view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, kIASKCreditsViewWidth, credits.frame.size.height + 6 + 11)] autorelease];
281     [view setBackgroundColor:[UIColor clearColor]];
282     
283     CGRect frame = credits.frame;
284     frame.origin.y = 8;
285     frame.origin.x = 16;
286     frame.size.width = kIASKCreditsViewWidth;
287     credits.frame = frame;
288     
289     [view addSubview:credits];
290     [view sizeToFit];
291     
292     return view;
293 }
294
295 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
296     if (!_showCreditsFooter || section != [self.settingsReader numberOfSections]-1) return 0.0f;
297     
298     UIView* view = [self tableView:tableView viewForFooterInSection:section];
299     if (view != nil) {
300       return view.frame.size.height;
301     }
302     return -1;
303 }
304
305 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
306     IASKSpecifier *specifier  = [self.settingsReader specifierForIndexPath:indexPath];
307     NSString *key           = [specifier key];
308     
309     if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
310         IASKPSToggleSwitchSpecifierViewCell *cell = (IASKPSToggleSwitchSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
311         
312         if (!cell) {
313             cell = (IASKPSToggleSwitchSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSToggleSwitchSpecifierViewCell" 
314                                                                                                                                                                            owner:self 
315                                                                                                                                                                          options:nil] objectAtIndex:0];
316         }
317         [[cell label] setText:[specifier title]];
318
319                 id currentValue = [[NSUserDefaults standardUserDefaults] objectForKey:key];
320                 BOOL toggleState;
321                 if (currentValue) {
322                         if ([currentValue isEqual:[specifier trueValue]]) {
323                                 toggleState = YES;
324                         } else if ([currentValue isEqual:[specifier falseValue]]) {
325                                 toggleState = NO;
326                         } else {
327                                 toggleState = [currentValue boolValue];
328                         }
329                 } else {
330                         toggleState = [specifier defaultBoolValue];
331                 }
332                 [[cell toggle] setOn:toggleState];
333                 
334         [[cell toggle] addTarget:self action:@selector(toggledValue:) forControlEvents:UIControlEventValueChanged];
335         [[cell toggle] setKey:key];
336         return cell;
337     }
338     else if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
339         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
340         
341         if (!cell) {
342             cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
343                         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
344                 }
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]];
348         return cell;
349     }
350     else if ([[specifier type] isEqualToString:kIASKPSTitleValueSpecifier]) {
351         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
352         
353         if (!cell) {
354             cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
355                         cell.accessoryType = UITableViewCellAccessoryNone;
356         }
357                 
358                 cell.textLabel.text = [specifier title];
359                 id value = [[NSUserDefaults standardUserDefaults] objectForKey:key] ? : [specifier defaultValue];
360                 
361                 NSString *stringValue;
362                 if ([specifier multipleValues] || [specifier multipleTitles]) {
363                         stringValue = [specifier titleForCurrentValue:value];
364                 } else {
365                         stringValue = [value description];
366                 }
367
368                 cell.detailTextLabel.text = stringValue;
369                 [cell setUserInteractionEnabled:NO];
370                 
371         return cell;
372     }
373     else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
374         IASKPSTextFieldSpecifierViewCell *cell = (IASKPSTextFieldSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
375         
376         if (!cell) {
377             cell = (IASKPSTextFieldSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSTextFieldSpecifierViewCell" 
378                                                                                                                                                                         owner:self 
379                                                                                                                                                                   options:nil] objectAtIndex:0];
380                         cell.textField.textAlignment = UITextAlignmentLeft;
381                         cell.textField.returnKeyType = UIReturnKeyDone;
382                         cell.accessoryType = UITableViewCellAccessoryNone;
383         }
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];
395                 return cell;
396         }
397         else if ([[specifier type] isEqualToString:kIASKPSSliderSpecifier]) {
398         IASKPSSliderSpecifierViewCell *cell = (IASKPSSliderSpecifierViewCell*)[tableView dequeueReusableCellWithIdentifier:[specifier type]];
399         
400         if (!cell) {
401             cell = (IASKPSSliderSpecifierViewCell*) [[[NSBundle mainBundle] loadNibNamed:@"IASKPSSliderSpecifierViewCell" 
402                                                                                                                                                                  owner:self 
403                                                                                                                                                            options:nil] objectAtIndex:0];
404                 }
405         
406         if ([[specifier minimumValueImage] length] > 0) {
407             [[cell minImage] setImage:[UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:[specifier minimumValueImage]]]];
408         }
409                 
410         if ([[specifier maximumValueImage] length] > 0) {
411             [[cell maxImage] setImage:[UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:[specifier maximumValueImage]]]];
412         }
413         
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];
421         return cell;
422     }
423     else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
424         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
425         
426         if (!cell) {
427             cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
428                         [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
429         }
430
431         [[cell textLabel] setText:[specifier title]];
432         return cell;
433     } else if ([[specifier type] isEqualToString:kIASKOpenURLSpecifier]) {
434         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
435         
436         if (!cell) {
437             cell = [[[IASKPSTitleValueSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[specifier type]] autorelease];
438                         [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
439         }
440
441                 cell.textLabel.text = [specifier title];
442                 cell.detailTextLabel.text = [[specifier defaultValue] description];
443                 return cell;        
444         } else {
445         UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[specifier type]];
446                 
447         if (!cell) {
448             cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[specifier type]] autorelease];
449         }
450         [[cell textLabel] setText:[specifier title]];
451         return cell;
452     }
453 }
454
455 - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
456         IASKSpecifier *specifier  = [self.settingsReader specifierForIndexPath:indexPath];
457         
458         if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
459                 return nil;
460         } else {
461                 return indexPath;
462         }
463 }
464
465 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
466     IASKSpecifier *specifier  = [self.settingsReader specifierForIndexPath:indexPath];
467     
468     if ([[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]) {
469         [tableView deselectRowAtIndexPath:indexPath animated:NO];
470     }
471     else if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
472         IASKSpecifierValuesViewController *targetViewController = [[_viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
473                 
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
479             
480             targetViewController = [[IASKSpecifierValuesViewController alloc] initWithNibName:@"IASKSpecifierValuesView" bundle:nil];
481                         
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];
486             
487             // load the view controll back in to push it
488             targetViewController = [[_viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
489         }
490         _currentIndexPath = indexPath;
491         [targetViewController setCurrentSpecifier:specifier];
492         targetViewController.settingsReader = self.settingsReader;
493         [[self navigationController] pushViewController:targetViewController animated:YES];
494     }
495     else if ([[specifier type] isEqualToString:kIASKPSSliderSpecifier]) {
496         [tableView deselectRowAtIndexPath:indexPath animated:NO];
497     }
498     else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
499                 IASKPSTextFieldSpecifierViewCell *textFieldCell = (id)[tableView cellForRowAtIndexPath:indexPath];
500                 [textFieldCell.textField becomeFirstResponder];
501     }
502     else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
503         IASKAppSettingsViewController *targetViewController = [[_viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
504                 
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
510             
511             targetViewController = [[[self class] alloc] initWithNibName:@"IASKAppSettingsView" bundle:nil];
512                         
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];
517             
518             // load the view controll back in to push it
519             targetViewController = [[_viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
520         }
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]];    
529         } else {
530         [tableView deselectRowAtIndexPath:indexPath animated:NO];
531     }
532 }
533
534
535 #pragma mark -
536 #pragma mark UITextFieldDelegate Functions
537
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]];
542 }
543
544 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
545         [textField setTextAlignment:UITextAlignmentLeft];
546         self.currentFirstResponder = textField;
547     return YES;
548 }
549
550 - (void)textFieldDidBeginEditing:(UITextField *)textField {
551         if ([_tableView indexPathsForVisibleRows].count) {
552                 _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[_tableView indexPathsForVisibleRows] objectAtIndex:0];
553         } else {
554                 // this should never happen
555                 _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0];
556                 [textField resignFirstResponder];
557         }
558 }
559
560 - (void)textFieldDidEndEditing:(UITextField *)textField {
561         self.currentFirstResponder = nil;
562 }
563
564 - (BOOL)textFieldShouldReturn:(UITextField *)textField{
565     [textField resignFirstResponder];
566         return YES;
567 }
568
569 #pragma mark Keyboard Management
570 - (void)_keyboardWillShow:(NSNotification*)notification {
571         if (self.navigationController.topViewController == self) {
572                 NSDictionary* userInfo = [notification userInfo];
573
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"];
578                 }
579                 
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);
585
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];
591                 
592                 UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview;
593                 NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell];
594
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];
598                 
599                 [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
600         }
601 }
602
603 - (void) scrollToOldPosition {
604   [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES];
605 }
606
607 - (void)_keyboardWillHide:(NSNotification*)notification {
608         if (self.navigationController.topViewController == self) {
609                 NSDictionary* userInfo = [notification userInfo];
610                 
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];
616                 
617                 [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1];
618         }
619 }       
620 @end