PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Dependencies/InAppSettingsKit/Controllers/IASKAppSettingsViewController.m

https://gitlab.com/Mr.Tomato/VideoEffects
Objective C | 969 lines | 800 code | 125 blank | 44 comment | 203 complexity | 940e3c8722429ccd5f139117b55f9fd7 MD5 | raw file
  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. #import "IASKAppSettingsViewController.h"
  17. #import "IASKSettingsReader.h"
  18. #import "IASKSettingsStoreUserDefaults.h"
  19. #import "IASKPSSliderSpecifierViewCell.h"
  20. #import "IASKPSTextFieldSpecifierViewCell.h"
  21. #import "IASKSwitch.h"
  22. #import "IASKSlider.h"
  23. #import "IASKSpecifier.h"
  24. #import "IASKSpecifierValuesViewController.h"
  25. #import "IASKTextField.h"
  26. #import "IASKMultipleValueSelection.h"
  27. #if !__has_feature(objc_arc)
  28. #error "IASK needs ARC"
  29. #endif
  30. static NSString *kIASKCredits = @"Powered by Johnny Xu"; // Leave this as-is!!!
  31. #define kIASKSpecifierValuesViewControllerIndex 0
  32. #define kIASKSpecifierChildViewControllerIndex 1
  33. #define kIASKCreditsViewWidth 285
  34. CGRect IASKCGRectSwap(CGRect rect);
  35. @interface IASKAppSettingsViewController () {
  36. IASKSettingsReader *_settingsReader;
  37. id<IASKSettingsStore> _settingsStore;
  38. id _currentFirstResponder;
  39. __weak UIViewController *_currentChildViewController;
  40. BOOL _reloadDisabled;
  41. /// The selected index for every group (in case it's a radio group).
  42. NSArray *_selections;
  43. }
  44. @property (nonatomic, strong) id currentFirstResponder;
  45. - (void)_textChanged:(id)sender;
  46. - (void)synchronizeSettings;
  47. - (void)userDefaultsDidChange;
  48. - (void)reload;
  49. @end
  50. @implementation IASKAppSettingsViewController
  51. //synthesize properties from protocol
  52. @synthesize settingsReader = _settingsReader;
  53. @synthesize settingsStore = _settingsStore;
  54. @synthesize file = _file;
  55. #pragma mark accessors
  56. - (IASKSettingsReader*)settingsReader {
  57. if (!_settingsReader) {
  58. _settingsReader = [[IASKSettingsReader alloc] initWithFile:self.file];
  59. if (self.neverShowPrivacySettings) {
  60. _settingsReader.showPrivacySettings = NO;
  61. }
  62. }
  63. return _settingsReader;
  64. }
  65. - (id<IASKSettingsStore>)settingsStore {
  66. if (!_settingsStore) {
  67. _settingsStore = [[IASKSettingsStoreUserDefaults alloc] init];
  68. }
  69. return _settingsStore;
  70. }
  71. - (NSString*)file {
  72. if (!_file) {
  73. self.file = @"Root";
  74. }
  75. return _file;
  76. }
  77. - (void)setFile:(NSString *)file {
  78. _file = [file copy];
  79. self.tableView.contentOffset = CGPointMake(0, -self.tableView.contentInset.top);
  80. self.settingsReader = nil; // automatically initializes itself
  81. if (!_reloadDisabled) {
  82. [self.tableView reloadData];
  83. [self createSelections];
  84. }
  85. }
  86. - (void)createSelections {
  87. NSMutableArray *sectionSelection = [NSMutableArray new];
  88. for (int i = 0; i < _settingsReader.numberOfSections; i++) {
  89. IASKSpecifier *specifier = [self.settingsReader headerSpecifierForSection:i];
  90. if ([specifier.type isEqualToString:kIASKPSRadioGroupSpecifier]) {
  91. IASKMultipleValueSelection *selection = [IASKMultipleValueSelection new];
  92. selection.tableView = self.tableView;
  93. selection.specifier = specifier;
  94. selection.section = i;
  95. selection.settingsStore = self.settingsStore;
  96. [sectionSelection addObject:selection];
  97. } else {
  98. [sectionSelection addObject:[NSNull null]];
  99. }
  100. }
  101. _selections = sectionSelection;
  102. }
  103. - (BOOL)isPad {
  104. BOOL isPad = NO;
  105. #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 30200)
  106. isPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
  107. #endif
  108. return isPad;
  109. }
  110. #pragma mark standard view controller methods
  111. - (id)init {
  112. return [self initWithStyle:UITableViewStyleGrouped];
  113. }
  114. - (id)initWithStyle:(UITableViewStyle)style {
  115. if (style != UITableViewStyleGrouped) {
  116. NSLog(@"WARNING: only UITableViewStyleGrouped style is supported by InAppSettingsKit.");
  117. }
  118. if ((self = [super initWithStyle:style])) {
  119. [self configure];
  120. }
  121. return self;
  122. }
  123. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
  124. if (nibNameOrNil) {
  125. NSLog (@"%@ is now deprecated, we are moving away from nibs.", NSStringFromSelector(_cmd));
  126. self = [super initWithStyle:UITableViewStyleGrouped];
  127. } else {
  128. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  129. }
  130. if (self) {
  131. [self configure];
  132. }
  133. return self;
  134. }
  135. - (id)initWithCoder:(NSCoder *)aDecoder {
  136. if ((self = [super initWithCoder:aDecoder])) {
  137. [self configure];
  138. _showDoneButton = NO;
  139. }
  140. return self;
  141. }
  142. - (void)configure {
  143. _reloadDisabled = NO;
  144. _showDoneButton = YES;
  145. _showCreditsFooter = YES; // display credits for InAppSettingsKit creators
  146. }
  147. - (void)viewDidLoad {
  148. [super viewDidLoad];
  149. if ([self isPad]) {
  150. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
  151. if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) // don't use etched style on iOS 7
  152. #endif
  153. self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLineEtched;
  154. }
  155. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapToEndEdit:)];
  156. tapGesture.cancelsTouchesInView = NO;
  157. [self.tableView addGestureRecognizer:tapGesture];
  158. }
  159. - (void)viewWillAppear:(BOOL)animated
  160. {
  161. // if there's something selected, the value might have changed
  162. // so reload that row
  163. NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
  164. if(selectedIndexPath)
  165. {
  166. [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath]
  167. withRowAnimation:UITableViewRowAnimationNone];
  168. // and reselect it, so we get the nice default deselect animation from UITableViewController
  169. [self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
  170. }
  171. if (_showDoneButton)
  172. {
  173. // Change by Johnny Xu, 2015.6.16
  174. NSString *fontName = GBLocalizedString(@"FontName");
  175. CGFloat fontSize = 18;
  176. // UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithTitle:GBLocalizedString(@"Done") style:UIBarButtonItemStylePlain target:self action:@selector(dismiss:)];
  177. // [rightItem setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize]} forState:UIControlStateNormal];
  178. // self.navigationItem.rightBarButtonItem = rightItem;
  179. UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithTitle:GBLocalizedString(@"Back") style:UIBarButtonItemStylePlain target:self action:@selector(dismiss:)];
  180. [leftItem setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont fontWithName:fontName size:fontSize]} forState:UIControlStateNormal];
  181. self.navigationItem.leftBarButtonItem = leftItem;
  182. // UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
  183. // target:self
  184. // action:@selector(dismiss:)];
  185. // self.navigationItem.rightBarButtonItem = buttonItem;
  186. }
  187. if (!self.title)
  188. {
  189. self.title = GBLocalizedString(@"Settings");
  190. }
  191. if ([self.settingsStore isKindOfClass:[IASKSettingsStoreUserDefaults class]])
  192. {
  193. NSNotificationCenter *dc = NSNotificationCenter.defaultCenter;
  194. IASKSettingsStoreUserDefaults *udSettingsStore = (id)self.settingsStore;
  195. [dc addObserver:self selector:@selector(userDefaultsDidChange) name:NSUserDefaultsDidChangeNotification object:udSettingsStore.defaults];
  196. [dc addObserver:self selector:@selector(didChangeSettingViaIASK:) name:kIASKAppSettingChanged object:nil];
  197. [self userDefaultsDidChange]; // force update in case of changes while we were hidden
  198. }
  199. [super viewWillAppear:animated];
  200. }
  201. - (CGSize)contentSizeForViewInPopover
  202. {
  203. return [[self view] sizeThatFits:CGSizeMake(320, 2000)];
  204. }
  205. - (void)viewDidAppear:(BOOL)animated {
  206. [super viewDidAppear:animated];
  207. NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
  208. [dc addObserver:self selector:@selector(synchronizeSettings) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
  209. [dc addObserver:self selector:@selector(reload) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
  210. [dc addObserver:self selector:@selector(synchronizeSettings) name:UIApplicationWillTerminateNotification object:[UIApplication sharedApplication]];
  211. [self.tableView beginUpdates];
  212. [self.tableView endUpdates];
  213. }
  214. - (void)viewWillDisappear:(BOOL)animated {
  215. [NSObject cancelPreviousPerformRequestsWithTarget:self];
  216. // hide the keyboard
  217. [self.currentFirstResponder resignFirstResponder];
  218. [super viewWillDisappear:animated];
  219. }
  220. - (void)viewDidDisappear:(BOOL)animated {
  221. NSNotificationCenter *dc = [NSNotificationCenter defaultCenter];
  222. if ([self.settingsStore isKindOfClass:[IASKSettingsStoreUserDefaults class]]) {
  223. IASKSettingsStoreUserDefaults *udSettingsStore = (id)self.settingsStore;
  224. [dc removeObserver:self name:NSUserDefaultsDidChangeNotification object:udSettingsStore.defaults];
  225. [dc removeObserver:self name:kIASKAppSettingChanged object:self];
  226. }
  227. [dc removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
  228. [dc removeObserver:self name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
  229. [dc removeObserver:self name:UIApplicationWillTerminateNotification object:[UIApplication sharedApplication]];
  230. [super viewDidDisappear:animated];
  231. }
  232. - (void)setHiddenKeys:(NSSet *)theHiddenKeys {
  233. [self setHiddenKeys:theHiddenKeys animated:NO];
  234. }
  235. - (void)setHiddenKeys:(NSSet*)theHiddenKeys animated:(BOOL)animated {
  236. if (_hiddenKeys != theHiddenKeys) {
  237. NSSet *oldHiddenKeys = _hiddenKeys;
  238. _hiddenKeys = theHiddenKeys;
  239. if (animated) {
  240. NSMutableSet *showKeys = [NSMutableSet setWithSet:oldHiddenKeys];
  241. [showKeys minusSet:theHiddenKeys];
  242. NSMutableSet *hideKeys = [NSMutableSet setWithSet:theHiddenKeys];
  243. [hideKeys minusSet:oldHiddenKeys];
  244. // calculate rows to be deleted
  245. NSMutableArray *hideIndexPaths = [NSMutableArray array];
  246. for (NSString *key in hideKeys) {
  247. NSIndexPath *indexPath = [self.settingsReader indexPathForKey:key];
  248. if (indexPath) {
  249. [hideIndexPaths addObject:indexPath];
  250. }
  251. }
  252. // calculate sections to be deleted
  253. NSMutableIndexSet *hideSections = [NSMutableIndexSet indexSet];
  254. for (NSInteger section = 0; section < [self numberOfSectionsInTableView:self.tableView ]; section++) {
  255. NSInteger rowsInSection = 0;
  256. for (NSIndexPath *indexPath in hideIndexPaths) {
  257. if (indexPath.section == section) {
  258. rowsInSection++;
  259. }
  260. }
  261. if (rowsInSection && rowsInSection >= [self.settingsReader numberOfRowsForSection:section]) {
  262. [hideSections addIndex:section];
  263. }
  264. }
  265. // set the datasource
  266. self.settingsReader.hiddenKeys = theHiddenKeys;
  267. // calculate rows to be inserted
  268. NSMutableArray *showIndexPaths = [NSMutableArray array];
  269. for (NSString *key in showKeys) {
  270. NSIndexPath *indexPath = [self.settingsReader indexPathForKey:key];
  271. if (indexPath) {
  272. [showIndexPaths addObject:indexPath];
  273. }
  274. }
  275. // calculate sections to be inserted
  276. NSMutableIndexSet *showSections = [NSMutableIndexSet indexSet];
  277. for (NSInteger section = 0; section < [self.settingsReader numberOfSections]; section++) {
  278. NSInteger rowsInSection = 0;
  279. for (NSIndexPath *indexPath in showIndexPaths) {
  280. if (indexPath.section == section) {
  281. rowsInSection++;
  282. }
  283. }
  284. if (rowsInSection && rowsInSection >= [self.settingsReader numberOfRowsForSection:section]) {
  285. [showSections addIndex:section];
  286. }
  287. }
  288. if (hideSections.count || hideIndexPaths.count || showSections.count || showIndexPaths.count) {
  289. [self.tableView beginUpdates];
  290. UITableViewRowAnimation animation = animated ? UITableViewRowAnimationAutomatic : UITableViewRowAnimationNone;
  291. if (hideSections.count) {
  292. [self.tableView deleteSections:hideSections withRowAnimation:animation];
  293. }
  294. if (hideIndexPaths) {
  295. [self.tableView deleteRowsAtIndexPaths:hideIndexPaths withRowAnimation:animation];
  296. }
  297. if (showSections.count) {
  298. [self.tableView insertSections:showSections withRowAnimation:animation];
  299. }
  300. if (showIndexPaths) {
  301. [self.tableView insertRowsAtIndexPaths:showIndexPaths withRowAnimation:animation];
  302. }
  303. [self.tableView endUpdates];
  304. }
  305. } else {
  306. self.settingsReader.hiddenKeys = theHiddenKeys;
  307. if (!_reloadDisabled) [self.tableView reloadData];
  308. }
  309. }
  310. UIViewController *childViewController = _currentChildViewController;
  311. if([childViewController respondsToSelector:@selector(setHiddenKeys:animated:)]) {
  312. [(id)childViewController setHiddenKeys:theHiddenKeys animated:animated];
  313. }
  314. }
  315. - (void)dealloc
  316. {
  317. [[NSNotificationCenter defaultCenter] removeObserver:self];
  318. }
  319. #pragma mark -
  320. #pragma mark Actions
  321. - (void)dismissWithNotSave:(id)sender
  322. {
  323. if (self.delegate && [self.delegate conformsToProtocol:@protocol(IASKSettingsDelegate)])
  324. {
  325. [self.delegate settingsViewControllerDidEnd:self];
  326. }
  327. }
  328. - (void)dismiss:(id)sender
  329. {
  330. [self.settingsStore synchronize];
  331. if (self.delegate && [self.delegate conformsToProtocol:@protocol(IASKSettingsDelegate)])
  332. {
  333. [self.delegate settingsViewControllerDidEnd:self];
  334. }
  335. }
  336. - (void)toggledValue:(id)sender
  337. {
  338. IASKSwitch *toggle = (IASKSwitch*)sender;
  339. IASKSpecifier *spec = [_settingsReader specifierForKey:[toggle key]];
  340. if ([toggle isOn])
  341. {
  342. if ([spec trueValue] != nil)
  343. {
  344. [self.settingsStore setObject:[spec trueValue] forKey:[toggle key]];
  345. }
  346. else
  347. {
  348. [self.settingsStore setBool:YES forKey:[toggle key]];
  349. }
  350. }
  351. else
  352. {
  353. if ([spec falseValue] != nil)
  354. {
  355. [self.settingsStore setObject:[spec falseValue] forKey:[toggle key]];
  356. }
  357. else
  358. {
  359. [self.settingsStore setBool:NO forKey:[toggle key]];
  360. }
  361. }
  362. [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged
  363. object:[toggle key]
  364. userInfo:[NSDictionary dictionaryWithObject:[self.settingsStore objectForKey:[toggle key]]
  365. forKey:[toggle key]]];
  366. }
  367. - (void)sliderChangedValue:(id)sender {
  368. IASKSlider *slider = (IASKSlider*)sender;
  369. [self.settingsStore setFloat:[slider value] forKey:[slider key]];
  370. [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged
  371. object:[slider key]
  372. userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:[slider value]]
  373. forKey:[slider key]]];
  374. }
  375. #pragma mark -
  376. #pragma mark UITableView Functions
  377. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  378. return [self.settingsReader numberOfSections];
  379. }
  380. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  381. return [self.settingsReader numberOfRowsForSection:section];
  382. }
  383. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  384. IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
  385. if ([[specifier type] isEqualToString:kIASKCustomViewSpecifier]) {
  386. if ([self.delegate respondsToSelector:@selector(tableView:heightForSpecifier:)]) {
  387. return [self.delegate tableView:tableView heightForSpecifier:specifier];
  388. } else {
  389. return 0;
  390. }
  391. }
  392. IASK_IF_IOS7_OR_GREATER
  393. (
  394. NSDictionary *rowHeights = @{UIContentSizeCategoryExtraSmall: @(44),
  395. UIContentSizeCategorySmall: @(44),
  396. UIContentSizeCategoryMedium: @(44),
  397. UIContentSizeCategoryLarge: @(44),
  398. UIContentSizeCategoryExtraLarge: @(47)};
  399. return (CGFloat)[rowHeights[UIApplication.sharedApplication.preferredContentSizeCategory] doubleValue] ? : 51;
  400. );
  401. return 44;
  402. }
  403. - (NSString *)tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section {
  404. NSString *header = [self.settingsReader titleForSection:section];
  405. if (0 == header.length) {
  406. return nil;
  407. }
  408. return header;
  409. }
  410. - (UIView *)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
  411. if ([self.delegate respondsToSelector:@selector(settingsViewController:tableView:viewForHeaderForSection:)]) {
  412. return [self.delegate settingsViewController:self tableView:tableView viewForHeaderForSection:section];
  413. } else {
  414. return nil;
  415. }
  416. }
  417. - (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section {
  418. if ([self tableView:tableView viewForHeaderInSection:section] && [self.delegate respondsToSelector:@selector(settingsViewController:tableView:heightForHeaderForSection:)]) {
  419. CGFloat result;
  420. if ((result = [self.delegate settingsViewController:self tableView:tableView heightForHeaderForSection:section])) {
  421. return result;
  422. }
  423. }
  424. return UITableViewAutomaticDimension;
  425. }
  426. - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
  427. {
  428. NSString *footerText = [self.settingsReader footerTextForSection:section];
  429. if (_showCreditsFooter && (section == [self.settingsReader numberOfSections]-1)) {
  430. // show credits since this is the last section
  431. if ((footerText == nil) || ([footerText length] == 0)) {
  432. // show the credits on their own
  433. return kIASKCredits;
  434. } else {
  435. // show the credits below the app's FooterText
  436. return [NSString stringWithFormat:@"%@\n\n%@", footerText, kIASKCredits];
  437. }
  438. } else {
  439. return footerText;
  440. }
  441. }
  442. - (UITableViewCell*)tableView:(UITableView *)tableView newCellForSpecifier:(IASKSpecifier*)specifier {
  443. NSString *identifier = [NSString stringWithFormat:@"%@-%ld-%d", specifier.type, (long)specifier.textAlignment, !!specifier.subtitle.length];
  444. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  445. if (cell) {
  446. return cell;
  447. }
  448. UITableViewCellStyle style = (specifier.textAlignment == NSTextAlignmentLeft || specifier.subtitle.length) ? UITableViewCellStyleSubtitle : UITableViewCellStyleDefault;
  449. if ([identifier hasPrefix:kIASKPSToggleSwitchSpecifier]) {
  450. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kIASKPSToggleSwitchSpecifier];
  451. cell.accessoryView = [[IASKSwitch alloc] initWithFrame:CGRectMake(0, 0, 79, 27)];
  452. [((IASKSwitch*)cell.accessoryView) addTarget:self action:@selector(toggledValue:) forControlEvents:UIControlEventValueChanged];
  453. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  454. }
  455. else if ([identifier hasPrefix:kIASKPSMultiValueSpecifier] || [identifier hasPrefix:kIASKPSTitleValueSpecifier]) {
  456. cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
  457. cell.accessoryType = [identifier hasPrefix:kIASKPSMultiValueSpecifier] ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
  458. }
  459. else if ([identifier hasPrefix:kIASKPSTextFieldSpecifier]) {
  460. cell = [[IASKPSTextFieldSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kIASKPSTextFieldSpecifier];
  461. [((IASKPSTextFieldSpecifierViewCell*)cell).textField addTarget:self action:@selector(_textChanged:) forControlEvents:UIControlEventEditingChanged];
  462. }
  463. else if ([identifier hasPrefix:kIASKPSSliderSpecifier]) {
  464. cell = [[IASKPSSliderSpecifierViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kIASKPSSliderSpecifier];
  465. } else if ([identifier hasPrefix:kIASKPSChildPaneSpecifier]) {
  466. cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:identifier];
  467. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  468. } else if ([identifier isEqualToString:kIASKMailComposeSpecifier]) {
  469. cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:identifier];
  470. [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
  471. } else {
  472. cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:identifier];
  473. if ([identifier isEqualToString:kIASKOpenURLSpecifier]) {
  474. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  475. }
  476. }
  477. IASK_IF_PRE_IOS6(cell.textLabel.minimumFontSize = kIASKMinimumFontSize;
  478. cell.detailTextLabel.minimumFontSize = kIASKMinimumFontSize;);
  479. IASK_IF_IOS6_OR_GREATER(cell.textLabel.minimumScaleFactor = kIASKMinimumFontSize / cell.textLabel.font.pointSize;
  480. cell.detailTextLabel.minimumScaleFactor = kIASKMinimumFontSize / cell.detailTextLabel.font.pointSize;);
  481. return cell;
  482. }
  483. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  484. IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
  485. if ([specifier.type isEqualToString:kIASKCustomViewSpecifier] && [self.delegate respondsToSelector:@selector(tableView:cellForSpecifier:)]) {
  486. UITableViewCell* cell = [self.delegate tableView:tableView cellForSpecifier:specifier];
  487. assert(nil != cell && "delegate must return a UITableViewCell for custom cell types");
  488. return cell;
  489. }
  490. UITableViewCell* cell = [self tableView:tableView newCellForSpecifier:specifier];
  491. if ([specifier.type isEqualToString:kIASKPSToggleSwitchSpecifier]) {
  492. cell.textLabel.text = specifier.title;
  493. cell.detailTextLabel.text = specifier.subtitle;
  494. id currentValue = [self.settingsStore objectForKey:specifier.key];
  495. BOOL toggleState;
  496. if (currentValue) {
  497. if ([currentValue isEqual:specifier.trueValue]) {
  498. toggleState = YES;
  499. } else if ([currentValue isEqual:specifier.falseValue]) {
  500. toggleState = NO;
  501. } else {
  502. toggleState = [currentValue boolValue];
  503. }
  504. } else {
  505. toggleState = specifier.defaultBoolValue;
  506. }
  507. IASKSwitch *toggle = (IASKSwitch*)cell.accessoryView;
  508. toggle.on = toggleState;
  509. toggle.key = specifier.key;
  510. }
  511. else if ([specifier.type isEqualToString:kIASKPSMultiValueSpecifier]) {
  512. cell.textLabel.text = specifier.title;
  513. cell.detailTextLabel.text = [[specifier titleForCurrentValue:[self.settingsStore objectForKey:specifier.key] != nil ?
  514. [self.settingsStore objectForKey:specifier.key] : specifier.defaultValue] description];
  515. }
  516. else if ([specifier.type isEqualToString:kIASKPSTitleValueSpecifier]) {
  517. cell.textLabel.text = specifier.title;
  518. id value = [self.settingsStore objectForKey:specifier.key] ? : specifier.defaultValue;
  519. NSString *stringValue;
  520. if (specifier.multipleValues || specifier.multipleTitles) {
  521. stringValue = [specifier titleForCurrentValue:value];
  522. } else {
  523. stringValue = [value description];
  524. }
  525. cell.detailTextLabel.text = stringValue;
  526. cell.userInteractionEnabled = NO;
  527. }
  528. else if ([specifier.type isEqualToString:kIASKPSTextFieldSpecifier]) {
  529. cell.textLabel.text = specifier.title;
  530. NSString *textValue = [self.settingsStore objectForKey:specifier.key] != nil ? [self.settingsStore objectForKey:specifier.key] : specifier.defaultStringValue;
  531. if (textValue && ![textValue isMemberOfClass:[NSString class]]) {
  532. textValue = [NSString stringWithFormat:@"%@", textValue];
  533. }
  534. IASKTextField *textField = ((IASKPSTextFieldSpecifierViewCell*)cell).textField;
  535. textField.text = textValue;
  536. textField.key = specifier.key;
  537. textField.delegate = self;
  538. textField.secureTextEntry = [specifier isSecure];
  539. textField.keyboardType = specifier.keyboardType;
  540. textField.autocapitalizationType = specifier.autocapitalizationType;
  541. if([specifier isSecure]){
  542. textField.autocorrectionType = UITextAutocorrectionTypeNo;
  543. } else {
  544. textField.autocorrectionType = specifier.autoCorrectionType;
  545. }
  546. textField.textAlignment = specifier.textAlignment;
  547. textField.adjustsFontSizeToFitWidth = specifier.adjustsFontSizeToFitWidth;
  548. }
  549. else if ([specifier.type isEqualToString:kIASKPSSliderSpecifier]) {
  550. if (specifier.minimumValueImage.length > 0) {
  551. ((IASKPSSliderSpecifierViewCell*)cell).minImage.image = [UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:specifier.minimumValueImage]];
  552. }
  553. if (specifier.maximumValueImage.length > 0) {
  554. ((IASKPSSliderSpecifierViewCell*)cell).maxImage.image = [UIImage imageWithContentsOfFile:[_settingsReader pathForImageNamed:specifier.maximumValueImage]];
  555. }
  556. IASKSlider *slider = ((IASKPSSliderSpecifierViewCell*)cell).slider;
  557. slider.minimumValue = specifier.minimumValue;
  558. slider.maximumValue = specifier.maximumValue;
  559. slider.value = [self.settingsStore objectForKey:specifier.key] != nil ? [[self.settingsStore objectForKey:specifier.key] floatValue] : [specifier.defaultValue floatValue];
  560. [slider addTarget:self action:@selector(sliderChangedValue:) forControlEvents:UIControlEventValueChanged];
  561. slider.key = specifier.key;
  562. [cell setNeedsLayout];
  563. }
  564. else if ([specifier.type isEqualToString:kIASKPSChildPaneSpecifier]) {
  565. cell.textLabel.text = specifier.title;
  566. cell.detailTextLabel.text = specifier.subtitle;
  567. } else if ([specifier.type isEqualToString:kIASKOpenURLSpecifier] || [specifier.type isEqualToString:kIASKMailComposeSpecifier]) {
  568. cell.textLabel.text = specifier.title;
  569. cell.detailTextLabel.text = specifier.subtitle ? : [specifier.defaultValue description];
  570. cell.accessoryType = (specifier.textAlignment == NSTextAlignmentLeft) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
  571. } else if ([specifier.type isEqualToString:kIASKButtonSpecifier]) {
  572. NSString *value = [self.settingsStore objectForKey:specifier.key];
  573. cell.textLabel.text = [value isKindOfClass:[NSString class]] ? [self.settingsReader titleForStringId:value] : specifier.title;
  574. cell.detailTextLabel.text = specifier.subtitle;
  575. IASK_IF_IOS7_OR_GREATER
  576. (if (specifier.textAlignment != NSTextAlignmentLeft) {
  577. cell.textLabel.textColor = tableView.tintColor;
  578. });
  579. cell.textLabel.textAlignment = specifier.textAlignment;
  580. cell.accessoryType = (specifier.textAlignment == NSTextAlignmentLeft) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
  581. } else if ([specifier.type isEqualToString:kIASKPSRadioGroupSpecifier]) {
  582. NSInteger index = [specifier.multipleValues indexOfObject:specifier.radioGroupValue];
  583. cell.textLabel.text = [self.settingsReader titleForStringId:specifier.multipleTitles[index]];
  584. [_selections[indexPath.section] updateSelectionInCell:cell indexPath:indexPath];
  585. } else {
  586. cell.textLabel.text = specifier.title;
  587. }
  588. cell.imageView.image = specifier.cellImage;
  589. cell.imageView.highlightedImage = specifier.highlightedCellImage;
  590. if (![specifier.type isEqualToString:kIASKPSMultiValueSpecifier] && ![specifier.type isEqualToString:kIASKPSTitleValueSpecifier] && ![specifier.type isEqualToString:kIASKPSTextFieldSpecifier]) {
  591. cell.textLabel.textAlignment = specifier.textAlignment;
  592. }
  593. cell.detailTextLabel.textAlignment = specifier.textAlignment;
  594. cell.textLabel.adjustsFontSizeToFitWidth = specifier.adjustsFontSizeToFitWidth;
  595. cell.detailTextLabel.adjustsFontSizeToFitWidth = specifier.adjustsFontSizeToFitWidth;
  596. return cell;
  597. }
  598. - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  599. //create a set of specifier types that can't be selected
  600. static NSSet* noSelectionTypes = nil;
  601. if(nil == noSelectionTypes) {
  602. noSelectionTypes = [NSSet setWithObjects:kIASKPSToggleSwitchSpecifier, kIASKPSSliderSpecifier, nil];
  603. }
  604. IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
  605. if([noSelectionTypes containsObject:specifier.type]) {
  606. return nil;
  607. } else {
  608. return indexPath;
  609. }
  610. }
  611. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  612. IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
  613. //switches and sliders can't be selected (should be captured by tableView:willSelectRowAtIndexPath: delegate method)
  614. assert(![[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]);
  615. assert(![[specifier type] isEqualToString:kIASKPSSliderSpecifier]);
  616. if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
  617. IASKSpecifierValuesViewController *targetViewController = [[IASKSpecifierValuesViewController alloc] init];
  618. [targetViewController setCurrentSpecifier:specifier];
  619. targetViewController.settingsReader = self.settingsReader;
  620. targetViewController.settingsStore = self.settingsStore;
  621. IASK_IF_IOS7_OR_GREATER(targetViewController.view.tintColor = self.view.tintColor;)
  622. _currentChildViewController = targetViewController;
  623. [[self navigationController] pushViewController:targetViewController animated:YES];
  624. } else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
  625. IASKPSTextFieldSpecifierViewCell *textFieldCell = (id)[tableView cellForRowAtIndexPath:indexPath];
  626. [textFieldCell.textField becomeFirstResponder];
  627. } else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
  628. if ([specifier viewControllerStoryBoardID]){
  629. NSString *storyBoardFileFromSpecifier = [specifier viewControllerStoryBoardFile];
  630. storyBoardFileFromSpecifier = storyBoardFileFromSpecifier && storyBoardFileFromSpecifier.length > 0 ? storyBoardFileFromSpecifier : [[NSBundle mainBundle].infoDictionary objectForKey:@"UIMainStoryboardFile"];
  631. UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:storyBoardFileFromSpecifier bundle:nil];
  632. UIViewController * vc = [storyBoard instantiateViewControllerWithIdentifier:[specifier viewControllerStoryBoardID]];
  633. IASK_IF_IOS7_OR_GREATER(vc.view.tintColor = self.view.tintColor;)
  634. [self.navigationController pushViewController:vc animated:YES];
  635. return;
  636. }
  637. Class vcClass = [specifier viewControllerClass];
  638. if (vcClass) {
  639. SEL initSelector = [specifier viewControllerSelector];
  640. if (!initSelector) {
  641. initSelector = @selector(init);
  642. }
  643. UIViewController * vc = [vcClass alloc];
  644. #pragma clang diagnostic push
  645. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  646. vc = [vc performSelector:initSelector withObject:[specifier file] withObject:specifier];
  647. #pragma clang diagnostic pop
  648. if ([vc respondsToSelector:@selector(setDelegate:)]) {
  649. [vc performSelector:@selector(setDelegate:) withObject:self.delegate];
  650. }
  651. if ([vc respondsToSelector:@selector(setSettingsStore:)]) {
  652. [vc performSelector:@selector(setSettingsStore:) withObject:self.settingsStore];
  653. }
  654. IASK_IF_IOS7_OR_GREATER(vc.view.tintColor = self.view.tintColor;)
  655. [self.navigationController pushViewController:vc animated:YES];
  656. return;
  657. }
  658. if (nil == [specifier file]) {
  659. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  660. return;
  661. }
  662. _reloadDisabled = YES; // Disable internal unnecessary reloads
  663. IASKAppSettingsViewController *targetViewController = [[[self class] alloc] init];
  664. targetViewController.showDoneButton = NO;
  665. targetViewController.showCreditsFooter = NO; // Does not reload the tableview (but next setters do it)
  666. targetViewController.delegate = self.delegate;
  667. targetViewController.settingsStore = self.settingsStore;
  668. targetViewController.file = specifier.file;
  669. targetViewController.hiddenKeys = self.hiddenKeys;
  670. targetViewController.title = specifier.title;
  671. IASK_IF_IOS7_OR_GREATER(targetViewController.view.tintColor = self.view.tintColor;)
  672. _currentChildViewController = targetViewController;
  673. _reloadDisabled = NO;
  674. [[self navigationController] pushViewController:targetViewController animated:YES];
  675. } else if ([[specifier type] isEqualToString:kIASKOpenURLSpecifier]) {
  676. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  677. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[specifier localizedObjectForKey:kIASKFile]]];
  678. } else if ([[specifier type] isEqualToString:kIASKButtonSpecifier]) {
  679. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  680. if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForSpecifier:)]) {
  681. [self.delegate settingsViewController:self buttonTappedForSpecifier:specifier];
  682. } else if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForKey:)]) {
  683. // deprecated, provided for backward compatibility
  684. NSLog(@"InAppSettingsKit Warning: -settingsViewController:buttonTappedForKey: is deprecated. Please use -settingsViewController:buttonTappedForSpecifier:");
  685. [self.delegate settingsViewController:self buttonTappedForKey:[specifier key]];
  686. } else {
  687. // legacy code, provided for backward compatibility
  688. // the delegate mechanism above is much cleaner and doesn't leak
  689. Class buttonClass = [specifier buttonClass];
  690. SEL buttonAction = [specifier buttonAction];
  691. if ([buttonClass respondsToSelector:buttonAction]) {
  692. #pragma clang diagnostic push
  693. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  694. [buttonClass performSelector:buttonAction withObject:self withObject:[specifier key]];
  695. #pragma clang diagnostic pop
  696. NSLog(@"InAppSettingsKit Warning: Using IASKButtonSpecifier without implementing the delegate method is deprecated");
  697. }
  698. }
  699. } else if ([[specifier type] isEqualToString:kIASKMailComposeSpecifier]) {
  700. [tableView deselectRowAtIndexPath:indexPath animated:YES];
  701. if ([MFMailComposeViewController canSendMail]) {
  702. MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
  703. mailViewController.navigationBar.barStyle = self.navigationController.navigationBar.barStyle;
  704. IASK_IF_IOS7_OR_GREATER(mailViewController.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;);
  705. mailViewController.navigationBar.titleTextAttributes = self.navigationController.navigationBar.titleTextAttributes;
  706. if ([specifier localizedObjectForKey:kIASKMailComposeSubject]) {
  707. [mailViewController setSubject:[specifier localizedObjectForKey:kIASKMailComposeSubject]];
  708. }
  709. if ([[specifier specifierDict] objectForKey:kIASKMailComposeToRecipents]) {
  710. [mailViewController setToRecipients:[[specifier specifierDict] objectForKey:kIASKMailComposeToRecipents]];
  711. }
  712. if ([[specifier specifierDict] objectForKey:kIASKMailComposeCcRecipents]) {
  713. [mailViewController setCcRecipients:[[specifier specifierDict] objectForKey:kIASKMailComposeCcRecipents]];
  714. }
  715. if ([[specifier specifierDict] objectForKey:kIASKMailComposeBccRecipents]) {
  716. [mailViewController setBccRecipients:[[specifier specifierDict] objectForKey:kIASKMailComposeBccRecipents]];
  717. }
  718. if ([specifier localizedObjectForKey:kIASKMailComposeBody]) {
  719. BOOL isHTML = NO;
  720. if ([[specifier specifierDict] objectForKey:kIASKMailComposeBodyIsHTML]) {
  721. isHTML = [[[specifier specifierDict] objectForKey:kIASKMailComposeBodyIsHTML] boolValue];
  722. }
  723. if ([self.delegate respondsToSelector:@selector(settingsViewController:mailComposeBodyForSpecifier:)]) {
  724. [mailViewController setMessageBody:[self.delegate settingsViewController:self
  725. mailComposeBodyForSpecifier:specifier] isHTML:isHTML];
  726. }
  727. else {
  728. [mailViewController setMessageBody:[specifier localizedObjectForKey:kIASKMailComposeBody] isHTML:isHTML];
  729. }
  730. }
  731. UIViewController<MFMailComposeViewControllerDelegate> *vc = nil;
  732. if ([self.delegate respondsToSelector:@selector(settingsViewController:viewControllerForMailComposeViewForSpecifier:)]) {
  733. vc = [self.delegate settingsViewController:self viewControllerForMailComposeViewForSpecifier:specifier];
  734. }
  735. if (vc == nil) {
  736. vc = self;
  737. }
  738. mailViewController.mailComposeDelegate = vc;
  739. _currentChildViewController = mailViewController;
  740. UIStatusBarStyle savedStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
  741. [vc presentViewController:mailViewController animated:YES completion:^{
  742. [UIApplication sharedApplication].statusBarStyle = savedStatusBarStyle;
  743. }];
  744. } else {
  745. UIAlertView *alert = [[UIAlertView alloc]
  746. initWithTitle:NSLocalizedString(@"Mail not configured", @"InAppSettingsKit")
  747. message:NSLocalizedString(@"This device is not configured for sending Email. Please configure the Mail settings in the Settings app.", @"InAppSettingsKit")
  748. delegate: nil
  749. cancelButtonTitle:NSLocalizedString(@"OK", @"InAppSettingsKit")
  750. otherButtonTitles:nil];
  751. [alert show];
  752. }
  753. } else if ([[specifier type] isEqualToString:kIASKCustomViewSpecifier] && [self.delegate respondsToSelector:@selector(settingsViewController:tableView:didSelectCustomViewSpecifier:)]) {
  754. [self.delegate settingsViewController:self tableView:tableView didSelectCustomViewSpecifier:specifier];
  755. } else if ([[specifier type] isEqualToString:kIASKPSRadioGroupSpecifier]) {
  756. [_selections[indexPath.section] selectRowAtIndexPath:indexPath];
  757. } else {
  758. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  759. }
  760. }
  761. #pragma mark -
  762. #pragma mark MFMailComposeViewControllerDelegate Function
  763. -(void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
  764. // Forward the mail compose delegate
  765. if ([self.delegate respondsToSelector:@selector(settingsViewController:mailComposeController:didFinishWithResult:error:)]) {
  766. [self.delegate settingsViewController:self
  767. mailComposeController:controller
  768. didFinishWithResult:result
  769. error:error];
  770. }
  771. [self dismissViewControllerAnimated:YES
  772. completion:nil];
  773. }
  774. #pragma mark -
  775. #pragma mark UITextFieldDelegate Functions
  776. - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
  777. self.currentFirstResponder = textField;
  778. return YES;
  779. }
  780. - (void)_textChanged:(id)sender {
  781. IASKTextField *text = sender;
  782. [_settingsStore setObject:[text text] forKey:[text key]];
  783. [[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged
  784. object:[text key]
  785. userInfo:[NSDictionary dictionaryWithObject:[text text]
  786. forKey:[text key]]];
  787. }
  788. - (BOOL)textFieldShouldReturn:(UITextField *)textField
  789. {
  790. [textField resignFirstResponder];
  791. self.currentFirstResponder = nil;
  792. return YES;
  793. }
  794. - (void)singleTapToEndEdit:(UIGestureRecognizer *)sender
  795. {
  796. [self.tableView endEditing:NO];
  797. }
  798. #pragma mark Notifications
  799. - (void)synchronizeSettings
  800. {
  801. [_settingsStore synchronize];
  802. }
  803. static NSDictionary *oldUserDefaults = nil;
  804. - (void)userDefaultsDidChange {
  805. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  806. IASKSettingsStoreUserDefaults *udSettingsStore = (id)self.settingsStore;
  807. NSDictionary *currentDict = udSettingsStore.defaults.dictionaryRepresentation;
  808. NSMutableArray *indexPathsToUpdate = [NSMutableArray array];
  809. for (NSString *key in currentDict.allKeys) {
  810. if (oldUserDefaults && ![[oldUserDefaults valueForKey:key] isEqual:[currentDict valueForKey:key]]) {
  811. NSIndexPath *path = [self.settingsReader indexPathForKey:key];
  812. if (path && ![[self.settingsReader specifierForKey:key].type isEqualToString:kIASKCustomViewSpecifier] && [self.tableView.indexPathsForVisibleRows containsObject:path]) {
  813. [indexPathsToUpdate addObject:path];
  814. }
  815. }
  816. }
  817. oldUserDefaults = currentDict;
  818. for (UITableViewCell *cell in self.tableView.visibleCells) {
  819. if ([cell isKindOfClass:[IASKPSTextFieldSpecifierViewCell class]] && [((IASKPSTextFieldSpecifierViewCell*)cell).textField isFirstResponder]) {
  820. [indexPathsToUpdate removeObject:[self.tableView indexPathForCell:cell]];
  821. }
  822. }
  823. if (indexPathsToUpdate.count) {
  824. [self.tableView reloadRowsAtIndexPaths:indexPathsToUpdate withRowAnimation:UITableViewRowAnimationAutomatic];
  825. }
  826. });
  827. }
  828. - (void)didChangeSettingViaIASK:(NSNotification*)notification {
  829. [oldUserDefaults setValue:[self.settingsStore objectForKey:notification.object] forKey:notification.object];
  830. }
  831. - (void)reload {
  832. // wait 0.5 sec until UI is available after applicationWillEnterForeground
  833. [self.tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0.5];
  834. }
  835. #pragma mark CGRect Utility function
  836. CGRect IASKCGRectSwap(CGRect rect) {
  837. CGRect newRect;
  838. newRect.origin.x = rect.origin.y;
  839. newRect.origin.y = rect.origin.x;
  840. newRect.size.width = rect.size.height;
  841. newRect.size.height = rect.size.width;
  842. return newRect;
  843. }
  844. @end