LinkIDViewController.m 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "LinkIDViewController.h"
  21. #import "MyIdentityStore.h"
  22. #import "ServerAPIConnector.h"
  23. #import "NBMetadataHelper.h"
  24. #import "LinkIDCountryPickerRowView.h"
  25. #import "RectUtil.h"
  26. #import "PhoneNumberNormalizer.h"
  27. #import "UIDefines.h"
  28. #import "Utils.h"
  29. #import "IntroQuestionView.h"
  30. #import "LicenseStore.h"
  31. #import "MDMSetup.h"
  32. #import "UIImage+ColoredImage.h"
  33. #import "NibUtil.h"
  34. #define COUNTRY_ROW_HEIGHT 44.0
  35. @interface LinkIDViewController () <UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate, IntroQuestionDelegate>
  36. @property NBMetadataHelper *metaDataHelper;
  37. @property NSArray *allPhoneMetadata;
  38. @property NSDictionary *selectedPhoneMetadata;
  39. @property NSArray *countryNames;
  40. @property NSMutableDictionary *country2Region;
  41. @property NSString *currentCountry;
  42. @property MyIdentityStore *identityStore;
  43. @property BOOL didShowEmailWarning;
  44. @property BOOL didShowPhoneWarning;
  45. @property BOOL didAckInputWarning;
  46. @property CGFloat phoneViewYOffset;
  47. @property CGFloat countryViewYOffset;
  48. @property CGFloat emailViewYOffset;
  49. @property IntroQuestionView *questionView;
  50. @property BOOL restoredLinkedEmail;
  51. @property BOOL restoredLinkedPhone;
  52. @end
  53. @implementation LinkIDViewController
  54. - (void)viewDidLoad {
  55. [super viewDidLoad];
  56. _identityStore = [MyIdentityStore sharedMyIdentityStore];
  57. [self checkForRestoredLinkData];
  58. [self setup];
  59. [self setupCountrySelection];
  60. _phoneViewYOffset = _phoneView.frame.origin.y;
  61. _countryViewYOffset = _countryView.frame.origin.y;
  62. _emailViewYOffset = _emailView.frame.origin.y;
  63. }
  64. - (void)viewWillDisappear:(BOOL)animated {
  65. [super viewWillDisappear:animated];
  66. _identityStore.createIDEmail = _emailTextField.text;
  67. if (_restoredLinkedPhone) {
  68. _identityStore.createIDPhone = _phoneTextField.text;
  69. } else {
  70. NSString *phoneNumber = [self fullPhoneNumber];
  71. if (phoneNumber) {
  72. _identityStore.createIDPhone = phoneNumber;
  73. } else {
  74. _identityStore.createIDPhone = nil;
  75. }
  76. }
  77. [self hideKeyboard];
  78. }
  79. - (void)checkForRestoredLinkData {
  80. if (_identityStore.linkedEmail.length > 0) {
  81. _restoredLinkedEmail = YES;
  82. _emailTextField.userInteractionEnabled = NO;
  83. }
  84. if (_identityStore.linkedMobileNo.length > 0) {
  85. _restoredLinkedPhone = YES;
  86. _phoneTextField.userInteractionEnabled = NO;
  87. }
  88. }
  89. - (NSString *)fullPhoneNumber {
  90. if (_phoneTextField.text.length > 0) {
  91. NSString *regionalPart = [_phoneTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  92. return [NSString stringWithFormat:@"%@ %@", _countryCodeLabel.text, regionalPart];
  93. }
  94. return nil;
  95. }
  96. - (void)hideKeyboard {
  97. [_emailTextField resignFirstResponder];
  98. [_phoneTextField resignFirstResponder];
  99. }
  100. - (void)viewWillAppear:(BOOL)animated {
  101. [super viewWillAppear:animated];
  102. [self updateView];
  103. }
  104. - (void)viewDidAppear:(BOOL)animated {
  105. [super viewDidAppear:animated];
  106. [self registerForKeyboardNotifications];
  107. }
  108. - (void)viewDidDisappear:(BOOL)animated {
  109. [super viewDidDisappear:animated];
  110. [self unregisterForKeyboardNotifications];
  111. }
  112. - (NSString *)countryNameForRegion:(NSString *)region {
  113. id countryDictionaryInstance = [NSDictionary dictionaryWithObject:region forKey:NSLocaleCountryCode];
  114. NSString *identifier = [NSLocale localeIdentifierFromComponents:countryDictionaryInstance];
  115. NSString *country = [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:identifier];
  116. return country;
  117. }
  118. - (void)setupCountrySelection {
  119. NSMutableArray *countries = [NSMutableArray array];
  120. NSMutableArray *codes = [NSMutableArray array];
  121. _country2Region = [NSMutableDictionary dictionary];
  122. [NBMetadataHelper.CCode2CNMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
  123. NSString *countryName = [self countryNameForRegion:key];
  124. if (countryName) {
  125. [countries addObject:countryName];
  126. [codes addObject:obj];
  127. [_country2Region setObject:key forKey:countryName];
  128. }
  129. }];
  130. _countryNames = [countries sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
  131. _countryPicker.dataSource = self;
  132. _countryPicker.delegate = self;
  133. if (_restoredLinkedPhone == NO) {
  134. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedCountry:)];
  135. [_countryView addGestureRecognizer:tapGesture];
  136. _countryView.userInteractionEnabled = YES;
  137. _countryView.isAccessibilityElement = YES;
  138. [_countryView setAccessibilityHint: [BundleUtil localizedStringForKey:@"tap_to_change"]];
  139. UITapGestureRecognizer *codeTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedCountry:)];
  140. [_countryCodeLabel addGestureRecognizer:codeTapGesture];
  141. _countryCodeLabel.userInteractionEnabled = YES;
  142. _countryCodeLabel.isAccessibilityElement = YES;
  143. [_countryCodeLabel setAccessibilityHint: [BundleUtil localizedStringForKey:@"tap_to_change"]];
  144. }
  145. UITapGestureRecognizer *mainTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedMainView:)];
  146. [self.mainContentView addGestureRecognizer:mainTapGesture];
  147. }
  148. - (void)adaptToSmallScreen {
  149. [super adaptToSmallScreen];
  150. CGFloat yOffset = -36.0;
  151. _descriptionLabel.frame = [RectUtil offsetRect:_descriptionLabel.frame byX:0.0 byY:yOffset];
  152. _emailView.frame = [RectUtil offsetRect:_emailView.frame byX:0.0 byY:yOffset];
  153. _phoneView.frame = [RectUtil offsetRect:_phoneView.frame byX:0.0 byY:yOffset];
  154. _countryView.frame = [RectUtil offsetRect:_countryView.frame byX:0.0 byY:yOffset];
  155. yOffset -= 32.0;
  156. _countryPickerView.frame = [RectUtil offsetRect:_countryPickerView.frame byX:0.0 byY:yOffset];
  157. }
  158. - (void)setup {
  159. _emailBackgroundView.layer.cornerRadius = 3;
  160. _phoneBackroundView.layer.cornerRadius = 3;
  161. _countryView.layer.cornerRadius = 3;
  162. _countryView.layer.borderColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1].CGColor;
  163. _countryView.layer.borderWidth = 0.5;
  164. _emailView.layer.cornerRadius = 3;
  165. _emailView.layer.borderColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1].CGColor;
  166. _emailView.layer.borderWidth = 0.5;
  167. _phoneView.layer.cornerRadius = 3;
  168. _phoneView.layer.borderColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1].CGColor;
  169. _phoneView.layer.borderWidth = 0.5;
  170. _selectedCountryButton.layer.cornerRadius = 3;
  171. [_selectedCountryButton setTitle:[BundleUtil localizedStringForKey:@"ok"] forState:UIControlStateNormal];
  172. NSString *emailPlaceholder = [BundleUtil localizedStringForKey:@"id_enter_email"];
  173. _emailTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:emailPlaceholder attributes:@{NSForegroundColorAttributeName: THREEMA_COLOR_PLACEHOLDER}];
  174. _emailTextField.delegate = self;
  175. _emailTextField.accessibilityHint = [BundleUtil localizedStringForKey:@"Email"];
  176. _phoneTextField.delegate = self;
  177. _phoneTextField.accessibilityHint = [BundleUtil localizedStringForKey:@"phone number"];
  178. if ([LicenseStore requiresLicenseKey]) {
  179. _descriptionLabel.text = [BundleUtil localizedStringForKey:@"id_link_description_work"];
  180. } else {
  181. _descriptionLabel.text = [BundleUtil localizedStringForKey:@"id_link_description"];
  182. }
  183. self.moreView.mainView = self.mainContentView;
  184. if ([LicenseStore requiresLicenseKey]) {
  185. _titleLabel.text = [BundleUtil localizedStringForKey:@"id_link_title_work"];
  186. self.moreView.moreMessageText = [BundleUtil localizedStringForKey:@"more_information_link_id_work"];
  187. } else {
  188. _titleLabel.text = [BundleUtil localizedStringForKey:@"id_link_title"];
  189. self.moreView.moreMessageText = [BundleUtil localizedStringForKey:@"more_information_link_id"];
  190. }
  191. _titleLabel.accessibilityIdentifier = @"id_link_title";
  192. MDMSetup *mdmSetup = [[MDMSetup alloc] initWithSetup:YES];
  193. if ([mdmSetup readonlyProfile]) {
  194. _emailTextField.enabled = NO;
  195. _phoneTextField.enabled = NO;
  196. }
  197. _emailTextField.tintColor = [Colors mainThemeDark];
  198. _phoneTextField.tintColor = [Colors mainThemeDark];
  199. _selectedCountryButton.backgroundColor = [Colors mainThemeDark];
  200. [_selectedCountryButton setTitleColor:[Colors white] forState:UIControlStateNormal];
  201. _phoneImageView.image = [UIImage imageNamed:@"Phone" inColor:[UIColor whiteColor]];
  202. _mailImageView.image = [UIImage imageNamed:@"Mail" inColor:[UIColor whiteColor]];
  203. }
  204. - (void)updatePhonePlaceholder {
  205. PhoneNumberNormalizer *normalizer = [PhoneNumberNormalizer sharedInstance];
  206. NSString *region;
  207. if (_currentCountry) {
  208. region = [_country2Region objectForKey:_currentCountry];
  209. } else {
  210. region = [PhoneNumberNormalizer userRegion];
  211. }
  212. if (region) {
  213. [self updateUIWithRegion:region];
  214. NSString *examplePhone = [normalizer exampleRegionalPhoneNumberForRegion:region];
  215. if (examplePhone != nil) {
  216. NSString *placeholder = [NSString stringWithFormat:@"%@ %@", examplePhone, [BundleUtil localizedStringForKey:@"(optional)"]];
  217. _phoneTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholder attributes:@{NSForegroundColorAttributeName: THREEMA_COLOR_PLACEHOLDER}];
  218. }
  219. }
  220. }
  221. - (void)updateUIWithRegion:(NSString *)region {
  222. if (region) {
  223. NSString *countryName = [self countryNameForRegion:region];
  224. _countryLabel.text = countryName;
  225. NSInteger row = [_countryNames indexOfObject:countryName];
  226. if (row != NSNotFound) {
  227. NSString *codeString = [self codeForCountry:countryName];
  228. _countryCodeLabel.text = codeString;
  229. _countryCodeLabel.accessibilityLabel = [NSString stringWithFormat:@"%@, %@", [BundleUtil localizedStringForKey:@"country_code"], codeString];
  230. [_countryPicker selectRow:row inComponent:0 animated:NO];
  231. }
  232. }
  233. }
  234. - (void)updateView {
  235. /* linked e-mail */
  236. if (_restoredLinkedEmail) {
  237. // A linked email address was restored, but we don't know the actual address (linkedEmail will be "***@***").
  238. _emailTextField.text = [BundleUtil localizedStringForKey:@"(linked)"];
  239. } else if (_identityStore.createIDEmail) {
  240. self.emailTextField.text = _identityStore.createIDEmail;
  241. }
  242. /* linked mobile number */
  243. if (_restoredLinkedPhone) {
  244. // A linked phone number was restored, but we don't know the actual number (linkedMobileNo will be "***").
  245. _phoneTextField.text = [BundleUtil localizedStringForKey:@"(linked)"];
  246. } else if (_identityStore.createIDPhone) {
  247. NSString *phoneNumber = _identityStore.createIDPhone;
  248. PhoneNumberNormalizer *normalizer = [PhoneNumberNormalizer sharedInstance];
  249. NSString *region = [normalizer regionForPhoneNumber:phoneNumber];
  250. if (region) {
  251. [self updateUIWithRegion:region];
  252. NSString *regionalPart = [normalizer regionalPartForPhoneNumber:phoneNumber];
  253. _phoneTextField.text = regionalPart;
  254. }
  255. }
  256. [self updatePhonePlaceholder];
  257. [self hideEmailIfNeeded];
  258. }
  259. - (void)showCountrySelector {
  260. [self hideKeyboard];
  261. [self showMessageView:_countryPickerView];
  262. }
  263. - (void)hideCountrySelector {
  264. [self hideMessageView:_countryPickerView];
  265. }
  266. - (BOOL)isInputValid {
  267. [self hideKeyboard];
  268. if ([self.moreView isShown]) {
  269. return NO;
  270. }
  271. if (_countryPickerView.hidden == NO) {
  272. return NO;
  273. }
  274. if ([self hasInput] == NO && _didAckInputWarning == NO) {
  275. [self showAlert];
  276. return NO;
  277. }
  278. BOOL shouldPage = YES;
  279. if ([self validateEmail] == NO) {
  280. shouldPage = NO;
  281. }
  282. if ([self validatePhoneNumber] == NO) {
  283. shouldPage = NO;
  284. }
  285. return shouldPage;
  286. }
  287. - (BOOL)hasInput {
  288. if (_emailTextField.text.length > 0) {
  289. return YES;
  290. }
  291. if (_phoneTextField.text.length > 0) {
  292. return YES;
  293. }
  294. return NO;
  295. }
  296. - (BOOL)validateEmail {
  297. if (_restoredLinkedEmail) {
  298. return YES;
  299. }
  300. if (_emailTextField.text.length == 0) {
  301. _emailStateImageView.hidden = YES;
  302. return YES;
  303. }
  304. BOOL isValid = [Utils isValidEmail:_emailTextField.text];
  305. if (isValid) {
  306. _emailStateImageView.hidden = YES;
  307. return YES;
  308. } else {
  309. _emailStateImageView.hidden = NO;
  310. }
  311. if (_didShowEmailWarning) {
  312. return YES;
  313. } else {
  314. _didShowEmailWarning = YES;
  315. return NO;
  316. }
  317. }
  318. - (BOOL)validatePhoneNumber {
  319. if (_restoredLinkedPhone) {
  320. return YES;
  321. }
  322. NSString *phone = [self fullPhoneNumber];
  323. if (phone == nil) {
  324. _phoneStateImageView.hidden = YES;
  325. return YES;
  326. }
  327. PhoneNumberNormalizer *normalizer = [PhoneNumberNormalizer sharedInstance];
  328. NSString *prettyMobileNo;
  329. NSString *mobileNo = [normalizer phoneNumberToE164:phone withDefaultRegion:[PhoneNumberNormalizer userRegion] prettyFormat:&prettyMobileNo];
  330. if (mobileNo) {
  331. _phoneStateImageView.hidden = YES;
  332. return YES;
  333. } else {
  334. _phoneStateImageView.hidden = NO;
  335. }
  336. if (_didShowPhoneWarning) {
  337. return YES;
  338. } else {
  339. _didShowPhoneWarning = YES;
  340. return NO;
  341. }
  342. }
  343. - (NSString *)codeForCountry:(NSString *)countryName {
  344. NSString *region = [_country2Region objectForKey:countryName];
  345. NSString *code = [NBMetadataHelper.CCode2CNMap objectForKey:region];
  346. return [NSString stringWithFormat:@"+%@", code];
  347. }
  348. - (void)hideAlert {
  349. [self hideMessageView:_questionView];
  350. }
  351. - (void)showAlert {
  352. [self hideKeyboard];
  353. if (_questionView == nil) {
  354. _questionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"];
  355. if ([LicenseStore requiresLicenseKey]) {
  356. _questionView.questionLabel.text = [BundleUtil localizedStringForKey:@"id_link_no_input_work"];
  357. } else {
  358. _questionView.questionLabel.text = [BundleUtil localizedStringForKey:@"id_link_no_input"];
  359. }
  360. _questionView.delegate = self;
  361. _questionView.frame = [RectUtil rect:_questionView.frame centerIn:self.view.frame round:YES];
  362. [self.view addSubview:_questionView];
  363. }
  364. [self showMessageView:_questionView];
  365. }
  366. - (void)hideEmailIfNeeded {
  367. _emailView.hidden = ![LicenseStore requiresLicenseKey];
  368. }
  369. #pragma mark - IntroQuestionViewDelegate
  370. - (void)selectedYes:(IntroQuestionView *)sender {
  371. _didAckInputWarning = YES;
  372. [self hideAlert];
  373. [self.containerDelegate pageLeft];
  374. }
  375. - (void)selectedNo:(IntroQuestionView *)sender {
  376. [self hideAlert];
  377. [_phoneTextField becomeFirstResponder];
  378. }
  379. #pragma mark - UITapGestureRecognizer
  380. - (void)tappedCountry:(UITapGestureRecognizer *)sender
  381. {
  382. if (sender.state == UIGestureRecognizerStateEnded) {
  383. [self showCountrySelector];
  384. }
  385. }
  386. - (void)tappedMainView:(UITapGestureRecognizer *)sender
  387. {
  388. if (sender.state == UIGestureRecognizerStateEnded) {
  389. [self hideKeyboard];
  390. }
  391. }
  392. - (IBAction)selectedCountryAction:(id)sender {
  393. [self hideCountrySelector];
  394. NSInteger row = [_countryPicker selectedRowInComponent:0];
  395. NSString *name = [_countryNames objectAtIndex:row];
  396. _countryLabel.text = name;
  397. _currentCountry = name;
  398. _countryCodeLabel.text = [self codeForCountry:name];
  399. [self updatePhonePlaceholder];
  400. }
  401. #pragma mark - UIPickerViewDataSource, UIPickerViewDelegate
  402. - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
  403. return 1;
  404. }
  405. - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
  406. return [_countryNames count];
  407. }
  408. -(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
  409. LinkIDCountryPickerRowView *rowView;
  410. if (view) {
  411. rowView = (LinkIDCountryPickerRowView *)view;
  412. } else {
  413. CGRect rect = CGRectMake(0.0, 0.0, _countryPicker.frame.size.width, COUNTRY_ROW_HEIGHT);
  414. rowView = [[LinkIDCountryPickerRowView alloc] initWithFrame:rect];
  415. }
  416. NSString *name = [_countryNames objectAtIndex:row];
  417. rowView.nameLabel.text = name;
  418. rowView.codeLabel.text = [self codeForCountry:name];
  419. return rowView;
  420. }
  421. #pragma mark - UITextFieldDelegate
  422. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  423. NSString *newText = [textField.text stringByReplacingCharactersInRange:range withString:string];
  424. if (textField == _emailTextField && _didShowEmailWarning) {
  425. _emailTextField.text = newText;
  426. [self validateEmail];
  427. return NO;
  428. }
  429. if (textField == _phoneTextField && _didShowPhoneWarning) {
  430. _phoneTextField.text = newText;
  431. [self validatePhoneNumber];
  432. return NO;
  433. }
  434. return YES;
  435. }
  436. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  437. if (textField == _emailTextField) {
  438. [_emailTextField resignFirstResponder];
  439. return NO;
  440. }
  441. return YES;
  442. }
  443. # pragma mark Keyboard Notifications
  444. - (void)registerForKeyboardNotifications {
  445. [[NSNotificationCenter defaultCenter] addObserver:self
  446. selector:@selector(keyboardWillShow:)
  447. name:UIKeyboardWillShowNotification object:nil];
  448. [[NSNotificationCenter defaultCenter] addObserver:self
  449. selector:@selector(keyboardWillHide:)
  450. name:UIKeyboardWillHideNotification object:nil];
  451. }
  452. - (void)unregisterForKeyboardNotifications {
  453. [[NSNotificationCenter defaultCenter] removeObserver: self];
  454. }
  455. - (void)keyboardWillShow: (NSNotification *) notification {
  456. NSDictionary* info = [notification userInfo];
  457. CGRect keyboardRect = [[info objectForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue];
  458. CGRect keyboardRectConverted = [self.view convertRect:keyboardRect fromView:nil];
  459. UIView *responderView;
  460. if (_emailTextField.isFirstResponder) {
  461. responderView = _emailView;
  462. } else if (_phoneTextField.isFirstResponder){
  463. responderView = _phoneView;
  464. } else {
  465. return;
  466. }
  467. _titleLabel.accessibilityLabel = [BundleUtil localizedStringForKey:@"Done"];
  468. CGFloat diff = CGRectGetMinY(keyboardRectConverted) - CGRectGetMaxY(responderView.frame) - 32.0;
  469. if (diff < 0.0) {
  470. if (_emailTextField.isFirstResponder) {
  471. _phoneView.hidden = YES;
  472. _countryView.hidden = YES;
  473. } else if (_phoneTextField.isFirstResponder){
  474. _emailView.hidden = YES;
  475. _countryView.hidden = YES;
  476. }
  477. NSTimeInterval animationDuration;
  478. UIViewAnimationOptions options = [Utils animationOptionsFor:notification animationDuration:&animationDuration];
  479. [UIView animateWithDuration:animationDuration delay:0 options:options animations:^{
  480. responderView.frame = [RectUtil offsetRect:responderView.frame byX:0.0 byY:diff];
  481. } completion:^(BOOL finished) {}];
  482. }
  483. }
  484. - (void)keyboardWillHide:(NSNotification*)notification {
  485. NSTimeInterval animationDuration;
  486. UIViewAnimationOptions options = [Utils animationOptionsFor:notification animationDuration:&animationDuration];
  487. _titleLabel.accessibilityLabel = _titleLabel.text;
  488. [UIView animateWithDuration:animationDuration delay:0 options:options animations:^{
  489. _phoneView.frame = [RectUtil setYPositionOf:_phoneView.frame y:_phoneViewYOffset];
  490. _countryView.frame = [RectUtil setYPositionOf:_countryView.frame y:_countryViewYOffset];
  491. _emailView.frame = [RectUtil setYPositionOf:_emailView.frame y:_emailViewYOffset];
  492. } completion:^(BOOL finished) {
  493. [self hideEmailIfNeeded];
  494. _countryView.hidden = NO;
  495. _phoneView.hidden = NO;
  496. }];
  497. }
  498. @end