Index: ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
diff --git a/ios/chrome/browser/payments/payment_request_picker_view_controller.mm b/ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7131852bc0d4f89a9adf467ce20c7440ff7b8e70 |
--- /dev/null |
+++ b/ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
@@ -0,0 +1,224 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import "ios/chrome/browser/payments/payment_request_picker_view_controller.h" |
+ |
+#import "base/logging.h" |
+#import "ios/chrome/browser/payments/payment_request_picker_row.h" |
+#import "ios/third_party/material_components_ios/src/components/CollectionCells/src/MaterialCollectionCells.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+NSString* const kPaymentRequestPickerRowAccessibilityID = |
+ @"kPaymentRequestPickerRowAccessibilityID"; |
+NSString* const kPaymentRequestPickerSelectedRowAccessibilityID = |
+ @"kPaymentRequestPickerSelectedRowAccessibilityID"; |
+NSString* const kPaymentRequestPickerSearchBarAccessibilityID = |
+ @"kPaymentRequestPickerSearchBarAccessibilityID"; |
+ |
+namespace { |
+NSString* const kPaymentRequestPickerViewControllerAccessibilityID = |
+ @"kPaymentRequestPickerViewControllerAccessibilityID"; |
+ |
lpromero
2017/03/29 11:38:15
Remove empty line.
Moe
2017/03/29 17:04:09
Done.
|
+} // namespace |
+ |
+@interface PaymentRequestPickerViewController ()<UITableViewDataSource, |
+ UITableViewDelegate, |
lpromero
2017/03/29 11:38:15
This is already done by the base class.
Moe
2017/03/29 17:04:09
Done.
|
+ UISearchResultsUpdating> |
+ |
+// Search controller that contains search bar. |
+@property(nonatomic, strong) UISearchController* searchController; |
+ |
+// Full data set displayed when tableView is not filtered. |
+@property(nonatomic, strong) NSArray<PickerRow*>* allRows; |
+ |
+// Displayed rows in the tableView. |
+@property(nonatomic, strong) NSArray<PickerRow*>* displayedRows; |
+ |
+// Selected row. |
+@property(nonatomic, strong) PickerRow* selectedRow; |
+ |
+@end |
+ |
+@implementation PaymentRequestPickerViewController |
+@synthesize searchController = _searchController; |
+@synthesize allRows = _allRows; |
+@synthesize displayedRows = _displayedRows; |
+@synthesize selectedRow = _selectedRow; |
+@synthesize delegate = _delegate; |
+ |
+- (instancetype)initWithRows:(NSArray<PickerRow*>*)rows |
+ selected:(PickerRow*)selectedRow { |
+ self = [super initWithStyle:UITableViewStylePlain]; |
+ if (self) { |
+ self.allRows = [rows sortedArrayUsingComparator:^NSComparisonResult( |
+ PickerRow* row1, PickerRow* row2) { |
+ return [row1.label localizedCaseInsensitiveCompare:row2.label]; |
+ }]; |
+ self.selectedRow = selectedRow; |
+ // Default to displaying all the rows. |
+ self.displayedRows = self.allRows; |
+ } |
+ return self; |
+} |
+ |
+#pragma mark - UIViewController |
+ |
+- (void)viewDidLoad { |
+ [super viewDidLoad]; |
+ |
+ self.tableView.rowHeight = MDCCellDefaultOneLineHeight; |
lpromero
2017/03/29 11:38:15
I'd suggest just setting your own value here, not
Moe
2017/03/29 17:04:09
Done.
|
+ self.tableView.accessibilityIdentifier = |
+ kPaymentRequestPickerViewControllerAccessibilityID; |
+ |
+ self.searchController = |
+ [[UISearchController alloc] initWithSearchResultsController:nil]; |
+ self.searchController.searchResultsUpdater = self; |
+ self.searchController.dimsBackgroundDuringPresentation = NO; |
+ self.searchController.searchBar.accessibilityIdentifier = |
+ kPaymentRequestPickerSearchBarAccessibilityID; |
+ self.tableView.tableHeaderView = self.searchController.searchBar; |
+ |
+ // Presentation of searchController will walk up the view controller hierarchy |
+ // until it finds the root view controller or one that defines a presentation |
+ // context. Make this class the presentation context so that the search |
+ // controller does not present on top of the navigation controller. |
+ self.definesPresentationContext = YES; |
+} |
+ |
+#pragma mark - UITableViewDataSource |
+ |
+- (NSArray<NSString*>*)sectionIndexTitlesForTableView:(UITableView*)tableView { |
+ return [self sectionTitles]; |
+} |
+ |
+- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView { |
+ return [[self sectionTitles] count]; |
+} |
+ |
+- (NSString*)tableView:(UITableView*)tableView |
+ titleForHeaderInSection:(NSInteger)section { |
+ return [[self sectionTitles] objectAtIndex:section]; |
+} |
+ |
+- (NSInteger)tableView:(UITableView*)tableView |
+ numberOfRowsInSection:(NSInteger)section { |
+ return [[self rowsInSection:section] count]; |
+} |
+ |
+- (UITableViewCell*)tableView:(UITableView*)tableView |
+ cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
+ UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; |
+ if (!cell) { |
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle |
+ reuseIdentifier:@"cell"]; |
+ } |
+ PickerRow* row = |
+ [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
+ cell.textLabel.text = row.label; |
+ cell.accessoryType = (row == self.selectedRow) |
+ ? UITableViewCellAccessoryCheckmark |
+ : UITableViewCellAccessoryNone; |
+ cell.accessibilityLabel = row.label; |
+ cell.accessibilityIdentifier = |
lpromero
2017/03/29 11:38:15
If you remove the need for the 2 different IDs, yo
Moe
2017/03/29 17:04:09
Done.
|
+ (row == self.selectedRow) |
+ ? kPaymentRequestPickerSelectedRowAccessibilityID |
+ : kPaymentRequestPickerRowAccessibilityID; |
+ return cell; |
+} |
+ |
+#pragma mark - UITableViewDelegate |
+ |
+- (void)tableView:(UITableView*)tableView |
+ didSelectRowAtIndexPath:(NSIndexPath*)indexPath { |
+ if (self.selectedRow) { |
+ NSIndexPath* oldSelectedIndexPath = [self indexPathForRow:self.selectedRow]; |
+ self.selectedRow = nil; |
+ // Reload the previously selected row. |
+ [self.tableView reloadRowsAtIndexPaths:@[ oldSelectedIndexPath ] |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ } |
+ |
+ self.selectedRow = |
+ [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
+ NSIndexPath* newSelectedIndexPath = [self indexPathForRow:self.selectedRow]; |
lpromero
2017/03/29 11:38:15
Isn't this equal to indexPath?
Moe
2017/03/29 17:04:09
Oops. Done.
|
+ // Reload the newly selected row. |
+ [self.tableView reloadRowsAtIndexPaths:@[ newSelectedIndexPath ] |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ |
+ [_delegate paymentRequestPickerViewController:self |
+ didSelectRow:self.selectedRow]; |
+} |
+ |
+#pragma mark - UISearchResultsUpdating |
+ |
+- (void)updateSearchResultsForSearchController: |
+ (UISearchController*)searchController { |
+ [self filterContentForSearchText:searchController.searchBar.text]; |
+} |
+ |
+#pragma mark - Private |
+ |
+// Returns the indexPath for |row| by calculating its section and its index |
+// within the section. |
+- (NSIndexPath*)indexPathForRow:(PickerRow*)row { |
+ NSString* sectionTitle = [self sectionTitleForRow:row]; |
+ NSInteger section = [[self sectionTitles] indexOfObject:sectionTitle]; |
+ DCHECK(section != NSNotFound); |
+ |
+ NSInteger indexInSection = [[self rowsInSection:section] indexOfObject:row]; |
+ DCHECK(indexInSection != NSNotFound); |
+ |
+ return [NSIndexPath indexPathForRow:indexInSection inSection:section]; |
+} |
+ |
+// Returns the titles for the displayed sections in the tableView. |
+- (NSArray<NSString*>*)sectionTitles { |
+ NSMutableOrderedSet<NSString*>* sectionTitles = |
+ [[NSMutableOrderedSet alloc] init]; |
+ [self.displayedRows |
+ enumerateObjectsUsingBlock:^(PickerRow* row, NSUInteger idx, BOOL* stop) { |
+ [sectionTitles addObject:[self sectionTitleForRow:row]]; |
+ }]; |
+ return [sectionTitles array]; |
lpromero
2017/03/29 11:38:15
Should you cache this array? (You'll need to inval
Moe
2017/03/29 17:04:09
I'm now keeping a map of section titles to the sec
|
+} |
+ |
+// Returns the displayed rows in the given section. |
+- (NSArray<PickerRow*>*)rowsInSection:(NSInteger)section { |
+ NSArray<NSString*>* sectionTitles = [self sectionTitles]; |
+ DCHECK(section >= 0 && section < static_cast<NSInteger>(sectionTitles.count)); |
+ |
+ NSString* sectionTitle = [sectionTitles objectAtIndex:section]; |
+ |
+ // Find the rows in the section with the title |sectionTitle| based on the |
+ // labels of the rows. The search is case-insensitive and ignores diacritics. |
+ NSPredicate* prediacate = [NSPredicate |
lpromero
2017/03/29 11:38:15
*predicate
Moe
2017/03/29 17:04:09
Done.
|
+ predicateWithFormat:@"label BEGINSWITH[cd] %@", sectionTitle]; |
+ return [self.displayedRows filteredArrayUsingPredicate:prediacate]; |
lpromero
2017/03/29 11:38:15
idem, should this be cached? It is called several
Moe
2017/03/29 17:04:09
with the map of section titles to the section rows
|
+} |
+ |
+// Returns the title for the section the given row gets added to. The section |
+// title for a row is the capitalized first letter of the label for that row. |
+- (NSString*)sectionTitleForRow:(PickerRow*)row { |
+ return [[row.label substringToIndex:1] uppercaseString]; |
+} |
+ |
+// Filters |allRows| for |searchText| and reloads the tableView. If |searchText| |
+// is empty, tableView will be loaded with |allRows|. |
+- (void)filterContentForSearchText:(NSString*)searchText { |
+ self.displayedRows = self.allRows; |
+ |
+ if (searchText.length != 0) { |
+ // The search is case-insensitive and ignores diacritics. |
+ NSPredicate* prediacate = |
+ [NSPredicate predicateWithFormat:@"label CONTAINS[cd] %@", searchText]; |
+ self.displayedRows = [self.allRows filteredArrayUsingPredicate:prediacate]; |
+ } |
+ |
+ [self.tableView reloadData]; |
lpromero
2017/03/29 11:38:15
Showcase handles that more nicely, as it animates
Moe
2017/03/29 17:04:09
Since we have sections here and they would have to
|
+} |
+ |
+@end |