Index: ios/showcase/core/showcase_view_controller.mm |
diff --git a/ios/showcase/core/showcase_view_controller.mm b/ios/showcase/core/showcase_view_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6348724556f20b24c33717301955d0d0de71adf7 |
--- /dev/null |
+++ b/ios/showcase/core/showcase_view_controller.mm |
@@ -0,0 +1,202 @@ |
+// Copyright 2016 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/showcase/core/showcase_view_controller.h" |
+ |
+#import "base/logging.h" |
+#import "ios/showcase/common/coordinator.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+namespace showcase { |
+ |
+NSString* const kClassForDisplayKey = @"classForDisplay"; |
+NSString* const kClassForInstantiationKey = @"classForInstantiation"; |
+NSString* const kUseCaseKey = @"useCase"; |
+ |
+} // namespace showcase |
+ |
+@interface ShowcaseViewController ()<UITableViewDataSource, |
+ UITableViewDelegate, |
+ 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<showcase::ModelRow*>* allRows; |
+ |
+// Displayed rows in tableView. |
+@property(nonatomic, strong) NSArray<showcase::ModelRow*>* displayedRows; |
+ |
+// Selected coordinator. |
+@property(nonatomic, strong) id<Coordinator> activeCoordinator; |
+ |
+@end |
+ |
+@implementation ShowcaseViewController |
+@synthesize searchController = _searchController; |
+@synthesize allRows = _allRows; |
+@synthesize displayedRows = _displayedRows; |
+@synthesize activeCoordinator = _activeCoordinator; |
+ |
+- (instancetype)initWithRows:(NSArray<showcase::ModelRow*>*)rows { |
+ self = [super initWithStyle:UITableViewStylePlain]; |
+ if (self) { |
+ self.allRows = [rows copy]; |
+ // Default to displaying all rows. |
+ self.displayedRows = self.allRows; |
+ } |
+ return self; |
+} |
+ |
+#pragma mark - UIViewController |
+ |
+- (void)viewDidLoad { |
+ [super viewDidLoad]; |
+ |
+ self.title = @"Showcase"; |
+ self.tableView.tableFooterView = [[UIView alloc] init]; |
+ self.tableView.rowHeight = 70.0; |
+ |
+ self.searchController = |
+ [[UISearchController alloc] initWithSearchResultsController:nil]; |
+ self.searchController.searchResultsUpdater = self; |
+ self.searchController.dimsBackgroundDuringPresentation = NO; |
+ 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; |
+} |
+ |
+- (void)viewDidAppear:(BOOL)animated { |
+ [super viewDidAppear:animated]; |
+ // Resets the current coordinator whenever the navigation controller pops |
+ // back to this view controller. |
+ self.activeCoordinator = nil; |
+} |
+ |
+#pragma mark - Table view data source |
+ |
+- (NSInteger)tableView:(UITableView*)tableView |
+ numberOfRowsInSection:(NSInteger)section { |
+ return self.displayedRows.count; |
+} |
+ |
+- (UITableViewCell*)tableView:(UITableView*)tableView |
+ cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
+ UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; |
+ if (!cell) { |
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle |
+ reuseIdentifier:@"cell"]; |
+ cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
+ } |
+ showcase::ModelRow* row = self.displayedRows[indexPath.row]; |
+ cell.textLabel.text = row[showcase::kClassForDisplayKey]; |
+ cell.detailTextLabel.text = row[showcase::kUseCaseKey]; |
+ return cell; |
+} |
+ |
+#pragma mark - Table view delegate |
+ |
+- (void)tableView:(UITableView*)tableView |
+ didSelectRowAtIndexPath:(NSIndexPath*)indexPath { |
+ showcase::ModelRow* row = self.displayedRows[indexPath.row]; |
+ Class classForInstantiation = |
+ NSClassFromString(row[showcase::kClassForInstantiationKey]); |
+ if ([classForInstantiation isSubclassOfClass:[UIViewController class]]) { |
+ UIViewController* viewController = [[classForInstantiation alloc] init]; |
+ viewController.title = row[showcase::kUseCaseKey]; |
+ [self.navigationController pushViewController:viewController animated:YES]; |
+ } else if ([classForInstantiation |
+ conformsToProtocol:@protocol(Coordinator)]) { |
+ self.activeCoordinator = [[classForInstantiation alloc] init]; |
+ self.activeCoordinator.baseViewController = self.navigationController; |
+ [self.activeCoordinator start]; |
+ } else { |
+ NOTREACHED(); |
+ } |
+} |
+ |
+#pragma mark - UISearchResultsUpdating |
+ |
+- (void)updateSearchResultsForSearchController: |
+ (UISearchController*)searchController { |
+ [self filterContentForSearchText:searchController.searchBar.text]; |
+} |
+ |
+#pragma mark - Private |
+ |
+// Filters |allRows| for |searchText| and reloads the tableView with |
+// animation. If |searchText| is empty, tableView will be loaded with |allRows|. |
+- (void)filterContentForSearchText:(NSString*)searchText { |
+ NSArray<showcase::ModelRow*>* newFilteredRows = self.allRows; |
+ |
+ if (![self.searchController.searchBar.text isEqualToString:@""]) { |
+ // The search is case-insensitive and searches both displayed texts. |
+ NSIndexSet* matchingRows = |
+ [self.allRows indexesOfObjectsPassingTest:^BOOL( |
+ showcase::ModelRow* row, NSUInteger idx, BOOL* stop) { |
+ return [row[showcase::kClassForDisplayKey] |
+ localizedCaseInsensitiveContainsString:searchText] || |
+ [row[showcase::kUseCaseKey] |
+ localizedCaseInsensitiveContainsString:searchText]; |
+ }]; |
+ newFilteredRows = [self.allRows objectsAtIndexes:matchingRows]; |
+ } |
+ |
+ NSArray<NSIndexPath*>* indexPathsToInsert = |
+ [self indexPathsToInsertFromArray:self.displayedRows |
+ toArray:newFilteredRows |
+ indexPathSection:0]; |
+ NSArray<NSIndexPath*>* indexPathsToDelete = |
+ [self indexPathsToDeleteFromArray:self.displayedRows |
+ toArray:newFilteredRows |
+ indexPathSection:0]; |
+ |
+ [self.tableView beginUpdates]; |
+ [self.tableView insertRowsAtIndexPaths:indexPathsToInsert |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ [self.tableView deleteRowsAtIndexPaths:indexPathsToDelete |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ self.displayedRows = newFilteredRows; |
+ [self.tableView endUpdates]; |
+} |
+ |
+// Returns indexPaths that need to be inserted into the tableView. |
+- (NSArray<NSIndexPath*>*)indexPathsToInsertFromArray:(NSArray*)fromArray |
+ toArray:(NSArray*)toArray |
+ indexPathSection:(NSUInteger)section { |
+ NSMutableArray<NSIndexPath*>* indexPathsToInsert = |
+ [[NSMutableArray alloc] init]; |
+ for (NSUInteger row = 0; row < toArray.count; row++) { |
+ if (![fromArray containsObject:toArray[row]]) { |
+ [indexPathsToInsert |
+ addObject:[NSIndexPath indexPathForRow:row inSection:section]]; |
+ } |
+ } |
+ return indexPathsToInsert; |
+} |
+ |
+// Returns indexPaths that need to be deleted from the tableView. |
+- (NSArray<NSIndexPath*>*)indexPathsToDeleteFromArray:(NSArray*)fromArray |
+ toArray:(NSArray*)toArray |
+ indexPathSection:(NSUInteger)section { |
+ NSMutableArray<NSIndexPath*>* indexPathsToDelete = |
+ [[NSMutableArray alloc] init]; |
+ for (NSUInteger row = 0; row < fromArray.count; row++) { |
+ if (![toArray containsObject:fromArray[row]]) { |
+ [indexPathsToDelete |
+ addObject:[NSIndexPath indexPathForRow:row inSection:section]]; |
+ } |
+ } |
+ return indexPathsToDelete; |
+} |
+ |
+@end |