iOS實錄1:UITableView構(gòu)建UI界面

[這是第一篇]

導(dǎo)語:UITableView是iOS項目中使用相關(guān)廣泛的UI組件腌零,本文主要從構(gòu)建界面方案的實現(xiàn)梯找、TableViewCell之間的通信Native動態(tài)化頁面的野望(挖坑)三部分討論了如何使用TableView構(gòu)建比較常見的UI界面益涧。

一锈锤、概述

1、問題#####
  • 在iOS的開發(fā)中闲询,有大量的UI工作久免,這些UI工作簡言之就是在畫“頁面”,“頁面”的復(fù)雜度有高有低扭弧,復(fù)雜度高的比如繪制直播畫面(聊天列表阎姥、點贊飛花、送禮物鸽捻、廣告UI等)呼巴,復(fù)雜度低的比如繪制一個關(guān)于頁面,一個logo和幾行文本就搞定了御蒲。

  • 而我們在實際工作中遇到的UI工作大部分是中等復(fù)雜度的頁面衣赶,比如某某列表頁、某某詳情頁厚满,在繪制頁面的同時府瞄,還需要響應(yīng)頁面上的操作。

  • 其實碘箍,很多頁面都是可以利用UITableView來構(gòu)建出來的遵馆。

2、優(yōu)勢#####

采用分而治之的辦法敲街,將一個整體頁面劃分成若干部分团搞,每一個部分就相當一類Cell,其實這么做是有好處的多艇,好處如下:

  • 提高部分UI組件的復(fù)用性。尤其在同一個App中像吻,甚至一個App中的同一功能模塊中峻黍,有些UI是相同的,是可以復(fù)用的拨匆。

  • 支持數(shù)據(jù)驅(qū)動UI的展示姆涩,在頁面數(shù)據(jù)變化的情況下,UI也能發(fā)生對應(yīng)的變化惭每。

  • 在繪制同一個頁面時候骨饿,有利于多人合作亏栈,每個人可以負責(zé)一個頁面中不同的部分,且在開發(fā)過程中宏赘,不受其他人的工作進度影響绒北。

3、解決方案
  • 封裝TableViewController察署,凡是想使用UITableView構(gòu)建頁面的Controller都需要繼承該類闷游。

  • 支持UI組件響應(yīng)的的處理。

  • 使用通知中心或其他方案來完成Cell之間的通信贴汪。

  • 支持UITableView的上拉加載和下拉刷新脐往,一般列表頁面比較需要這兩個功能。

二扳埂、 構(gòu)建界面方案的實現(xiàn)####

1业簿、QSTableViewCell 和 QSTableViewCellModel
  • QSTableViewCell是對UITableViewCell的封裝,而QSTableViewCellModel是對QSTableViewCell上數(shù)據(jù)和UI響應(yīng)動作的描述阳懂。

  • QSTableViewCell定義了其子類需要實現(xiàn)的辦法辖源,如如數(shù)據(jù)更新布局,cellHeight處理希太,點擊cell的處理等主要方法克饶。

     #pragma mark - QSTableViewCell
    @interface QSTableViewCell : UITableViewCell
    
    #pragma mark - 子類需重寫
    - (void)layoutWithModel:(id)model;
    
    + (CGFloat)cellHeightWithModel:(id)model;
    
    - (void)onTapCellAction;
    
    @end
    
  • QSTableViewCellModel定義了對應(yīng)Cell類的類名厂榛、展示需要的數(shù)據(jù)恨锚, 以及響應(yīng)UI操作點擊的動作,我們將響應(yīng)UI操作的動作封裝在Block中惊完。

    #pragma mark - QSTableViewCellModel
    @interface QSTableViewCellModel : NSObject
    
    @property (nonatomic,copy)NSString  *cellClassName;
    @property (nonatomic,copy)QSTableViewCellActionBlock tapCellBlock;
    @property (nonatomic,assign)CGFloat cellHeight;
    @property (nonatomic,strong)id userInfo;
    
    @end
    

說明:cellClassName屬性保存對應(yīng)的Cell類的類名堕澄,可以利用反射來實現(xiàn)cell的創(chuàng)建邀跃。

2、實現(xiàn)UITableView代理方法

選擇在TableViewController中實現(xiàn)UITableView的UITableViewDelegate蛙紫、UITableViewDataSource的代理方法拍屑。在TableViewController中有頂一個dataSource數(shù)組,這個數(shù)組中存放的各個Cell對應(yīng)的CellModel坑傅,利用cellModel中的信息僵驰,完成Cell的繪制和響應(yīng)處理。以cell的創(chuàng)建唁毒、cell高度獲取為例蒜茴。

  • 根據(jù)cellModel來實現(xiàn)heightForRowAtIndexPath代理

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    
       QSTableViewCellModel *cellModel = [self.dataSource objectAtIndex:indexPath.section];
       return [self.cellFactory cellHeightForData:cellModel];
    }
    
  • 根據(jù)cellModel來完成cell的創(chuàng)建和更新

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
         QSTableViewCellModel *cellModel = [self.dataSource objectAtIndex:indexPath.section];
         QSTableViewCell *cell = [self.cellFactory cellInstanceForData:cellModel];
         [cell layoutWithModel:cellModel];
         return cell;
      }
    

    說明:在cellForRowAtIndexPath:代理方法中實現(xiàn)了cell的創(chuàng)建、復(fù)用浆西、數(shù)據(jù)更新粉私,添加cell上的響應(yīng)動作處理。

  • QSTableViewCellFactory的職責(zé):負責(zé)cell高度的計算近零、創(chuàng)建和復(fù)用

    //QSTableViewCellFactory的定義
    @interface QSTableViewCellFactory : NSObject
    
    - (instancetype)initWithTableView:(UITableView *)tableView;
    
    - (CGFloat)cellHeightForData:(id)data;
    
    - (QSTableViewCell *)cellInstanceForData:(id)data;
    
    @end
    

說明:self.cellFactory就是QSTableViewCellFactory對象诺核,達到解耦的目的抄肖,使得Controller中代碼更簡潔。

3窖杀、上拉加載和下拉刷新

利用MJRefresh來實現(xiàn)TableView的上拉加載和下拉刷新漓摩。

  • 設(shè)置上拉加載

    - (void)setupRefreshFooter{
    
         //上拉加載更多
        @weakify(self);
         MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
              @strongify(self);
                  [self refreshDataWithStyle:QSRefreshTableViewDataStyleLoadMore];
          }];
    
          [footer setTitle:@"已顯示全部"forState:MJRefreshStateNoMoreData];
    
          footer.refreshingTitleHidden = YES;
          self.tableView.mj_footer = footer;
          [footer.stateLabel setTextColor:[UIColor lightGrayColor]];
      }
    
  • 設(shè)置下拉刷新

      - (void)setupRefreshHeader{
    
          //下拉刷新
          @weakify(self);
          MJRefreshHeader *header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
             [self refreshDataWithStyle:QSRefreshTableViewDataStylePull];
          }];
          self.tableView.mj_header = header;
      }
    
  • 結(jié)束刷新

    - (void)endRefreshingWithMoreData:(BOOL)hasMoreData{
    
        if (self.needPullRefresh) {
            [self.tableView.mj_header endRefreshing];
        }
    
        if (self.needPullToLoadMore) {
            if (hasMoreData) {
                [self.tableView.mj_footer endRefreshing];
            }else{
                [self.tableView.mj_footer endRefreshingWithNoMoreData];
            }
        }
    }
    

總結(jié): 使用UITableView構(gòu)建頁面,Controller繼承自定義的TableViewController陈瘦,使用Cell來定義視圖幌甘,CellModel來定義視圖上的內(nèi)容和動作,然后將CellModel放入TableViewController的dataSource數(shù)組中痊项,即可锅风。

三、TableViewCell之間的通信####

在使用TableView組織頁面的時候鞍泉,有個非常值得考慮的問題皱埠,那就是TableViewCellCell之間的的通信。

1咖驮、概述#####
  • 采用分而治之的思路边器,將一個大的視圖拆分成若干個TableViewCell。而在業(yè)務(wù)上托修,這幾個TableViewCell視圖是相互關(guān)聯(lián)的忘巧。如點擊某子視圖,另一個子視圖的數(shù)據(jù)展示發(fā)生變化睦刃。這就導(dǎo)致了需要選擇方案去實現(xiàn)部分TableViewCell之間的通信砚嘴。

  • 早期方案是選用了通知中心

  • 通知中心不僅可以在不同的UITableViewCell之間傳遞數(shù)據(jù)的涩拙,還降低了代碼耦合际长。但是這也導(dǎo)致了TableViewCell中大量存在注冊觀察,移除觀察這樣的代碼(移除的工作還非常重要)兴泥。最關(guān)鍵的是工育,通知中心傳遞的參數(shù)不可以靈活地使用model對象。

  • 后期方案是QSMessageCenter搓彻,這是從業(yè)務(wù)出發(fā)如绸,實現(xiàn)的通知中心的替換方案。

  • QSMessageCenter主要實現(xiàn)了不同對象之間的數(shù)據(jù)傳遞好唯;傳遞的參數(shù)可以是任何的對象,包括數(shù)據(jù)model竭沫,數(shù)組,字典等;注冊后不需要手動移除骑篙; 使用簡單和代碼耦合低。

2森书、通信方案的使用#####

通信方案使用了QSMessageCenter靶端,主要使用步驟如下:

  • 在觀察者中注冊谎势,指定receiverKey

    [self registerMessageReceiverWithKey:receiverKey];
    
  • 發(fā)送消息,指定receiverKey杨名,當注冊方法和發(fā)送消息中的receiverKey相同脏榆,才可以把消息發(fā)送給觀察者

    [self sendMessage:message messageId:messageId receiverKey:receiverKey];
    
  • 在觀察者類中實現(xiàn)qsReceiveMessage:messageId:方法

    - (void)qsReceiveMessage:(id)message messageId:(NSString *)msgId{
        //可以根據(jù)msgId去做不同處理
    }
    

說明:QSMessageCenter的具體詳情參考iOS實錄7:iOS通知中心的替換方案

四、Demo展示####

  • 根據(jù)方案台谍,定義兩類Cell和CellModel,可以實現(xiàn)如下的列表頁须喂。
列表頁效果圖.png
  • 根據(jù)方案,復(fù)用列表頁的一類Cell和CellModel趁蕊,新增一類Cell和CellModel,可以實現(xiàn)如下的詳情頁坞生。
詳情頁效果圖

總結(jié):這樣的方案,為實現(xiàn)列表頁掷伙、詳情頁省去了大量的時間是己,開發(fā)者只需要集中精力做好Cell的繪制、CellModel的定義即可任柜。

五卒废、Native動態(tài)化頁面的野望(挖坑)

H5頁面有個很大的優(yōu)點,就是可以不用發(fā)版就可以更新頁面內(nèi)容宙地;如果可以利用TableView實現(xiàn)動態(tài)化的Native頁面摔认,豈不是很完美的事情。然后宅粥,現(xiàn)實的需求和業(yè)務(wù)比較復(fù)雜多變参袱,實現(xiàn)這樣的方案,投入和產(chǎn)出比是多少粹胯,能不能達到目標蓖柔,都不好判斷。但是針對某些特性的場景风纠,實現(xiàn)頁面的動態(tài)化數(shù)據(jù)更新還是可以的况鸣。

1、數(shù)據(jù)描述界面的各個組成#####

使用的是JSON格式數(shù)據(jù)竹观,由后臺返回镐捧,如下所示(部分JSON數(shù)據(jù)):

{
   "body" :[{
      "type": 0, 
       "content": "http://xxxxxxxxxxxxxxxxxxxxx.jpg",
       "target":"appName://previewSingleImage"
    },
    {
        "type": 1, 
        "content": "H5頁面有個很大的優(yōu)點,就是可以不用發(fā)版就可以更新頁面內(nèi)容臭增;如果可以利用TableView實現(xiàn)動態(tài)化的Native頁面懂酱,豈不是很完美的事情....",
        "target":"appName://lookAllContent"
    },
    {
        "type": 2, 
        "content": "http://xxxxxxxxxxxxxxxxx.mp4",
        "target":"appName://playInFullScreen"
    }
    //其他略
  ]
//其他略
}

說明1:type定義的是內(nèi)容的類型,如0是圖片誊抛,1是文本列牺,2是視頻;content定義的是內(nèi)容拗窃;tagert定義的是點擊內(nèi)容的行為瞎领;tagert值的是App中自己定義的協(xié)議泌辫,如appName://previewSingleImage表明點擊圖片Cell預(yù)覽全圖,appName://playInFullScreen是點擊視頻Cell九默,全屏播放視頻震放。

2、動態(tài)化UI方案#####

有兩種方案可以選:

1)在Native實現(xiàn)盛裝不同數(shù)據(jù)類型的容器Cell驼修,和Cell的響應(yīng)處理行為殿遂。

說明:該方案沒有深入下去,因為TableView處理的視圖比較簡單乙各,而且還給后臺帶來比較大的負擔(dān)(本來后臺開發(fā)資源就緊張)墨礁,后期考慮使用CollectionView來實現(xiàn)特定場景下較復(fù)雜的動態(tài)化界面。

2)將數(shù)據(jù)和展示需求拼接成H5代碼觅丰,然后由WebView渲染出來饵溅,結(jié)合JS和WebViewJavascriptBridge處理Cell的響應(yīng)處理行為。

說明:該方案是目前采納的方法妇萄,一定程度上實現(xiàn)了頁面的動態(tài)化蜕企,比直接加載H5頁面快很多。但是也僅僅是適用于某些固定展示行為的模塊冠句,如文章詳情頁等轻掩。

3、 總結(jié)#####

End

  • 源碼QSUseTableViewDemo

  • 我是一個iOS開發(fā)程序猿杆查,至今(2017年7月)正式做iOS開發(fā)已有1年多點的光景扮惦。iOS實(踐)錄系列是自己的一點開發(fā)心得。這個系列是17年4月1日開始寫的亲桦,不僅是總結(jié)自己開發(fā)中經(jīng)驗崖蜜,也是對自己的鞭策。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末客峭,一起剝皮案震驚了整個濱河市豫领,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舔琅,老刑警劉巖等恐,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡鼠锈,警方通過查閱死者的電腦和手機闪檬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門星著,熙熙樓的掌柜王于貴愁眉苦臉地迎上來购笆,“玉大人,你說我怎么就攤上這事虚循⊥罚” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵横缔,是天一觀的道長铺遂。 經(jīng)常有香客問我,道長茎刚,這世上最難降的妖魔是什么襟锐? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮膛锭,結(jié)果婚禮上粮坞,老公的妹妹穿的比我還像新娘。我一直安慰自己初狰,他們只是感情好莫杈,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奢入,像睡著了一般筝闹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腥光,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天关顷,我揣著相機與錄音,去河邊找鬼武福。 笑死议双,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的艘儒。 我是一名探鬼主播聋伦,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼界睁!你這毒婦竟也來了觉增?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤翻斟,失蹤者是張志新(化名)和其女友劉穎逾礁,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嘹履,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年腻扇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砾嫉。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡幼苛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焕刮,到底是詐尸還是另有隱情舶沿,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布配并,位于F島的核電站括荡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏溉旋。R本人自食惡果不足惜畸冲,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望观腊。 院中可真熱鬧邑闲,春花似錦、人聲如沸恕沫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婶溯。三九已至鲸阔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迄委,已是汗流浹背褐筛。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叙身,地道東北人渔扎。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像信轿,于是被迫代替她去往敵國和親晃痴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 概述在iOS開發(fā)中UITableView可以說是使用最廣泛的控件财忽,我們平時使用的軟件中到處都可以看到它的影子倘核,類似...
    liudhkk閱讀 9,003評論 3 38
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,130評論 30 470
  • 前言 由于最近兩個多月,筆者正和小伙伴們忙于對公司新項目的開發(fā)即彪,筆者主要負責(zé)項目整體架構(gòu)的搭建以及功能模塊的分工紧唱。...
    CoderMikeHe閱讀 27,006評論 74 271
  • 媽媽老了漏益,耳朵有些背了蛹锰,記憶力也不大好,常常叫錯我們兄弟倆的名字绰疤。解決辦法是要常常讓她喜悅地參與活動铜犬,用笑話刺激大...
    chchfo閱讀 318評論 3 1
  • 我像每個人都一段第一次獨自走夜路的歷程,我也不例外峦睡,不同尋常翎苫,它像一次刻骨銘心的修行讓我明白了很多。 那是...
    灰叔漫畫閱讀 951評論 6 10