爲什麼要把搜索欄單獨寫,主要是這裏牽涉到一個深層可變副本。在這裏爲什麼要用這個~~~~你迷茫嗎?
我也很迷茫哈哈~~~~~~
該項目是請一個項目的加強版,雖然只是多了一個搜索控件,可是卻多了許多步驟。
上次公司需要添加一個字段。也就是在數據庫中新增一個字段。我排的時間相對長了一點,受到衆人笑話。可是當你對系統的複雜性瞭解後,你會知道特別時在數據庫增加字段時帶來了很多的問題。並且我這個字段幾乎整個項目的所有表都要增加。因爲每張表的關聯性,並且有些地方要添加,有的地方使用存儲過程,有的是創建臨時表,如果要時間短,是可以完成,但是誰有知道那些地方沒有修改,當過了一段時間後,就會有一段垃圾數據出現,那時也只有去修改數據庫了,哈哈廢話一段!!!!也許還是我的能力有限。
前面我使用了多數組的字段,其中字母表中的每個字母都佔用一個數組。該字典是不可改變的。這就意味着不能從字典添加和刪除值。它包含的數組也是如此。所以要創建兩個字典,一個包含完整數據集的不可改變的字典,一個可以從中刪除行的可變的字典副本。
複習一下,淺層複製和深層複製
淺層複製:不復制引用對象,新複製的對象值指向現有的引用對象。
深層複製:將複製所用的引用對象。
NSDictionary遵循NSMutableCopying協議,該方法創建的是一個淺層副本。但是引用對象是不能刪除的,所以是無法刪除對象的。需要一個類別去存放數組的字典的副本。
添加類別後 項目導航變成
在頭文件中做一個接口返回NSMutbleDictionary類型的方法
#import <Foundation/Foundation.h>
@interface NSDictionary (MutableDeepCopy)
-(NSMutableDictionary *) mutableDeepCopy;
@end
- (NSMutableDictionary *)mutableDeepCopy {
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc]
initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys) {
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:@selector(mutableDeepCopy)])
oneCopy = [oneValue mutableDeepCopy];
else if ([oneValue respondsToSelector:@selector(mutableCopy)])
oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
oneCopy = [oneValue copy];
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
用一個數組存儲這個這個字典。遍歷並且判斷對象如果沒有響應mutableDeepCopy消息,那麼它將創建可變副本,否則就創建常規副本。for (id key in keys)稱爲快速枚舉類似C#中的foreach()方法。NSDictionary 、NSArray、NSSet都支持快速枚舉。
現在頭文件修改爲
#import <UIKit/UIKit.h>
@interface BIDViewController :UIViewController
<UITableViewDataSource,UITableViewDelegate>
@property (strong,nonatomic) IBOutlet UITableView *table; //指向輸出口視圖
@property (strong,nonatomic) IBOutlet UISearchBar *search; //指向輸出口搜索欄
@property (strong,nonatomic) NSDictionary *allNames; //字典存放所有的數據集
@property (strong,nonatomic) NSMutableDictionary *names; //將存有那些與當前搜索條件匹配的數據集
@property (strong,nonatomic) NSMutableArray *keys; //將存有索引值和分區名稱
@property (assign, nonatomic) BOOL isSearching; //是否在使用搜索欄
-(void)resetSearch; //複製數據
-(void)handleSearchForTerm:(NSString *)searchTerm; //實現搜索的方法
@end
實現方法如下
#import "BIDViewController.h"
#import "NSDictionary+MutableDeepCopy.h"
@implementation BIDViewController
@synthesize table;
@synthesize search;
@synthesize allNames;
@synthesize names;
@synthesize keys;
@synthesize isSearching;
#pragma mark
#pragma mark Custom Methods
-(void)resetSearch
{
self.names =[self.allNames mutableDeepCopy];
NSMutableArray *keyArray= [[NSMutableArray alloc] init];
[keyArray addObject:UITableViewIndexSearch];
[keyArray addObjectsFromArray:[[self.allNames allKeys] sortedArrayUsingSelector:@selector(compare:)]];
self.keys=keyArray;
}
-(void) handleSearchForTerm:(NSString *)searchTerm
{
NSMutableArray *sectionsToRemove=[[NSMutableArray alloc] init];//創建一個數組,存放我們找到的空分區。
[self resetSearch];
for(NSString *key in self.keys)
{
NSMutableArray *array=[names valueForKey:key];//存放需要從names數組中刪除的值的數組
NSMutableArray *toRemove=[[NSMutableArray alloc]init];
for(NSString *name in array)
{
//循環使用一個字符串中子字符串位置的NSString的方法。並且返回一個NSRange結構,如果返回的包含了NSNotFound就添加到要刪除的對象數組中
if ([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location == NSNotFound)
{
[toRemove addObject:name];
}
}
if ([array count]==[toRemove count]) {
[sectionsToRemove addObject:key];
}
[array removeObjectsInArray:toRemove];//從此分區中刪除不匹配的對稱
}
//刪除空分區 並告知重新加載數據
[self.keys removeObjectsInArray:sectionsToRemove];
//[sectionsToRemove release];
[table reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
//初始化數據
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *path= [[NSBundle mainBundle] pathForResource:@"sortednames"ofType:@"plist"];
NSDictionary *dict = [[NSDictionary alloc]initWithContentsOfFile:path];
self.allNames=dict;
[self resetSearch];
[table reloadData];
[table setContentOffset:CGPointMake(0.0,44.0) animated:NO];//設置表中內容的偏移量
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.names=nil;
self.keys=nil;
self.allNames=nil;
self.table=nil;
self.search=nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation !=UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark
#pragma mark Table View Data Source Methods
//指定分區的數量
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ([keys count])>0?[keys count]:1;
}
//用於計算特定分區中的行數,檢索與討論中的分區對應的數組。並從該數組中返回行的數量。
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([keys count]==0) {
return 0;
}
NSString *key =[keys objectAtIndex:section];
NSArray *nameSection=[names objectForKey:key];
return [nameSection count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section=[indexPath section];
NSUInteger row=[indexPath row];
NSString *key =[keys objectAtIndex:section];
NSArray *nameSection=[names objectForKey:key];
static NSString *SectionsTableIdentifier=@"SectionsTableIdentifiler";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:SectionsTableIdentifier];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionsTableIdentifier];
}
cell.textLabel.text=[nameSection objectAtIndex:row];
return cell;
}
//爲每個分區指定一個可選的標題值,然後只返回這一組的字母就可以了
-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ([keys count]==0) {
return nil;
}
NSString *key=[keys objectAtIndex:section];
if (key==UITableViewIndexSearch) {
return nil;
}
return key;
}
//添加索引的方法
-(NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView
{
if (isSearching)
return nil;
return keys;
}
#pragma mark -
#pragma mark Table View Delegate Methods
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[search resignFirstResponder];//如果用戶在使用搜索欄時單擊一行,我們希望鍵盤不再起作用。
isSearching = NO;
search.text = @"";
[tableView reloadData];
return indexPath;
}
#pragma mark -
#pragma mark Search Bar Delegate Methods
//當用戶單擊鍵盤上的返回按鈕或搜索按鈕時,調用,此方法從搜索欄獲取搜索短語。並調用搜索方法。
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSString *searchTerm = [searchBar text];
[self handleSearchForTerm:searchTerm];
}
//實時搜索,只要搜索欄中的短語發生變化都重新搜索,這個是需要設備的高性能。
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchTerm {
if ([searchTerm length] == 0) {
[self resetSearch];
[table reloadData];
return;
}
[self handleSearchForTerm:searchTerm];
}
//取消按鈕的觸發的事件
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
isSearching = NO;
search.text = @"";
[self resetSearch];
[table reloadData];
[searchBar resignFirstResponder];
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
isSearching = YES;
[table reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView
sectionForSectionIndexTitle:(NSString *)title
atIndex:(NSInteger)index {
NSString *key = [keys objectAtIndex:index];
if (key == UITableViewIndexSearch) {
[tableView setContentOffset:CGPointZero animated:NO];
return NSNotFound;
} else return index;
}
@end
提到一個搜索的放大器功能。
3個步驟:
a。向keys數組添加一個特殊值以指示我們需要一個放大鏡。
-(void)resetSearch
{
self.names =[self.allNames mutableDeepCopy];
NSMutableArray *keyArray= [[NSMutableArray alloc] init];
[keyArray addObject:UITableViewIndexSearch];
[keyArray addObjectsFromArray:[[self.allNames allKeys] sortedArrayUsingSelector:@selector(compare:)]];
self.keys=keyArray;
}
b。必須阻止IOS在表格中顯示該特殊值的部分標題。
-(NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if ([keys count]==0) {
return nil;
}
NSString *key=[keys objectAtIndex:section];
if (key==UITableViewIndexSearch) {
return nil;
}
return key;
}
c。告訴表格在該項被選中時滾至頂部。
- (NSInteger)tableView:(UITableView *)tableView
sectionForSectionIndexTitle:(NSString *)title
atIndex:(NSInteger)index {
NSString *key = [keys objectAtIndex:index];
if (key == UITableViewIndexSearch) {
[tableView setContentOffset:CGPointZero animated:NO];
return NSNotFound;
} else return index;
}
遇到很多問題。
1。在視圖頁面的時候,沒有在UIView中添加一個View,直接將Search Bar 拖進去。無法修改其長度。
2。 self.names =[self.allNames mutableDeepCopy];的時候mutableCopy,報數組不能變成可變的。