소스 검색

Search bar filtering and sorting by file size in the file browser

DaidoujiChen 12 년 전
부모
커밋
77fcccc546

+ 25 - 0
Classes/Global State Explorers/FLEXFileBrowserSearchOperation.h

@@ -0,0 +1,25 @@
+//
+//  FLEXFileBrowserSearchOperation.h
+//  UICatalog
+//
+//  Created by 啟倫 陳 on 2014/8/4.
+//  Copyright (c) 2014年 f. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol FLEXFileBrowserSearchOperationDelegate;
+
+@interface FLEXFileBrowserSearchOperation : NSOperation
+
+@property (nonatomic, weak) id<FLEXFileBrowserSearchOperationDelegate> delegate;
+
+- (id)initWithPath:(NSString *)currentPath searchString:(NSString *)searchString;
+
+@end
+
+@protocol FLEXFileBrowserSearchOperationDelegate <NSObject>
+
+- (void)fileBrowserSearchOperationResult:(NSArray *)searchResult size:(uint64_t)size;
+
+@end

+ 123 - 0
Classes/Global State Explorers/FLEXFileBrowserSearchOperation.m

@@ -0,0 +1,123 @@
+//
+//  FLEXFileBrowserSearchOperation.m
+//  UICatalog
+//
+//  Created by 啟倫 陳 on 2014/8/4.
+//  Copyright (c) 2014年 f. All rights reserved.
+//
+
+#import "FLEXFileBrowserSearchOperation.h"
+
+@implementation NSMutableArray (FLEXStack)
+
+- (void)flex_push:(id)anObject
+{
+    [self addObject:anObject];
+}
+
+- (id)flex_pop
+{
+    id anObject = [self lastObject];
+    [self removeLastObject];
+    return anObject;
+}
+
+@end
+
+@interface FLEXFileBrowserSearchOperation ()
+
+@property (nonatomic, strong) NSString *path;
+@property (nonatomic, strong) NSString *searchString;
+
+@end
+
+@implementation FLEXFileBrowserSearchOperation
+
+#pragma mark - private
+
+- (uint64_t)totalSizeAtPath:(NSString *)path
+{
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:NULL];
+    uint64_t totalSize = [attributes fileSize];
+    
+    for (NSString *fileName in [fileManager enumeratorAtPath:path]) {
+        attributes = [fileManager attributesOfItemAtPath:[path stringByAppendingPathComponent:fileName] error:NULL];
+        totalSize += [attributes fileSize];
+    }
+    return totalSize;
+}
+
+#pragma mark - instance method
+
+- (id)initWithPath:(NSString *)currentPath searchString:(NSString *)searchString
+{
+    self = [super init];
+    if (self) {
+        self.path = currentPath;
+        self.searchString = searchString;
+    }
+    return self;
+}
+
+#pragma mark - methods to override
+
+- (void)main
+{
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSMutableArray *searchPaths = [NSMutableArray array];
+    NSMutableDictionary *sizeMapping = [NSMutableDictionary dictionary];
+    uint64_t totalSize = 0;
+    NSMutableArray *stack = [NSMutableArray array];
+    [stack flex_push:self.path];
+    
+    //recursive found all match searchString paths, and precomputing there size
+    while ([stack count]) {
+        NSString *currentPath = [stack flex_pop];
+        NSArray *directoryPath = [fileManager contentsOfDirectoryAtPath:currentPath error:nil];
+        
+        for (NSString *subPath in directoryPath) {
+            NSString *fullPath = [currentPath stringByAppendingPathComponent:subPath];
+            
+            if ([[subPath lowercaseString] rangeOfString:[self.searchString lowercaseString]].location != NSNotFound) {
+                [searchPaths addObject:fullPath];
+                if (!sizeMapping[fullPath]) {
+                    uint64_t fullPathSize = [self totalSizeAtPath:fullPath];
+                    totalSize += fullPathSize;
+                    [sizeMapping setObject:@(fullPathSize) forKey:fullPath];
+                }
+            }
+            BOOL isDirectory;
+            if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory] && isDirectory) {
+                [stack flex_push:fullPath];
+            }
+            
+            if ([self isCancelled]) {
+                return;
+            }
+        }
+    }
+    
+    //sort
+    NSArray *sortedArray = [searchPaths sortedArrayUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) {
+        uint64_t pathSize1 = [sizeMapping[path1] unsignedLongLongValue];
+        uint64_t pathSize2 = [sizeMapping[path2] unsignedLongLongValue];
+        if (pathSize1 < pathSize2) {
+            return NSOrderedAscending;
+        } else if (pathSize1 > pathSize2) {
+            return NSOrderedDescending;
+        } else {
+            return NSOrderedSame;
+        }
+    }];
+    
+    if ([self isCancelled]) {
+        return;
+    }
+    
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [self.delegate fileBrowserSearchOperationResult:sortedArray size:totalSize];
+    });
+}
+
+@end

+ 3 - 1
Classes/Global State Explorers/FLEXFileBrowserTableViewController.h

@@ -8,7 +8,9 @@
 
 #import <UIKit/UIKit.h>
 
-@interface FLEXFileBrowserTableViewController : UITableViewController
+#import "FLEXFileBrowserSearchOperation.h"
+
+@interface FLEXFileBrowserTableViewController : UITableViewController <UISearchDisplayDelegate, FLEXFileBrowserSearchOperationDelegate>
 
 - (id)initWithPath:(NSString *)path;
 

+ 90 - 11
Classes/Global State Explorers/FLEXFileBrowserTableViewController.m

@@ -15,7 +15,11 @@
 
 @property (nonatomic, copy) NSString *path;
 @property (nonatomic, copy) NSArray *childPaths;
+@property (nonatomic, strong) NSArray *searchPaths;
 @property (nonatomic, strong) NSNumber *recursiveSize;
+@property (nonatomic, strong) NSNumber *searchPathsSize;
+@property (nonatomic, strong) UISearchDisplayController *searchController;
+@property (nonatomic) NSOperationQueue *operationQueue;
 
 @end
 
@@ -32,10 +36,21 @@
     if (self) {
         self.path = path;
         self.title = [path lastPathComponent];
+        self.operationQueue = [NSOperationQueue new];
         
+        //add search controller
+        UISearchBar *searchBar = [UISearchBar new];
+        [searchBar sizeToFit];
+        self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
+        self.searchController.delegate = self;
+        self.searchController.searchResultsDataSource = self;
+        self.searchController.searchResultsDelegate = self;
+        self.tableView.tableHeaderView = self.searchController.searchBar;
+        
+        //computing path size
         FLEXFileBrowserTableViewController *__weak weakSelf = self;
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            NSFileManager *fileManager = [[NSFileManager alloc] init];
+            NSFileManager *fileManager = [NSFileManager defaultManager];
             NSDictionary *attributes = [fileManager attributesOfItemAtPath:path error:NULL];
             uint64_t totalSize = [attributes fileSize];
             
@@ -61,6 +76,37 @@
     return self;
 }
 
+#pragma mark - FLEXFileBrowserSearchOperationDelegate
+
+- (void)fileBrowserSearchOperationResult:(NSArray *)searchResult size:(uint64_t)size
+{
+    self.searchPaths = searchResult;
+    self.searchPathsSize = @(size);
+    [self.searchController.searchResultsTableView reloadData];
+}
+
+#pragma mark - UISearchDisplayDelegate
+
+- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
+{
+    self.searchPaths = nil;
+    self.searchPathsSize = nil;
+    
+    //clear pre search request and start a new one
+    [self.operationQueue cancelAllOperations];
+    FLEXFileBrowserSearchOperation *newOperation = [[FLEXFileBrowserSearchOperation alloc] initWithPath:self.path searchString:searchString];
+    newOperation.delegate = self;
+    [self.operationQueue addOperation:newOperation];
+
+    return YES;
+}
+
+- (void)searchDisplayController:(UISearchDisplayController *)controller willHideSearchResultsTableView:(UITableView *)tableView
+{
+    //confirm to clear all operations
+    [self.operationQueue cancelAllOperations];
+}
+
 
 #pragma mark - Table view data source
 
@@ -71,25 +117,49 @@
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {
-    return [self.childPaths count];
+    if (tableView == self.tableView) {
+        return [self.childPaths count];
+    } else {
+        return [self.searchPaths count];
+    }
 }
 
 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
 {
+    NSNumber *currentSize;
+    NSArray *currentPaths;
+    
+    if (tableView == self.tableView) {
+        currentSize = self.recursiveSize;
+        currentPaths = self.childPaths;
+    } else {
+        currentSize = self.searchPathsSize;
+        currentPaths = self.searchPaths;
+    }
+    
     NSString *sizeString = nil;
-    if (!self.recursiveSize) {
+    if (!currentSize) {
         sizeString = @"Computing size…";
     } else {
-        sizeString = [NSByteCountFormatter stringFromByteCount:[self.recursiveSize longLongValue] countStyle:NSByteCountFormatterCountStyleFile];
+        sizeString = [NSByteCountFormatter stringFromByteCount:[currentSize longLongValue] countStyle:NSByteCountFormatterCountStyleFile];
     }
     
-    return [NSString stringWithFormat:@"%lu files (%@)", (unsigned long)[self.childPaths count], sizeString];
+    return [NSString stringWithFormat:@"%lu files (%@)", (unsigned long)[currentPaths count], sizeString];
 }
 
 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 {
-    NSString *subpath = [self.childPaths objectAtIndex:indexPath.row];
-    NSString *fullPath = [self.path stringByAppendingPathComponent:subpath];
+    NSString *subpath;
+    NSString *fullPath;
+    
+    if (tableView == self.tableView) {
+        subpath = [self.childPaths objectAtIndex:indexPath.row];
+        fullPath = [self.path stringByAppendingPathComponent:subpath];
+    } else {
+        fullPath = [self.searchPaths objectAtIndex:indexPath.row];
+        subpath = [fullPath lastPathComponent];
+    }
+    
     NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:NULL];
     BOOL isDirectory = [[attributes fileType] isEqual:NSFileTypeDirectory];
     NSString *subtitle = nil;
@@ -108,7 +178,7 @@
     // Separate image and text only cells because otherwise the separator lines get out-of-whack on image cells reused with text only.
     BOOL showImagePreview = [FLEXUtility isImagePathExtension:[fullPath pathExtension]];
     NSString *cellIdentifier = showImagePreview ? imageCellIdentifier : textCellIdentifier;
-
+    
     if (!cell) {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
         cell.textLabel.font = [FLEXUtility defaultTableViewCellLabelFont];
@@ -116,7 +186,7 @@
         cell.detailTextLabel.textColor = [UIColor grayColor];
         cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
     }
-    NSString *cellTitle = [subpath lastPathComponent];
+    NSString *cellTitle = [fullPath lastPathComponent];
     cell.textLabel.text = cellTitle;
     cell.detailTextLabel.text = subtitle;
     
@@ -130,8 +200,17 @@
 
 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 {
-    NSString *subpath = [self.childPaths objectAtIndex:indexPath.row];
-    NSString *fullPath = [self.path stringByAppendingPathComponent:subpath];
+    NSString *subpath;
+    NSString *fullPath;
+    
+    if (tableView == self.tableView) {
+        subpath = [self.childPaths objectAtIndex:indexPath.row];
+        fullPath = [self.path stringByAppendingPathComponent:subpath];
+    } else {
+        fullPath = [self.searchPaths objectAtIndex:indexPath.row];
+        subpath = [fullPath lastPathComponent];
+    }
+    
     BOOL isDirectory = NO;
     BOOL stillExists = [[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDirectory];
     if (stillExists) {

+ 6 - 0
Example/UICatalog.xcodeproj/project.pbxproj

@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		0149BFE2198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0149BFE1198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.m */; };
 		01985ABC1984DE9500A65332 /* FLEXArgumentInputFontsPickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 01985ABB1984DE9500A65332 /* FLEXArgumentInputFontsPickerView.m */; };
 		3EC6487318FF8A5000024205 /* ReadMe.txt in Resources */ = {isa = PBXBuildFile; fileRef = 3EC6487218FF8A5000024205 /* ReadMe.txt */; };
 		5356823E18F3656900BAAD62 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5356823D18F3656900BAAD62 /* Foundation.framework */; };
@@ -94,6 +95,8 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		0149BFE0198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXFileBrowserSearchOperation.h; sourceTree = "<group>"; };
+		0149BFE1198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXFileBrowserSearchOperation.m; sourceTree = "<group>"; };
 		01985ABA1984DE9500A65332 /* FLEXArgumentInputFontsPickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLEXArgumentInputFontsPickerView.h; sourceTree = "<group>"; };
 		01985ABB1984DE9500A65332 /* FLEXArgumentInputFontsPickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLEXArgumentInputFontsPickerView.m; sourceTree = "<group>"; };
 		3EC6487218FF8A5000024205 /* ReadMe.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ReadMe.txt; sourceTree = SOURCE_ROOT; };
@@ -545,6 +548,8 @@
 				944F7475197B458C009AB039 /* FLEXClassesTableViewController.m */,
 				944F7476197B458C009AB039 /* FLEXFileBrowserTableViewController.h */,
 				944F7477197B458C009AB039 /* FLEXFileBrowserTableViewController.m */,
+				0149BFE0198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.h */,
+				0149BFE1198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.m */,
 				944F7478197B458C009AB039 /* FLEXGlobalsTableViewController.h */,
 				944F7479197B458C009AB039 /* FLEXGlobalsTableViewController.m */,
 				944F747A197B458C009AB039 /* FLEXInstancesTableViewController.h */,
@@ -734,6 +739,7 @@
 				535682A918F3670300BAAD62 /* AAPLAlertViewController.m in Sources */,
 				944F74A6197B458C009AB039 /* FLEXIvarEditorViewController.m in Sources */,
 				944F748C197B458C009AB039 /* FLEXDescriptionTableViewCell.m in Sources */,
+				0149BFE2198FAFC700B90A1B /* FLEXFileBrowserSearchOperation.m in Sources */,
 				535682B218F3670300BAAD62 /* AAPLMasterViewController.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;