在咱們iOS開發中UITableView
幾乎是全部App都會使用的一個UI控件,由於業務的須要,咱們經常會註冊多種Cell,而後在html
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
複製代碼
中就會很天然的寫出一堆相似這樣的代碼:git
事件處理的代碼大概是這樣的: github
這彷佛沒有什麼問題,代碼很乾淨,邏輯也比較清晰。數組
可是你維護幾個版本以後,或者遇到了一個善變的產品經理。網絡
你會發現,這樣的代碼維護起來真的很危險,稍微一不注意就出錯了,這裏用的type
做爲判斷條件可能相較與indexPath
要好一點。app
若是使用indexPath
做爲判斷條件,若是你的cell順序有變化,或者有改動,那麼你可能至少須要維護如下幾個地方:ide
維護的東西越多,意味着你出錯的機率是越大的。ui
那有沒有什麼好的方法處理這類代碼?atom
其實咱們仔細想一想,不管一個多麼複雜的UITableView
,與之對應的其實只要一個模型數組。spa
那麼咱們若是維護好了模型數組,是否是就維護好了UITableView
中全部的cell,這是顯而易見的。
若是咱們的UITableView
中有N種cell樣式,那麼模型數組中確定也會有N種模型。
也就是說每種cell與每種模型是一一配對的,常規的模型與cell綁定是如上述的思路。
上述的思路,顯然不是咱們想要的,維護起來太不便,並且耦合性也比較大。
想想展現一個UITableView
的過程
大體就是這三步吧。
其實在第二步構造模型數組時,咱們是否是就能夠肯定好UI的樣式了?
若是這裏想不明白,再看看咱們上面的分析,一種cell樣式對應着一種模型,那麼咱們知道了模型,是否是就知道了cell樣式
若是你仍是不大清楚,那們就進入實戰部分
先看這樣一個簡單的頁面,你確定會說:朋友,你TM在逗咱們,這和UITableView
有毛關係?
這個界面須要UITableView
?
沒錯,這個界面在UIViewController
中直接構建就能夠了。
是否是感受都很相似,可是又有不少不一樣的地方。
缺點:
有不少重複代碼,並且後期的改動須要維護的地方,作不到高內聚。
抽象一個父類
缺點:
雖然三個VC看似UI上有不少共同之處,可是其中的業務處理徹底不一樣的
抽象一個UIHelper用於構建UI
缺點:
這種方案看似很好了,可是你看若是在一個界面中,若是添加一個或者減小一個控件,又得從新作約束了,這也顯然不是咱們想要的。
下面看看經過UITableView
構建的UI
SignInVC 中的代碼:
PasswordSignVC 中的代碼:
再看cell的dequeue代碼
數據的綁定,所有分散到了每一個cell中。
Row.h的代碼
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol Updatable <NSObject>
@optional
- (void)updateViewData:(id)viewData;
@end
@interface Row : NSObject
@property(nonatomic, copy, readonly) NSString *reuseIdentifier;
@property(nonatomic, strong, readonly) Class cellClass;
@property(nonatomic, strong, readonly) id model;
- (instancetype)initWithClass:(Class)cls model:(id)model;
- (instancetype)initWithClass:(Class)cls;
- (void)updateCell:(UITableViewCell *)cell;
@end
NS_ASSUME_NONNULL_END
複製代碼
Row.m的代碼
@interface Row()
@property(nonatomic, strong, readwrite) Class cellClass;
@property(nonatomic, strong, readwrite) id model;
@end
@implementation Row
- (instancetype)initWithClass:(Class)cls {
if (self = [self initWithClass:cls model:@""]) {
}
return self;
}
- (instancetype)initWithClass:(Class)cls model:(id)model {
if (self = [super init]) {
self.cellClass = cls;
self.model = model;
}
return self;
}
- (void)updateCell:(UITableViewCell *)cell {
if ([cell respondsToSelector:@selector(updateViewData:)]) {
[cell performSelector:@selector(updateViewData:) withObject:self.model];
}
}
- (NSString *)reuseIdentifier {
return [NSString stringWithFormat:@"%@", self.cellClass];
}
@end
複製代碼
整個Row的代碼不過100行,把全部的處理都內聚在了一塊兒,咱們只要維護好模型數組就能很好的管理UITableView
在iOS8以後UITableView
中推出了Self-sizing的功能,因此Cell的高度改變
UIView *dummyView = [[UIView alloc] init];
dummyView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView insertSubview:dummyView belowSubview:self.textField];
[dummyView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor].active = YES;
[dummyView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor].active = YES;
NSLayoutConstraint *constraint = [dummyView.heightAnchor constraintEqualToConstant:60];
constraint.priority = 999;
constraint.active = YES;
複製代碼
若是你對這塊不熟悉,請跳轉。若是你想對Auto Layout有一個提升建議看看Auto Layout Guide, 若是你想知道
systemLayoutSizeFittingSize
的做用,請看 深刻理解Auto Layout 第一彈
有人確定會不屑這裏,可是我想說:若是不用block、代理、觀察者。
怎麼把cell上button的事件回調到VC中(button沒有暴露給外部)?
咱們先看添加Action的方法
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
複製代碼
這裏須要這三個參數:
只要咱們找到了target,把action寫到target裏這個action綁定是否是就完成了。
target其實就是咱們的VC,咱們只要把VC傳遞給Cell便可,可是這樣是否是Cell又和VC耦合了啊。這個用block,delegate沒什麼區別吧!
如今咱們須要解決的問題就是找到Cell的VC,大功便可告成。
這是就須要一個重要的概念閃亮登場iOS響應鏈(Responder Chain)
這裏就不展開了,可是你必定要去了解這個。
響應鏈能夠解決的問題:
找到UIView
的UIViewController
- (UIViewController *)viewController {
UIResponder *responder = self;
while (![responder isKindOfClass:[UIViewController class]]) {
responder = [responder nextResponder];
if (nil == responder) {
break;
}
}
return (UIViewController *)responder;
}
複製代碼
ButtonCell事件綁定代碼:
這裏咱們仍是要用一個協議的:
用這個協議主要是方便代碼的閱讀,並且在Swift中是必須使用協議的,由於編譯時找不到這個方法。
能夠看到ButtonCell的代碼中並無這樣一段代碼
@property (nonatomic, weak) id<ButtonCellActionable> delegate;
複製代碼
或者
@property (nonatomic, strong) void (^buttonAction)(void);
複製代碼
這樣咱們的ButtonCell不會和VC耦合,修改起來真的很方便
以上思路大概就介紹完了,這只是Detail部分,List部分我會在demo中給出
關於Detail和List的概念我會在第三節中介紹,第二節是Swift版的思路,Swift能夠用到泛型,代碼更優雅。