TableView之Component

auu.space

這種模式是我在上家公司的項(xiàng)目里用到的一種方式,整體來說就像是拼積木一樣把各個(gè)展示組件盏道、響應(yīng)事件拆分出去猜嘱,作為一個(gè)單獨(dú)的類朗伶,然后通過工廠模式生產(chǎn)出來再組裝展示步咪。

image

在這里,我們把每個(gè)最小的封裝組件叫做一個(gè)Component点晴,每個(gè)事件的響應(yīng)動(dòng)作都叫做一個(gè)Action悯周。

約定

這種模式對(duì)于后臺(tái)的依賴性很強(qiáng),前端只需要封裝好不同的小組件和大概的框架即可屠橄,由后臺(tái)提供來規(guī)定布局的樣式和響應(yīng)的事件,所以礁哄,前后端就必須有一個(gè)完全統(tǒng)一的約定溪北。
下面是json數(shù)據(jù)格式:

{
    "component": {
        # 這種類型下的數(shù)據(jù)資源之拨,當(dāng)然也能`component`嵌套
        
        "action": {
            # 響應(yīng)動(dòng)作
        },
        # 組件類型
        "componentType": "word"
    },
}

舉個(gè)例子敦锌,這是評(píng)論內(nèi)容的一個(gè)視圖:

      {
        "component" : {
          "action" : {
            "actionType" : "detail",
            "clearMsgNum" : "1",
            "flag" : "-3",
            "go_comments" : "1",
            "id" : "480151",
            "title" : "釋然",
            "type" : "thread"
          },
          "componentType" : "postsNewMessage",
          "datetime" : "2016-11-02 17:44:26",
          "description" : "峨眉山,云中花嶺",
          "messageCount" : "1",
          "messageGroupId" : "480151",
          "name" : "釋然",
          "picUrl" : "http://s3.mingxingyichu.cn/group6/M00/92/78/wKgBjFUDbQaADktKAAXsk4b7S1s41.jpeg?imageMogr2/quality/95",
          "postSummary" : "http://ww2.sinaimg.cn/large/006AYr4pjw1f8s239mmx0j30m80de0ti.jpg",
          "userFansNum" : "6"
        },
        "message_type" : "thread_msg"
      }

我們?cè)谇岸藢⒛K分為了很多種颖变,包括文字模塊腥刹、圖片模塊衔峰、圖文混排模塊垫卤、視頻播放模塊出牧、商品模塊舔痕、推薦模塊、標(biāo)簽?zāi)K等等數(shù)十上百個(gè)小模塊慨代,然后通過不同的嵌套達(dá)到頁面展示的目的侍匙,如圖(截取的是蘑菇街的圖片组底,我們的APP貌似下架了):

image

創(chuàng)建基類

因?yàn)樗械目煞庋b的組件都是約定好的,所以可以做一個(gè)基類對(duì)設(shè)定的數(shù)據(jù)做統(tǒng)一的分離提取

  • Component
@interface CustomComponent : UIView

@property (nonatomic,strong) CustomAction *firedAction;
@property (nonatomic, weak) NSDictionary *detailStars;
@property (nonatomic, retain) NSDictionary  *data;

-(id) initWithFrame:(CGRect)frame data:(NSDictionary *)data;
-(id) initWithContainer:(UIView *)container data:(NSDictionary *)data;
-(void) initUI;

-(CGFloat)getComponentHeightWithData:(NSDictionary *)data withRealWidth:(CGFloat)realWidth;

//點(diǎn)擊action
-(void)fireAction;
-(void)fireActions:(NSInteger)index;
-(void)fireActionWith:(NSDictionary *)data;

@end

這里的點(diǎn)擊事件或者根據(jù)Action自定義的事件是加在單獨(dú)的Component里铛纬,一般都是加的一個(gè)點(diǎn)擊的手勢(shì)告唆,其他的根據(jù)需求來做修改晶密。
里面的具體實(shí)現(xiàn)就是View的定制,這里就不舉例了懂牧。

  • Action
@interface CustomAction : NSObject

// 統(tǒng)計(jì)信息
@property (nonatomic, copy) NSDictionary *trackEventInfor;  

-(id)initWith:(NSDictionary *)data;
-(void)initData;
-(void)fire;

@end

如視頻播放的事件:

@implementation ActionVideo

- (void)fire {
    [super fire];
    NSString *videoUrl = [_data objectForKey:@"videoUrl"];
    if (![NSString isBlankString:videoUrl]) {
        NSURL *movieURL = [NSURL URLWithString:videoUrl];
        AutoRatoteMPMoviePlayerViewController *controller = [[AutoRatoteMPMoviePlayerViewController alloc] initWithContentURL:movieURL];
        controller.delegateController = self.delegateNavigationController;
        
        [controller play];
    }
}

@end

創(chuàng)建工廠類

  • ComponentFactory

用于根據(jù)給定的數(shù)據(jù)創(chuàng)建每個(gè)小的組件

@implementation ComponentFactory

+(CustomComponent *)createComponentWithFrame:(CGRect)frame data:(NSDictionary *)data navigation:(UINavigationController *)delegteNavigarionController{
    
    NSString * componentStr = [ComponentFactory getComponentTypeWithData:data];
    Class someClass = NSClassFromString(componentStr);
    CustomComponent *cell = (CustomComponent *)[[someClass alloc] initWithFrame:frame data:data];
    
    cell.delegateNavigationController = delegteNavigarionController;
    return cell;
}

+(CGFloat )getComponentHeightWithData:(NSDictionary *)data withRealWidth:(CGFloat)realWidth{
    // 根據(jù)給定的數(shù)據(jù)內(nèi)容來計(jì)算當(dāng)前模塊的高度
}

+(NSString *)getComponentTypeWithData:(NSDictionary *)data{
    NSString * componentStr = @"CustomComponent";
    NSString *componentType = data[@"component"][@"componentType"];
    
    if([@"word" isEqual:componentType]){
        componentStr = @"ComponentWord";
    }
    if([@"videoCell" isEqual:componentType]){
        componentStr = @"ComponentVideoCell";
    }
    if([@"calendar" isEqual:componentType]){
        componentStr = @"ComponentCalendar";
    }
    if ([@"calendarWorthy" isEqualToString:componentType]) {
        componentStr = @"ComponentCalendarWorthy";
    }
    
    // ...

    return componentStr;
}

+(CustomComponent *)createComponentWithContainer:(UIView *)view data:(NSDictionary *)data navigation:(UINavigationController *)delegteNavigarionController{
    CGRect frame = view.bounds;
    CustomComponent *cell = [ComponentFactory createComponentWithFrame:frame data:data navigation:delegteNavigarionController];
    return cell;
}

@end

可以看出躯保,這里主要是把if-else的類型判斷挪到這里來了途事。

  • ActionFactory
@implementation ActionFactory

+(CustomAction *)createActionWithData:(NSDictionary *)data withDetaiStarData:(NSDictionary *)starDic navigation:(UINavigationController *)delegateNavigationController{
    CustomAction *action;
    if (![data isKindOfClass:[NSDictionary class]]) {
        return nil;
    }
    NSString *actionType = data[@"actionType"];
    if (!actionType) {
        return nil;
    }
    
    NSString *type = data[@"type"];
    NSString *child = data[@"child"];
    
    if ([@"livingShow" isEqualToString:actionType]) {   //直播播放
        action = [[ActionLiveShow alloc] initWith:data];
    }
    if ([@"thread" isEqualToString:actionType]) {
        if ([@"" isEqualToString:child]) {// 帖子列表
            action = [[ActionThread alloc] initWith:data];
        }
        if ([@"topiclist" isEqualToString:child]) {// 專題列表
            action = [[ActionTopicList alloc] initWith:data];
        }
    }
    
    if ([@"detail" isEqual:actionType]) {
        // 帖子詳情頁
        if ([@"thread" isEqual:type]) {
            action = [[ActionThreadDetail alloc]initWith:data];
        }
        // 用戶空間詳情頁
        if ([@"user" isEqual:type]) {
            action=[[ActionSpace alloc] initWith:data];
        }
    }
    if ([@"list" isEqualToString:actionType]) {
        if ([@"msg" isEqualToString:type]) {    //消息回復(fù)我的尸变,社區(qū)通知振惰,活動(dòng)通知
            action = [[ActionReplyMine alloc] initWith:data];
        }
        if ([@"msgEvent" isEqualToString:type]) {
            action = [[ActionActivityNoticeDetail alloc]initWith:data];
        }
    }

    return action;
}

@end

這里也是根據(jù)給定的數(shù)據(jù)來創(chuàng)建具體事件的實(shí)例。

頁面使用實(shí)例

當(dāng)然痛垛,由于數(shù)據(jù)的結(jié)構(gòu)化匙头,這完全可以做一層封裝蹂析。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _data.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSDictionary *dic = self.data[indexPath.row];
    NSInteger cellHeight = [ComponentFactory getComponentHeightWithData:dic withRealWidth:self.tableView.frame.size.width];
    return cellHeight;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomComponent *component;
    NSInteger row = [indexPath row];
    NSDictionary *data = _data[row];
    NSString * identifier= [NSString stringWithFormat:@"reuse%@", data[@"componentType"]];
    
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    
    if(cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
        
        CGFloat cellHeight = [ComponentFactory getComponentHeightWithData:data withRealWidth:tableView.frame.size.width];
        CGRect frame = CGRectMake(0, 0, tableView.frame.size.width, cellHeight);
        component = [ComponentFactory createComponentWithFrame:frame data:data navigation:self.navigationController];
        cell.component = component;
    }
    
    component.data = data;
    component.row = row;
    
    return cell;
}

總結(jié)

這種模式很適合閱讀內(nèi)容的展示竖共,特別是文章類型的頁面俺祠,由于頁面的展示樣式有后臺(tái)控制公给,所以就提供了更多可定制的可能性。
上面也只是一種粗糙的代碼展示蜘渣,要想深究淌铐,也可以在很多細(xì)節(jié)上做優(yōu)化。
不過缺點(diǎn)也很明顯蔫缸,后臺(tái)依賴性很強(qiáng)腿准,而且表格視圖的交互性不好,對(duì)于內(nèi)容的更改拾碌、cell位置的調(diào)整都不方便吐葱。
總之來說,這也是對(duì)于某種需求而產(chǎn)生的一種書寫的方式倦沧,找對(duì)應(yīng)用場(chǎng)景唇撬,做好優(yōu)化,這也會(huì)給我們的APP提供豐富的功能和開發(fā)體驗(yàn)展融。

數(shù)據(jù)的依賴性太強(qiáng)窖认,而且有原來項(xiàng)目現(xiàn)成的代碼燕偶,就懶得寫demo了酝惧,將就著看吧晚唇。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闽瓢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子届谈,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡挽唉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門严拒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事劈彪。” “怎么了滔吠?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵嚣潜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我喜德,道長(zhǎng)涣澡,這世上最難降的妖魔是什么奄薇? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮沮脖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘饼酿。我一直安慰自己紊婉,他們只是感情好刚陡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布蝙云。 她就那樣靜靜地躺著身隐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐籽,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音布近,去河邊找鬼预伺。 笑死骆撇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼雄右!你這毒婦竟也來了肋坚?” 一聲冷哼從身側(cè)響起铣鹏,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤脊髓,失蹤者是張志新(化名)和其女友劉穎痰腮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阱高,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年未舟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菩掏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片智绸。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖珍语,靈堂內(nèi)的尸體忽然破棺而出是偷,到底是詐尸還是另有隱情洪燥,我是刑警寧澤芒篷,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布搜变,位于F島的核電站,受9級(jí)特大地震影響针炉,放射性物質(zhì)發(fā)生泄漏挠他。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一篡帕、第九天 我趴在偏房一處隱蔽的房頂上張望殖侵。 院中可真熱鬧,春花似錦镰烧、人聲如沸拢军。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茉唉。三九已至,卻和暖如春结执,著一層夾襖步出監(jiān)牢的瞬間度陆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工昌犹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坚芜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓斜姥,卻偏偏與公主長(zhǎng)得像鸿竖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铸敏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355