2015年8月25日 星期二

iOS筆記:UICollectionView & Search Bar (iOS8)

UICollectionView

實現一個UICollectionView和實現一個UITableView基本沒有什麼大區別,它們都同樣是datasource和delegate設計模式的:
datasource為view提供數據源,告訴view要顯示些什麼東西以及如何顯示它們.
delegate提供一些樣式的小細節以及用戶交互的相應.

UICollectionViewDataSource
1. section的數量-numberOfSectionsInCollection:
2. 某個section裡有多少個item -collectionView:numberOfItemsInSection:
3. 對於某個位置應該顯示什麼樣的cell -collectionView:cellForItemAtIndexPath:

為了得到高效的View,對於cell的重用是必須的,避免了不斷生成和銷毀對象的操作,這與在UITableView中的情況是一致的。

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MY_CELL_ID"];  
if (!cell) {    //如果没有可重用的cell,就會生成一個  
    cell = [[UITableViewCell alloc] init]; 
} 
//配置cell.... 
return cell 


- (UICollectionView*)collectionView:(UICollectionView*)cv cellForItemAtIndexPath:(NSIndexPath*)indexPath { 
    MyCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@”MY_CELL_ID”]; 
    // Configure the cell's content 
    cell.imageView.image = ... 
    return cell; 
}

Customizing the Flow Layout Attributes

If all of the items in the collection view are the same size, assign the appropriate width and height values to the itemSize property of the flow layout object. (Always specify the size of items in points.) This is the fastest way to configure the layout object for content whose size does not vary.

If you want to specify different sizes for your cells, you must implement the collectionView:layout:sizeForItemAtIndexPath:method on the collection view delegate. You can use the provided index path information to return the size of the corresponding item. During layout, the flow layout object centers items vertically on the same line. The overall height or width of the line is then determined by the largest item in that dimension.

enter image description here

UICollectionViewDelegateFlowLayout is actually a sub-protocol of UICollectionViewDelegate, so there is no need to list both.

SearchBar Controller

在iOS8中已經將UISearchDisplayController取消了,取而代之的是UISearchController類別. 此類別可以將結果顯示在collection view上.

UISearchController必須符合協定. searchResult陣列負責存放搜尋後的結果. 再來宣告一家UISearchController型態的變數, 負責處理跟搜尋有關的事項.

@interface MovieTableViewController : UITableViewController<...,UISearchResultsUpdating>
{
    NSArray *searchResult;
    UISearchController *searchController;
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    ...
#初始化SearchController, 最後一個參數nil表示搜尋結果會顯示在目前的view上.
searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
#設定哪個controller要負責回應searchBar的更新.
searchController.searchResultsUpdater = self;
#NO表示搜尋時背景不會變暗.
searchController.dimsBackgroundDuringPresentation = NO;

#將searchBar的高度設定為40, searchBar才會出現, 預設為0.0
CGRect rect = searchController.searchBar.frame;
rect.size.height = 40;
searchController.searchBar.frame = rect;

#將searchBar放在tableView的上方.
self.tableView.tableHeaderView = searchController.searchBar;
#YES表示 UISearchController的畫面會覆蓋目前的controller.
self.definesPresentationContext = YES;
}

numberOfSectionsInTableView回傳1, 表示表格中有一個區段.

tableView: numberOfRowsInSection修改為判斷搜尋結果是否為nil.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (searchResult != nil) {
        return [searchResult count];
    }

    return [Movies count];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *movieidentifier = @"MovieListCell";

    MovieTableCell *cell = [tableView dequeueReusableCellWithIdentifier:movieidentifier];

    if (cell == nil) {
        cell = [[MovieTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:movieidentifier];
    }

    if (searchResult!=nil) {
        Movie *movie = [searchResult objectAtIndex:indexPath.row];
        cell.CellImage.image = [UIImage imageNamed:movie.movieImage];
        cell.CellLabel.text = movie.movieName;
        cell.CellTime.text = movie.movieTime;
    }
    else
    {
        Movie *movie = [Movies objectAtIndex:indexPath.row];
        cell.CellImage.image = [UIImage imageNamed:movie.movieImage];
        cell.CellLabel.text = movie.movieName;
        cell.CellTime.text = movie.movieTime;
    }
    return cell;
}

實作 updateSearchResultForSearchController此方法是當使用者點選搜尋列以及輸入資料時會觸發的方法, 因此在這個方法中根據使用者打的字串, 將搜尋結果放到變數 searchResult中, 最後呼叫tableView: reloadData更新表格內容.

NSPredicate:對self每個對象通過謂詞進行篩選,判斷是否與條件相匹配。Predicate中加入[cd],就能找到英文不區分大小寫的字串囉.
Apple 文件關於NSPredicate: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Predicates/Articles/pUsing.html#//apple_ref/doc/uid/TP40001794-250409
範例:

NSMutableArray *array =
    [NSMutableArray arrayWithObjects:@"Nick", @"Ben", @"Adam", @"Melissa", nil];

NSPredicate *sPredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"];
[array filterUsingPredicate:sPredicate];

#Array now contains { @"Nick", @"Ben", @"Melissa" }
If you use the Core Data framework, the array methods provide an efficient means of filtering an existing array of objects without—as a fetch does—requiring a round trip to a persistent data store.
- (void)updateSearchResultsForSearchController:(UISearchController *)mysearchController
{
    if (mysearchController.isActive) {
        NSString *searchString = mysearchController.searchBar.text;

        if ([searchString length] > 0) {
            NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.movieName CONTAINS[cd] %@", searchString];
            searchResult = [Movies filteredArrayUsingPredicate:p];
        } else{
            searchResult = nil;
        }
    } else {
        searchResult = nil;
    }
    [self.tableView reloadData];
}

參考:
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1
http://objccn.io/issue-12-5/
http://www.howzhi.com/group/iosDevelop/discuss/10134
http://stackoverflow.com/questions/7302842/about-searching-by-using-nspredicate

沒有留言:

張貼留言