OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/payments/payment_request_picker_view_controller.h" |
| 6 |
| 7 #import "base/logging.h" |
| 8 #import "base/mac/foundation_util.h" |
| 9 #import "ios/chrome/browser/payments/payment_request_picker_row.h" |
| 10 |
| 11 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 12 #error "This file requires ARC support." |
| 13 #endif |
| 14 |
| 15 NSString* const kPaymentRequestPickerRowAccessibilityID = |
| 16 @"kPaymentRequestPickerRowAccessibilityID"; |
| 17 NSString* const kPaymentRequestPickerSearchBarAccessibilityID = |
| 18 @"kPaymentRequestPickerSearchBarAccessibilityID"; |
| 19 |
| 20 namespace { |
| 21 NSString* const kPaymentRequestPickerViewControllerAccessibilityID = |
| 22 @"kPaymentRequestPickerViewControllerAccessibilityID"; |
| 23 } // namespace |
| 24 |
| 25 @interface PaymentRequestPickerViewController ()<UISearchResultsUpdating> |
| 26 |
| 27 // Search controller that contains search bar. |
| 28 @property(nonatomic, strong) UISearchController* searchController; |
| 29 |
| 30 // Full data set displayed when tableView is not filtered. |
| 31 @property(nonatomic, strong) NSArray<PickerRow*>* allRows; |
| 32 |
| 33 // Displayed rows in the tableView. |
| 34 @property(nonatomic, strong) NSArray<PickerRow*>* displayedRows; |
| 35 |
| 36 // Selected row. |
| 37 @property(nonatomic, strong) PickerRow* selectedRow; |
| 38 |
| 39 @property(nonatomic, strong) |
| 40 NSDictionary<NSString*, NSArray<PickerRow*>*>* sectionTitleToSectionRowsMap; |
| 41 |
| 42 @end |
| 43 |
| 44 @implementation PaymentRequestPickerViewController |
| 45 @synthesize searchController = _searchController; |
| 46 @synthesize allRows = _allRows; |
| 47 @synthesize displayedRows = _displayedRows; |
| 48 @synthesize selectedRow = _selectedRow; |
| 49 @synthesize sectionTitleToSectionRowsMap = _sectionTitleToSectionRowsMap; |
| 50 @synthesize delegate = _delegate; |
| 51 |
| 52 - (instancetype)initWithRows:(NSArray<PickerRow*>*)rows |
| 53 selected:(PickerRow*)selectedRow { |
| 54 self = [super initWithStyle:UITableViewStylePlain]; |
| 55 if (self) { |
| 56 self.allRows = [rows sortedArrayUsingComparator:^NSComparisonResult( |
| 57 PickerRow* row1, PickerRow* row2) { |
| 58 return [row1.label localizedCaseInsensitiveCompare:row2.label]; |
| 59 }]; |
| 60 self.selectedRow = selectedRow; |
| 61 // Default to displaying all the rows. |
| 62 self.displayedRows = self.allRows; |
| 63 } |
| 64 return self; |
| 65 } |
| 66 |
| 67 - (void)setDisplayedRows:(NSArray<PickerRow*>*)displayedRows { |
| 68 _displayedRows = displayedRows; |
| 69 |
| 70 // Update the mapping from section titles to rows in that section, for |
| 71 // currently displayed rows. |
| 72 [self updateSectionTitleToSectionRowsMap]; |
| 73 } |
| 74 |
| 75 #pragma mark - UIViewController |
| 76 |
| 77 - (void)viewDidLoad { |
| 78 [super viewDidLoad]; |
| 79 |
| 80 self.tableView.rowHeight = 48.0f; // The same as MDCCellDefaultOneLineHeight. |
| 81 self.tableView.accessibilityIdentifier = |
| 82 kPaymentRequestPickerViewControllerAccessibilityID; |
| 83 |
| 84 self.searchController = |
| 85 [[UISearchController alloc] initWithSearchResultsController:nil]; |
| 86 self.searchController.searchResultsUpdater = self; |
| 87 self.searchController.dimsBackgroundDuringPresentation = NO; |
| 88 self.searchController.searchBar.accessibilityIdentifier = |
| 89 kPaymentRequestPickerSearchBarAccessibilityID; |
| 90 self.tableView.tableHeaderView = self.searchController.searchBar; |
| 91 |
| 92 // Presentation of searchController will walk up the view controller hierarchy |
| 93 // until it finds the root view controller or one that defines a presentation |
| 94 // context. Make this class the presentation context so that the search |
| 95 // controller does not present on top of the navigation controller. |
| 96 self.definesPresentationContext = YES; |
| 97 } |
| 98 |
| 99 #pragma mark - UITableViewDataSource |
| 100 |
| 101 - (NSArray<NSString*>*)sectionIndexTitlesForTableView:(UITableView*)tableView { |
| 102 return [self sectionTitles]; |
| 103 } |
| 104 |
| 105 - (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView { |
| 106 return [[self sectionTitles] count]; |
| 107 } |
| 108 |
| 109 - (NSString*)tableView:(UITableView*)tableView |
| 110 titleForHeaderInSection:(NSInteger)section { |
| 111 return [[self sectionTitles] objectAtIndex:section]; |
| 112 } |
| 113 |
| 114 - (NSInteger)tableView:(UITableView*)tableView |
| 115 numberOfRowsInSection:(NSInteger)section { |
| 116 return [[self rowsInSection:section] count]; |
| 117 } |
| 118 |
| 119 - (UITableViewCell*)tableView:(UITableView*)tableView |
| 120 cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
| 121 UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; |
| 122 if (!cell) { |
| 123 cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle |
| 124 reuseIdentifier:@"cell"]; |
| 125 cell.isAccessibilityElement = YES; |
| 126 cell.accessibilityIdentifier = kPaymentRequestPickerRowAccessibilityID; |
| 127 } |
| 128 PickerRow* row = |
| 129 [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
| 130 cell.textLabel.text = row.label; |
| 131 cell.accessoryType = (row == self.selectedRow) |
| 132 ? UITableViewCellAccessoryCheckmark |
| 133 : UITableViewCellAccessoryNone; |
| 134 if (row == self.selectedRow) |
| 135 cell.accessibilityTraits |= UIAccessibilityTraitSelected; |
| 136 else |
| 137 cell.accessibilityTraits &= ~UIAccessibilityTraitSelected; |
| 138 |
| 139 return cell; |
| 140 } |
| 141 |
| 142 #pragma mark - UITableViewDelegate |
| 143 |
| 144 - (void)tableView:(UITableView*)tableView |
| 145 didSelectRowAtIndexPath:(NSIndexPath*)indexPath { |
| 146 if (self.selectedRow) { |
| 147 NSIndexPath* oldSelectedIndexPath = [self indexPathForRow:self.selectedRow]; |
| 148 self.selectedRow = nil; |
| 149 // Reload the previously selected row if it is displaying. |
| 150 if (oldSelectedIndexPath) { |
| 151 [self.tableView reloadRowsAtIndexPaths:@[ oldSelectedIndexPath ] |
| 152 withRowAnimation:UITableViewRowAnimationFade]; |
| 153 } |
| 154 } |
| 155 |
| 156 self.selectedRow = |
| 157 [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
| 158 // Reload the newly selected row. |
| 159 [self.tableView reloadRowsAtIndexPaths:@[ indexPath ] |
| 160 withRowAnimation:UITableViewRowAnimationFade]; |
| 161 |
| 162 [_delegate paymentRequestPickerViewController:self |
| 163 didSelectRow:self.selectedRow]; |
| 164 } |
| 165 |
| 166 #pragma mark - UISearchResultsUpdating |
| 167 |
| 168 - (void)updateSearchResultsForSearchController: |
| 169 (UISearchController*)searchController { |
| 170 NSString* searchText = searchController.searchBar.text; |
| 171 |
| 172 // Filter |allRows| for |searchText| and reload the tableView. If |searchText| |
| 173 // is empty, tableView will be loaded with |allRows|. |
| 174 if (searchText.length != 0) { |
| 175 // The search is case-insensitive and ignores diacritics. |
| 176 NSPredicate* predicate = |
| 177 [NSPredicate predicateWithFormat:@"label CONTAINS[cd] %@", searchText]; |
| 178 self.displayedRows = [self.allRows filteredArrayUsingPredicate:predicate]; |
| 179 } else { |
| 180 self.displayedRows = self.allRows; |
| 181 } |
| 182 |
| 183 [self.tableView reloadData]; |
| 184 } |
| 185 |
| 186 #pragma mark - Private |
| 187 |
| 188 // Creates a mapping from section titles to rows in that section, for currently |
| 189 // displaying rows, and updates |sectionTitleToSectionRowsMap|. |
| 190 - (void)updateSectionTitleToSectionRowsMap { |
| 191 NSMutableDictionary<NSString*, NSArray<PickerRow*>*>* |
| 192 sectionTitleToSectionRowsMap = [[NSMutableDictionary alloc] init]; |
| 193 |
| 194 for (PickerRow* row in self.displayedRows) { |
| 195 NSString* sectionTitle = [self sectionTitleForRow:row]; |
| 196 NSMutableArray<PickerRow*>* sectionRows = |
| 197 base::mac::ObjCCastStrict<NSMutableArray<PickerRow*>>( |
| 198 sectionTitleToSectionRowsMap[sectionTitle]); |
| 199 if (!sectionRows) |
| 200 sectionRows = [[NSMutableArray alloc] init]; |
| 201 [sectionRows addObject:row]; |
| 202 [sectionTitleToSectionRowsMap setObject:sectionRows forKey:sectionTitle]; |
| 203 } |
| 204 |
| 205 self.sectionTitleToSectionRowsMap = sectionTitleToSectionRowsMap; |
| 206 } |
| 207 |
| 208 // Returns the indexPath for |row| by calculating its section and its index |
| 209 // within the section. Returns nil if the row is not currently displaying. |
| 210 - (NSIndexPath*)indexPathForRow:(PickerRow*)row { |
| 211 NSString* sectionTitle = [self sectionTitleForRow:row]; |
| 212 |
| 213 NSInteger section = [[self sectionTitles] indexOfObject:sectionTitle]; |
| 214 if (section == NSNotFound) |
| 215 return nil; |
| 216 |
| 217 NSInteger indexInSection = |
| 218 [self.sectionTitleToSectionRowsMap[sectionTitle] indexOfObject:row]; |
| 219 if (indexInSection == NSNotFound) |
| 220 return nil; |
| 221 |
| 222 return [NSIndexPath indexPathForRow:indexInSection inSection:section]; |
| 223 } |
| 224 |
| 225 // Returns the titles for the displayed sections in the tableView. |
| 226 - (NSArray<NSString*>*)sectionTitles { |
| 227 return [[self.sectionTitleToSectionRowsMap allKeys] |
| 228 sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; |
| 229 } |
| 230 |
| 231 // Returns the displayed rows in the given section. |
| 232 - (NSArray<PickerRow*>*)rowsInSection:(NSInteger)section { |
| 233 NSArray<NSString*>* sectionTitles = [self sectionTitles]; |
| 234 DCHECK(section >= 0 && section < static_cast<NSInteger>(sectionTitles.count)); |
| 235 |
| 236 NSString* sectionTitle = [sectionTitles objectAtIndex:section]; |
| 237 |
| 238 return self.sectionTitleToSectionRowsMap[sectionTitle]; |
| 239 } |
| 240 |
| 241 // Returns the title for the section the given row gets added to. The section |
| 242 // title for a row is the capitalized first letter of the label for that row. |
| 243 - (NSString*)sectionTitleForRow:(PickerRow*)row { |
| 244 return [[row.label substringToIndex:1] uppercaseString]; |
| 245 } |
| 246 |
| 247 - (NSString*)description { |
| 248 return kPaymentRequestPickerViewControllerAccessibilityID; |
| 249 } |
| 250 |
| 251 @end |
OLD | NEW |