在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 整潔的 Table View 代碼
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實(shí)踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

整潔的 Table View 代碼

Table view 是 iOS 應(yīng)用程序中非常通用的組件。許多代碼和 table view 都有直接或間接的關(guān)系,隨便舉幾個例子,比如提供數(shù)據(jù)、更新 table view,控制它的行為以及響應(yīng)選擇事件。在這篇文章中,我們將會展示保持 table view 相關(guān)代碼的整潔和良好組織的技術(shù)。

UITableViewController vs. UIViewController

Apple 提供了 UITableViewController 作為 table views 專屬的 view controller 類。Table view controllers 實(shí)現(xiàn)了一些非常有用的特性,來幫你避免一遍又一遍地寫那些死板的代碼!但是話又說回來,table view controller 只限于管理一個全屏展示的 table view。大多數(shù)情況下,這就是你想要的,但如果不是,還有其他方法來解決這個問題,就像下面我們展示的那樣。

Table View Controllers 的特性

Table view controllers 會在第一次顯示 table view 的時候幫你加載其數(shù)據(jù)。另外,它還會幫你切換 table view 的編輯模式、響應(yīng)鍵盤通知、以及一些小任務(wù),比如閃現(xiàn)側(cè)邊的滑動提示條和清除選中時的背景色。為了讓這些特性生效,當(dāng)你在子類中覆寫類似 viewWillAppear: 或者 viewDidAppear: 等事件方法時,需要調(diào)用 super 版本。

Table view controllers 相對于標(biāo)準(zhǔn) view controllers 的一個特別的好處是它支持 Apple 實(shí)現(xiàn)的“下拉刷新”。目前,文檔中唯一的使用 UIRefreshControl 的方式就是通過 table view controller ,雖然通過努力在其他地方也能讓它工作(見此處),但很可能在下一次 iOS 更新的時候就不行了。

這些要素加一起,為我們提供了大部分 Apple 所定義的標(biāo)準(zhǔn) table view 交互行為,如果你的應(yīng)用恰好符合這些標(biāo)準(zhǔn),那么直接使用 table view controllers 來避免寫那些死板的代碼是個很好的方法。

Table View Controllers 的限制

Table view controllers 的 view 屬性永遠(yuǎn)都是一個 table view。如果你稍后決定在 table view 旁邊顯示一些東西(比如一個地圖),如果不依賴于那些奇怪的 hacks,估計(jì)就沒什么辦法了。

如果你是用代碼或 .xib 文件來定義的界面,那么遷移到一個標(biāo)準(zhǔn) view controller 將會非常簡單。但是如果你使用了 storyboards,那么這個過程要多包含幾個步驟。除非重新創(chuàng)建,否則你并不能在 storyboards 中將 table view controller 改成一個標(biāo)準(zhǔn)的 view controller。這意味著你必須將所有內(nèi)容拷貝到新的 view controller,然后再重新連接一遍。

最后,你需要把遷移后丟失的 table view controller 的特性給補(bǔ)回來。大多數(shù)都是 viewWillAppear:viewDidAppear: 中簡單的一條語句。切換編輯模式需要實(shí)現(xiàn)一個 action 方法,用來切換 table view 的 editing 屬性。大多數(shù)工作來自重新創(chuàng)建對鍵盤的支持。

在選擇這條路之前,其實(shí)還有一個更輕松的選擇,它可以通過分離我們需要關(guān)心的功能(關(guān)注點(diǎn)分離),讓你獲得額外的好處:

使用Child View Controllers

和完全拋棄 table view controller 不同,你還可以將它作為 child view controller 添加到其他 view controller 中(關(guān)于此話題的文章)。這樣,parent view controller 在管理其他的你需要的新加的界面元素的同時,table view controller 還可以繼續(xù)管理它的 table view。

- (void)addPhotoDetailsTableView
{
    DetailsViewController *details = [[DetailsViewController alloc] init];
    details.photo = self.photo;
    details.delegate = self;
    [self addChildViewController:details];
    CGRect frame = self.view.bounds;
    frame.origin.y = 110;
    details.view.frame = frame;
    [self.view addSubview:details.view];
    [details didMoveToParentViewController:self];
}

如果你使用這個解決方案,你就必須在 child view controller 和 parent view controller 之間建立消息傳遞的渠道。比如,如果用戶選擇了一個 table view 中的 cell,parent view controller 需要知道這個事件來推入其他 view controller。根據(jù)使用習(xí)慣,通常最清晰的方式是為這個 table view controller 定義一個 delegate protocol,然后到 parent view controller 中去實(shí)現(xiàn)。

@protocol DetailsViewControllerDelegate
- (void)didSelectPhotoAttributeWithKey:(NSString *)key;
@end

@interface PhotoViewController () 
@end

@implementation PhotoViewController
// ...
- (void)didSelectPhotoAttributeWithKey:(NSString *)key
{
    DetailViewController *controller = [[DetailViewController alloc] init];
    controller.key = key;
    [self.navigationController pushViewController:controller animated:YES];
}
@end

就像你看到的那樣,這種結(jié)構(gòu)為 view controller 之間的消息傳遞帶來了額外的開銷,但是作為回報,代碼封裝和分離非常清晰,有更好的復(fù)用性。根據(jù)實(shí)際情況的不同,這既可能讓事情變得更簡單,也可能會更復(fù)雜,需要讀者自行斟酌和決定。

分離關(guān)注點(diǎn)(Separating Concerns)

當(dāng)處理 table views 的時候,有許多各種各樣的任務(wù),這些任務(wù)穿梭于 models,controllers 和 views 之間。為了避免讓 view controllers 做所有的事,我們將盡可能地把這些任務(wù)劃分到合適的地方,這樣有利于閱讀、維護(hù)和測試。

這里描述的技術(shù)是文章更輕量的 View Controllers 中的概念的延伸,請參考這篇文章來理解如何重構(gòu) data source 和 model 的邏輯。結(jié)合 table views,我們來具體看看如何在 view controllers 和 views 之間分離關(guān)注點(diǎn)。

搭建 Model 對象和 Cells 之間的橋梁

有時我們需要將想顯示的 model 層中的數(shù)據(jù)傳到 view 層中去顯示。由于我們同時也希望讓 model 和 view 之間明確分離,所以通常把這個任務(wù)轉(zhuǎn)移到 table view 的 data source 中去處理:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PhotoCell"];
    Photo *photo = [self itemAtIndexPath:indexPath];
    cell.photoTitleLabel.text = photo.name;
    NSString* date = [self.dateFormatter stringFromDate:photo.creationDate];
    cell.photoDateLabel.text = date;
}

但是這樣的代碼會讓 data source 變得混亂,因?yàn)樗?data source 暴露了 cell 的設(shè)計(jì)。最好分解出來,放到 cell 類的一個 category 中。

@implementation PhotoCell (ConfigureForPhoto)

- (void)configureForPhoto:(Photo *)photo
{
    self.photoTitleLabel.text = photo.name;
    NSString* date = [self.dateFormatter stringFromDate:photo.creationDate];
    self.photoDateLabel.text = date;
}

@end

有了上述代碼后,我們的 data source 方法就變得簡單了。

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCell *cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier];
    [cell configureForPhoto:[self itemAtIndexPath:indexPath]];
    return cell;
}

在我們的示例代碼中,table view 的 data source 已經(jīng)分解到單獨(dú)的類中了,它用一個設(shè)置 cell 的 block 來初始化。這時,這個 block 就變得這樣簡單了:

TableViewCellConfigureBlock block = ^(PhotoCell *cell, Photo *photo) {
    [cell configureForPhoto:photo];
};

讓 Cells 可復(fù)用

有時多種 model 對象需要用同一類型的 cell 來表示,這種情況下,我們可以進(jìn)一步讓 cell 可以復(fù)用。首先,我們給 cell 定義一個 protocol,需要用這個 cell 顯示的對象必須遵循這個 protocol。然后簡單修改 category 中的設(shè)置方法,讓它可以接受遵循這個 protocol 的任何對象。這些簡單的步驟讓 cell 和任何特殊的 model 對象之間得以解耦,讓它可適應(yīng)不同的數(shù)據(jù)類型。

在 Cell 內(nèi)部控制 Cell 的狀態(tài)

如果你想自定義 table views 默認(rèn)的高亮或選擇行為,你可以實(shí)現(xiàn)兩個 delegate 方法,把點(diǎn)擊的 cell 修改成我們想要的樣子。例如:

- (void)tableView:(UITableView *)tableView
        didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.photoTitleLabel.shadowColor = [UIColor darkGrayColor];
    cell.photoTitleLabel.shadowOffset = CGSizeMake(3, 3);
}

- (void)tableView:(UITableView *)tableView
        didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    PhotoCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.photoTitleLabel.shadowColor = nil;
}

然而,這兩個 delegate 方法的實(shí)現(xiàn)又基于了 view controller 知曉 cell 實(shí)現(xiàn)的具體細(xì)節(jié)。如果我們想替換或重新設(shè)計(jì) cell,我們必須改寫 delegate 代碼。View 的實(shí)現(xiàn)細(xì)節(jié)和 delegate 的實(shí)現(xiàn)交織在一起了。我們應(yīng)該把這些細(xì)節(jié)移到 cell 自身中去。

@implementation PhotoCell
// ...
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
    [super setHighlighted:highlighted animated:animated];
    if (highlighted) {
        self.photoTitleLabel.shadowColor = [UIColor darkGrayColor];
        self.photoTitleLabel.shadowOffset = CGSizeMake(3, 3);
    } else {
        self.photoTitleLabel.shadowColor = nil;
    }
}
@end

總的來說,我們在努力把 view 層和 controller 層的實(shí)現(xiàn)細(xì)節(jié)分離開。delegate 肯定得清楚一個 view 該顯示什么狀態(tài),但是它不應(yīng)該了解如何修改 view 結(jié)構(gòu)或者給某些 subviews 設(shè)置某些屬性以獲得正確的狀態(tài)。所有這些邏輯都應(yīng)該封裝到 view 內(nèi)部,然后給外部提供一個簡單地 API。

控制多個 Cell 類型

如果一個 table view 里面有多種類型的 cell,data source 方法很快就難以控制了。在我們示例程序中,photo details table 有兩種不同類型的 cell:一種用于顯示幾個星,另一種用來顯示一個鍵值對。為了劃分處理不同 cell 類型的代碼,data source 方法簡單地通過判斷 cell 的類型,把任務(wù)派發(fā)給其他指定的方法。

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *key = self.keys[(NSUInteger) indexPath.row];
    id value = [self.photo valueForKey:key];
    UITableViewCell *cell;
    if ([key isEqual:PhotoRatingKey]) {
        cell = [self cellForRating:value indexPath:indexPath];
    } else {
        cell = [self detailCellForKey:key value:value];
    }
    return cell;
}

- (RatingCell *)cellForRating:(NSNumber *)rating
                    indexPath:(NSIndexPath *)indexPath
{
    // ...
}

- (UITableViewCell *)detailCellForKey:(NSString *)key
                                value:(id)value
{
    // ...
}

編輯 Table View

Table view 提供了易于使用的編輯特性,允許你對 cell 進(jìn)行刪除或重新排序。這些事件都可以讓 table view 的 data source 通過 delegate 方法得到通知。因此,通常我們能在這些 delegate 方法中看到對數(shù)據(jù)的進(jìn)行修改的操作。

修改數(shù)據(jù)很明顯是屬于 model 層的任務(wù)。Model 應(yīng)該為諸如刪除或重新排序等操作暴露一個 API,然后我們可以在 data source 方法中調(diào)用它。這樣,controller 就可以扮演 view 和 model 之間的協(xié)調(diào)者,而不需要知道 model 層的實(shí)現(xiàn)細(xì)節(jié)。并且還有額外的好處,model 的邏輯也變得更容易測試,因?yàn)樗辉俸?view controllers 的任務(wù)混雜在一起了。

總結(jié)

Table view controllers(以及其他的 controller 對象?。?yīng)該在 model 和 view 對象之間扮演協(xié)調(diào)者和調(diào)解者的角色。它不應(yīng)該關(guān)心明顯屬于 view 層或 model 層的任務(wù)。你應(yīng)該始終記住這點(diǎn),這樣 delegate 和 data source 方法會變得更小巧,最多包含一些簡單地樣板代碼。

這不僅減少了 table view controllers 那樣的大小和復(fù)雜性,而且還把業(yè)務(wù)邏輯和 view 的邏輯放到了更合適的地方。Controller 層的里里外外的實(shí)現(xiàn)細(xì)節(jié)都被封裝成了簡單地 API,最終,它變得更加容易理解,也更利于團(tuán)隊(duì)協(xié)作。

擴(kuò)展閱讀