成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

寫一個(gè)iOS復(fù)雜表單的正確姿勢(shì)

移動(dòng)開發(fā) iOS
這幾天項(xiàng)目的新需求中有個(gè)復(fù)雜的表單界面,在做的過程中發(fā)現(xiàn)要比想象中復(fù)雜很多,有好多問題需要處理。有很多東西值得寫下來(lái)好好梳理下。

前言

這幾天項(xiàng)目的新需求中有個(gè)復(fù)雜的表單界面,在做的過程中發(fā)現(xiàn)要比想象中復(fù)雜很多,有好多問題需要處理。有很多東西值得寫下來(lái)好好梳理下。

需求分析: 

 

 

 

6創(chuàng)建網(wǎng)店1.png

上圖便是UI根據(jù)需求給的高保真, 我們先根據(jù)這張圖片來(lái)描述一下具體需求,明確一下我們都需要干些什么。

創(chuàng)建網(wǎng)店這個(gè)界面是一個(gè)復(fù)雜的表單,有“網(wǎng)店名稱”、“網(wǎng)店主標(biāo)簽”、“網(wǎng)店簡(jiǎn)介”、“網(wǎng)店地址”、“網(wǎng)店座機(jī)”、“email”、“網(wǎng)店LOGO”、“網(wǎng)店封面圖”這些項(xiàng)。大部分都是輸入框,但也有幾項(xiàng)有所不同。“網(wǎng)店地址”項(xiàng),當(dāng)被點(diǎn)擊后會(huì)彈出一個(gè)pickView來(lái)選擇“市&區(qū)”;“網(wǎng)店LOGO”和“網(wǎng)店封面圖”是一樣的,是選取圖片的控件,要求既可以通過相冊(cè)選取圖片,也可以現(xiàn)場(chǎng)拍照選擇。當(dāng)被點(diǎn)擊后,彈出一個(gè)ActionSheet來(lái)是以“拍照”或以“相冊(cè)”來(lái)選取圖片。當(dāng)選取成功后拍照的背景圖片變?yōu)楸贿x取的圖片,并在右上角出現(xiàn)一個(gè)刪除按鈕,可以刪除還原再次選取。

表單中除了“email”外所有的項(xiàng)目都是必填的,且“網(wǎng)店名稱”、“網(wǎng)店主標(biāo)簽”、“網(wǎng)店簡(jiǎn)介”和“網(wǎng)店座機(jī)”分別有30、20、500、15字的長(zhǎng)度限制。“email”雖然為選填,但若填寫了則會(huì)進(jìn)行郵箱格式校驗(yàn)。對(duì)字?jǐn)?shù)長(zhǎng)度的限制要在輸入過程中進(jìn)行監(jiān)聽,若輸入時(shí)超過限制,則輸入框出現(xiàn)紅色邊框并出現(xiàn)提示文字。等***點(diǎn)擊了“提交”按鈕后要進(jìn)行數(shù)據(jù)校驗(yàn),所有該填但未填,所有格式不正確的項(xiàng)都會(huì)出現(xiàn)紅框和提示文字,當(dāng)所有數(shù)據(jù)都合法后才可以提交給服務(wù)器。

需求大體就是如此。

這個(gè)界面我們還是以tableView來(lái)實(shí)現(xiàn),由cell視圖來(lái)表示圖中所需填寫的項(xiàng)目。那我們得先分析下這個(gè)界面需要寫哪幾種樣式的cell。

該界面總共有4種樣式的cell。4種樣式的cell樣式也有共同點(diǎn),每個(gè)cell左邊部分均為表示該行所要填寫的項(xiàng)目名稱,右邊部分則為填寫或者選取的內(nèi)容值,這些值的顯示形式有所不同。 CreateShopTFCell和CreateShopTVCell其實(shí)非常類似,右邊首先是一個(gè)灰色的背景視圖,只不過在灰色背景之上的前者是textField,而后者是textView;CreateShopPickCell右邊則是兩個(gè)灰色背景視圖,點(diǎn)擊之后便彈出一個(gè)pickView供你選取“市&區(qū)”;CreateShopUploadPicCell右邊則是一個(gè)UIImageView,無(wú)圖片被選取時(shí)默認(rèn)是一個(gè)相機(jī)的圖片,當(dāng)被點(diǎn)擊后彈出ActionSheet供你選擇拍照還是從相冊(cè)選取照片,選好照片后UIImageView的圖片被替換,并在右上角出現(xiàn)紅色的刪除按鈕。

如下圖所示: 

 

 

 

6創(chuàng)建網(wǎng)店.png

正確地將視圖和數(shù)據(jù)綁定:

我們假設(shè)已經(jīng)寫好了上面4種樣式cell的代碼,現(xiàn)在我們?cè)诳刂破骼餅槠涮畛鋽?shù)據(jù)。

我們首先定義一個(gè)表示cell數(shù)據(jù)的CreateShopModel。該model是為了給cell填充數(shù)據(jù),可以看到它里面的屬性就是cell上對(duì)應(yīng)應(yīng)該顯示的數(shù)據(jù)項(xiàng)。

同時(shí),我們?cè)陂_頭也定義了一個(gè)枚舉CreateShopCellType來(lái)代表4種不同樣式的cell,用于在tableView返回cell的代理方法里根據(jù)枚舉值來(lái)返回相應(yīng)樣式的cell。

  1. #import 
  2.  
  3.   
  4.  
  5. typedef enum : NSUInteger { 
  6.  
  7.     CreateShopCellType_TF = 0, // textfield 
  8.  
  9.     CreateShopCellType_TV, // textView 
  10.  
  11.     CreateShopCellType_PICK, // picker 
  12.  
  13.     CreateShopCellType_PIC, // upload picture 
  14.  
  15. } CreateShopCellType; 
  16.  
  17.   
  18.  
  19. @interface CreateShopModel : NSObject 
  20.  
  21.   
  22.  
  23. @property (nonatomic, copy)NSString                    *title;  // 所要填寫的項(xiàng)目名稱 
  24.  
  25. @property (nonatomic, copy)NSString                    *placeholder; 
  26.  
  27. @property (nonatomic, copy)NSString                    *key; // 表單對(duì)應(yīng)的字段 
  28.  
  29. @property (nonatomic, copy)NSString                    *errText; // 校驗(yàn)出錯(cuò)時(shí)的提示信息 
  30.  
  31. @property (nonatomic, strong)UIImage                    *image;     // 所選取的圖片 
  32.  
  33. @property (nonatomic, assign)CreateShopCellType         cellType; // cell的類型 
  34.  
  35. @property (nonatomic, assign)NSInteger                 maxInputLength; // ***輸入長(zhǎng)度限制 
  36.  
  37.   
  38.  
  39. @end  

我們?cè)趯ableView創(chuàng)建并添加在控制器的view上后便可以初始化數(shù)據(jù)源了。該界面tableView的數(shù)據(jù)源是_tableViewData數(shù)組,數(shù)據(jù)的每項(xiàng)元素是代表cell顯示數(shù)據(jù)的CreateShopModel類型的model。準(zhǔn)確地來(lái)說(shuō),這些數(shù)據(jù)是表單未填寫之前的死數(shù)據(jù),所以需要我們手動(dòng)地給裝入數(shù)據(jù)源數(shù)組中。而在輸入框輸入或者選取而得的數(shù)據(jù)則需要我們?cè)谳斎胫髮⑵洳东@存儲(chǔ)下來(lái),以等到提交時(shí)提交給服務(wù)器,這個(gè)也有需要注意的坑點(diǎn),后面再說(shuō)。

 

 

 

  

 

現(xiàn)在我們的數(shù)據(jù)源準(zhǔn)備好了,但是tableView還沒做處理呢,要等tableView也配套完成后再刷新tableView就OK了。我們來(lái)看tableView代理方法。 

   

 

 

首先比較簡(jiǎn)單的,在設(shè)置行高的代理方法里,根據(jù)該行數(shù)據(jù)所表示的cellType類型來(lái)設(shè)置相應(yīng)的行高。

然后在返回cell的代理方法里,同樣以cellType來(lái)判斷返回相應(yīng)樣式的cell,并給該cell賦相應(yīng)的數(shù)據(jù)model。但是我們注意到,給cell賦值的方法,除了傳入我們前面說(shuō)定義的CreateShopModel類型的createModel外,還有個(gè)名叫_shopFormModel參數(shù)被傳入。_shopFormModel是什么,它代表什么意思?

_shopFormModel是CreateShopFormModel類型的一個(gè)實(shí)例對(duì)象,它用來(lái)表示這個(gè)表單需要提交的數(shù)據(jù),它里面的每個(gè)屬性基本上對(duì)應(yīng)著表單提交給服務(wù)器的字段。我們***不是要將表單數(shù)據(jù)作為參數(shù)去請(qǐng)求提交的接口嗎?表單數(shù)據(jù)從哪里來(lái),就從_shopFormModel中來(lái)。那_shopFormModel中的數(shù)據(jù)從哪里來(lái)?

  1. #import 
  2.  
  3.   
  4.  
  5. @interface CreateShopFormModel : NSObject 
  6.  
  7.   
  8.  
  9. @property (nonatomic, copy)NSString            *groupId; 
  10.  
  11. @property (nonatomic, copy)NSString            *groupName; 
  12.  
  13. @property (nonatomic, copy)NSString            *tag; 
  14.  
  15. @property (nonatomic, copy)NSString            *introduction; 
  16.  
  17. @property (nonatomic, copy)NSString            *regionId; 
  18.  
  19. @property (nonatomic, copy)NSString            *cityId; 
  20.  
  21. @property (nonatomic, copy)NSString            *address; 
  22.  
  23. @property (nonatomic, copy)NSString            *telephone; 
  24.  
  25. @property (nonatomic, copy)NSString            *contactMail; 
  26.  
  27. @property (nonatomic, copy)NSString            *coverUrl; 
  28.  
  29. @property (nonatomic, copy)NSString            *logoUrl; 
  30.  
  31. @property (nonatomic, strong)UIImage        *logo; 
  32.  
  33. @property (nonatomic, strong)UIImage        *cover; 
  34.  
  35. @property (nonatomic, strong)NSIndexPath    *indexPath; 
  36.  
  37. @property (nonatomic, strong)id             indexPathObj; 
  38.  
  39.   
  40.  
  41. + (CreateShopFormModel *)formModelFromDict:(NSDictionary *)dict; 
  42.  
  43. -(BOOL)submitCheck:(NSArray*)dataArr; 
  44.  
  45.   
  46.  
  47. @end  

以CreateShopTFCell為例,它所表示的字段的數(shù)據(jù)是我們?cè)谳斎肟蜉斎氲?,也就是說(shuō)數(shù)據(jù)來(lái)自textField,_shopFormModel對(duì)象在控制器被傳入cell的refreshContent:formModel:方法,在該方法內(nèi)部,將參數(shù)formModel賦給成員變量_formModel。需要格外注意的是,_shopFormModel、formModel和_ formModel是同一個(gè)對(duì)象,指向的是同一塊內(nèi)存地址。方法傳遞對(duì)象參數(shù)時(shí)只是“引用拷貝”,拷貝了一份對(duì)象的引用。既然這樣,我們可以預(yù)想到,我們?cè)赾ell內(nèi)部,將textField輸入的值賦給_formModel所指向的對(duì)象后,也即意味著控制器里的_shopFormModel也有數(shù)據(jù)了,因?yàn)樗鼈儽緛?lái)就是同一個(gè)對(duì)象嘛!

事實(shí)正是如此。

可以看到我們?cè)诮otextField添加的通知的回調(diào)方法textFiledEditChanged:里,將textField輸入的值以KVC的方式賦值給了_formModel。此時(shí)_formModel的某屬性,即該cell對(duì)應(yīng)的表單的字段已經(jīng)有了數(shù)據(jù)。同樣的,在控制器中與_formModel指向同一塊內(nèi)存地址的_shopFormModel也有了數(shù)據(jù)。 

  

 

 

 

我們看到在refreshContent:formModel:方法中,cell上的死數(shù)據(jù)是被CreateShopModel的實(shí)例對(duì)象createModel賦值的,而在其后我們又以KVC的方式又將_shopFormModel的某屬性的值賦給了textField。這是因?yàn)槲覀優(yōu)榱朔乐筩ell在復(fù)用的過程中出現(xiàn)數(shù)據(jù)錯(cuò)亂的問題,而在給cell賦值前先將每個(gè)視圖上的數(shù)據(jù)都清空了(即clearCellData方法),需要我們重新賦過。(不過,如果你沒清空數(shù)據(jù)的情況下,不再次給textField賦值好像也是沒問題的。不會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂和滑出屏幕再滑回來(lái)時(shí)從復(fù)用池取出cell后賦值時(shí)數(shù)據(jù)消失的問題。)

輸入長(zhǎng)度的限制:

需求中要求“網(wǎng)店名稱”、“網(wǎng)店主標(biāo)簽”、“網(wǎng)店簡(jiǎn)介”、“網(wǎng)店座機(jī)”都有輸入長(zhǎng)度的限制,分別為30、20、500、15字?jǐn)?shù)的限制。其實(shí)我們?cè)谏厦娉跏蓟瘮?shù)據(jù)源的時(shí)候已經(jīng)為每行的數(shù)據(jù)源model設(shè)置過字?jǐn)?shù)限制了,即maxInputLength屬性。

我們還是以CreateShopTFCell為例。

要在開始輸入的時(shí)候監(jiān)聽輸入的長(zhǎng)度,若字?jǐn)?shù)超過***限制,則要出現(xiàn)紅框,并且顯示提示信息。那我們就得給textField開始輸入時(shí)添加valueChange的觀察,在textField輸入結(jié)束時(shí)移除觀察。   

 

另外,可以看到在textField開始輸入的回調(diào)方法里,調(diào)用了該cell的代理方法。該cell為什么要調(diào)用這個(gè)代理方法,它需要代理給別人來(lái)干什么?…其實(shí)這個(gè)和鍵盤遮擋的處理有關(guān),下面我們慢慢解釋。

處理鍵盤遮擋問題:

這個(gè)界面有很多行輸入框,在自然情況下,下面的幾個(gè)輸入框肯定是在鍵盤彈出后高度之下的,也即會(huì)被鍵盤遮擋住,我們沒法輸入。這時(shí)就一定處理鍵盤遮擋問題了。

關(guān)于鍵盤遮擋問題,其實(shí)我在以前的一篇筆記中就寫過了:UITextField一籮筐——輸入長(zhǎng)度限制、自定義placeholder、鍵盤遮擋問題

我們要處理鍵盤遮擋問題,也就是要實(shí)現(xiàn)當(dāng)鍵盤彈出時(shí),被遮擋住的輸入框能上移到鍵盤高度之上;當(dāng)鍵盤收回時(shí),輸入框又能移回原來(lái)的位置。那么首先***步,我們得能獲取到鍵盤彈出或者收回這個(gè)動(dòng)作的時(shí)機(jī),在這個(gè)時(shí)機(jī)我們?cè)侔葱枰苿?dòng)輸入框的位置。系統(tǒng)提供了表示鍵盤彈出和收回的兩個(gè)觀察的key,分別為UIKeyboardWillShowNotification和UIKeyboardWillHideNotification。注冊(cè)這兩個(gè)觀察者,然后在兩者的回調(diào)方法里實(shí)現(xiàn)輸入框位移就大功告成了。

因?yàn)殒I盤遮擋的處理有可能是比較普遍的需求,所以在公司的項(xiàng)目架構(gòu)設(shè)計(jì)里是把上面兩個(gè)關(guān)于鍵盤的觀察是注冊(cè)在APPDelegate.m中的,并定義了一個(gè)有關(guān)鍵盤遮擋處理的協(xié)議,協(xié)議里定義了一個(gè)方法。具體需要具體處理,由需要處理鍵盤遮擋問題的控制器來(lái)實(shí)現(xiàn)該協(xié)議方法,具體實(shí)現(xiàn)怎么移動(dòng)界面元素來(lái)使鍵盤不遮擋輸入框。這么說(shuō)現(xiàn)在CreateShopViewController控制器需要處理鍵盤遮擋問題,那么就需要設(shè)置它為APPDelegate的代理,并由它實(shí)現(xiàn)所定義的協(xié)議嗎?其實(shí)不用,公司項(xiàng)目所有的控制器都是繼承于基類CommonViewController,在基類中實(shí)現(xiàn)了比較基本和普遍的功能,其實(shí)在基類中便定義了下面的方法來(lái)設(shè)置控制器為APPDelegate的代理,不過需要屬性isListensKeyboard為YES。下面這個(gè)方法在CommonViewController中是在viewWillAppear:方法中調(diào)用的。那我們?cè)谧宇怌reateShopViewController中需要做的僅僅只要在viewWillAppear之前設(shè)置isListensKeyboard屬性為YES,便會(huì)自動(dòng)設(shè)置將自己設(shè)為APPDelegate的代理。然后在CreateShopViewController控制器里實(shí)現(xiàn)協(xié)議所定義的方法,實(shí)現(xiàn)具體的輸入框移動(dòng)問題。

CommonViewController.m 

  1. -(void)initListensKeyboardNotificationDelegate 
  2.  
  3.  
  4.     if (!self.isListensKeyboard) { 
  5.  
  6.         return
  7.  
  8.     } 
  9.  
  10.   
  11.  
  12.     if (!self.appDelegate) { 
  13.  
  14.         self.appDelegate=(AppDelegate*)[[UIApplication sharedApplication] delegate]; 
  15.  
  16.     } 
  17.  
  18.   
  19.  
  20.     [self.appDelegate setKeyboardDelegate:self]; 
  21.  

CreateShopViewController.m 

 

 

 

可以看到在該代理方法的實(shí)現(xiàn)里。當(dāng)鍵盤彈出時(shí),我們首先將tableView的contentSize在原來(lái)的基礎(chǔ)上增加了鍵盤的高度keyBoard_h。然后將tableView的contentOffset值變?yōu)閟et_y,這個(gè)set_y的值是通過計(jì)算而來(lái),但是計(jì)算它的_inputY這個(gè)變量代表什么意思?

我們可以回過頭去看看tableView返回cell的代理方法中,當(dāng)為CreateShopTFCell時(shí),我們?cè)O(shè)置了當(dāng)前控制器為其cell的代理。

  1. cell.cellDelegate = self; 

并且我們的控制器CreateShopViewController也實(shí)現(xiàn)了該cell的協(xié)議CreateShopTFCellDelegate,并且也實(shí)現(xiàn)了協(xié)議定義的方法。

  1. #pragma mark - tfCell delegate 
  2.  
  3. - (void)cellBeginInputviewY:(CGFloat)orginY 
  4.  
  5.  
  6.     _inputY = orginY; 
  7.  
  8.  

原來(lái)上面的_intputY變量就是該協(xié)議方法從cell里的調(diào)用處傳遞而來(lái)的orginY參數(shù)值。我們回過頭看上面的代碼,該協(xié)議方法是在textField的開始輸入的回調(diào)方法里調(diào)用的,給協(xié)議方法傳入的參數(shù)是self.frame.origin.y,即被點(diǎn)擊的textField在手機(jī)屏幕內(nèi)所在的Y坐標(biāo)值。

可以看到,處理鍵盤遮擋問題,其實(shí)也不是改變輸入框的坐標(biāo)位置,而是變動(dòng)tableView的contentSize和contentOffset屬性。

選取地址的實(shí)現(xiàn):

CreateShopPickCell實(shí)現(xiàn)里地址的選取和顯示。有左右兩個(gè)框框,點(diǎn)擊任何一個(gè)將會(huì)從屏幕下方彈出一個(gè)選取器,選取器有“市”和“區(qū)”兩列數(shù)據(jù)對(duì)應(yīng)兩個(gè)框框,選取器左上方是“取消”按鈕,右上方是“確定”按鈕。點(diǎn)擊“取消”,選取器彈回,并不進(jìn)行選取;點(diǎn)擊“確定”,選取器彈回,選取選擇的數(shù)據(jù)。 

 

 

 

WechatIMG1.png

CreateShopPickCell的界面元素布局沒什么可說(shuō)的,值得一說(shuō)的是彈出的pickView視圖,是在cell的填充數(shù)據(jù)的方法中創(chuàng)建的。 

 

 

 

這里只是創(chuàng)建了pickView的對(duì)象,并設(shè)置了數(shù)據(jù)源items,已經(jīng)點(diǎn)擊之后的回調(diào)block,而并未將其添加在父視圖上。

要將選取的“市&區(qū)”的結(jié)果從CustomPickView中以block回調(diào)到cell來(lái),將數(shù)據(jù)賦給_formModel。并且當(dāng)有了數(shù)據(jù)后UILabel的文本顏色也有變化。 

 

 

 

pickView的對(duì)象已經(jīng)創(chuàng)建好,但是還未到彈出顯示的時(shí)機(jī)。所謂時(shí)機(jī),就是當(dāng)左右兩個(gè)框框被點(diǎn)擊后。

可以看到pickView是被添加在window上的。并且調(diào)用了pickView的接口方法showPickerView方法,讓其從屏幕底部彈出來(lái)。

  1. - (void)cityGestureHandle:(UITapGestureRecognizer *)tapGesture 
  2.  
  3.  
  4.     [_superView endEditing:YES]; 
  5.  
  6.     [self showPicker]; 
  7.  
  8.  
  9.   
  10.  
  11. - (void)areaGestureHandle:(UITapGestureRecognizer *)tapGesture 
  12.  
  13.  
  14.     [_superView endEditing:YES]; 
  15.  
  16.     [self showPicker]; 
  17.  
  18.  
  19.   
  20.  
  21. -(void)showPicker 
  22.  
  23.  
  24.     [[PubicClassMethod getCurrentWindow] addSubview:_pickView]; 
  25.  
  26.     [_pickView showPickerView]; 
  27.  
  28.  

前面代碼中給pickView設(shè)置數(shù)據(jù)源時(shí),它的數(shù)據(jù)源有點(diǎn)特別,調(diào)用了ShopAddressModel的類方法cityAddressArr來(lái)返回有關(guān)地址的數(shù)據(jù)源數(shù)組。這是因?yàn)檫@里的地址數(shù)據(jù)雖然是從服務(wù)器接口請(qǐng)求的,但是一般情況不會(huì)改變,***是從服務(wù)器拿到數(shù)據(jù)后緩存在本地,當(dāng)請(qǐng)求失敗或者無(wú)網(wǎng)絡(luò)時(shí)仍不受影響。

ShopAddressModel類定義了如下幾個(gè)屬性和方法。

  1. @interface ShopAddressModel : NSObject 
  2.  
  3.   
  4.  
  5. @property (nonatomic, copy)NSString            *addresssId; 
  6.  
  7. @property (nonatomic, copy)NSString            *name
  8.  
  9. @property (nonatomic, strong)NSArray        *subArr; 
  10.  
  11. #pragma mark - 地址緩存 
  12.  
  13. + (void)saveAddressArr:(NSArray *)addressArr; 
  14.  
  15. +(NSArray*)cityAddressArr; 
  16.  
  17. +(NSArray*)addressArr; 
  18.  
  19. #pragma mark - 解析 
  20.  
  21. + (ShopAddressModel *)addressModelFromDict:(NSDictionary *)dict; 
  22.  
  23. @end  

當(dāng)我們我們從服務(wù)器拿到返回而來(lái)的地址數(shù)據(jù)后,調(diào)用saveAddressArr:方法,將數(shù)據(jù)緩存在本地。

  1. + (void)saveAddressArr:(NSArray *)addressArr 
  2.  
  3.  
  4.     if (addressArr && addressArr.count > 0) { 
  5.  
  6.         NSData *data = [NSKeyedArchiver archivedDataWithRootObject:addressArr]; 
  7.  
  8.         [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"saveAddressArr"]; 
  9.  
  10.   
  11.  
  12.     }else 
  13.  
  14.     { 
  15.  
  16.         [[NSUserDefaults standardUserDefaults]setObject:nil forKey:@"saveAddressArr"]; 
  17.  
  18.     } 
  19.  
  20.   
  21.  
  22.     [[NSUserDefaults standardUserDefaults] synchronize]; 
  23.  
  24.  

當(dāng)創(chuàng)建好pickView后以下面方法將本地緩存數(shù)據(jù)讀出,賦給items作為數(shù)據(jù)源。 

 

 

 

 

 

 

 

注意:這也是為什么把創(chuàng)建pickView的代碼放在了填充cell數(shù)據(jù)的refreshContent:formModel:里,而不在創(chuàng)建cell界面元素時(shí)一氣創(chuàng)建pickView。因?yàn)槟菢赢?dāng)用戶***次打開這個(gè)界面,有可能數(shù)據(jù)來(lái)的比較慢,當(dāng)代碼執(zhí)行到賦數(shù)據(jù)源items時(shí),本地還沒有被緩存上數(shù)據(jù)呢!這樣用戶***次進(jìn)入這個(gè)界面時(shí)彈出的pickView是空的,沒有數(shù)據(jù)。而放在refreshContent:formModel:中是安全穩(wěn)妥的原因是,每次從接口拿到數(shù)據(jù)后我們會(huì)刷新tableView,便會(huì)執(zhí)行refreshContent:formModel:方法。它能保證先拿到數(shù)據(jù),再設(shè)置數(shù)據(jù)源的順序。

提交表單時(shí)校驗(yàn)數(shù)據(jù):

在將表單數(shù)據(jù)提交前,要先校驗(yàn)所填寫的表單是否有問題,該填的是否都填了,已填的數(shù)據(jù)格式是否是對(duì)的。若有問題,則要出現(xiàn)紅框和提示信息提醒用戶完善,等數(shù)據(jù)無(wú)誤后才可以提交給服務(wù)器。

數(shù)據(jù)校驗(yàn)代碼很繁長(zhǎng),寫在控制器里不太好。因?yàn)樗菍?duì)表單數(shù)據(jù)的校驗(yàn),那我們就寫在CreateShopFormModel里,這樣既可以給控制器瘦身,也可以降低耦合度,數(shù)據(jù)的歸數(shù)據(jù),邏輯的歸邏輯。

從前面CreateShopFormModel.h的代碼里我們其實(shí)已經(jīng)看到了這個(gè)校驗(yàn)方法:submitCheck:。若某條CreateShopFormModel實(shí)例的數(shù)據(jù)不達(dá)要求,則在相應(yīng)的CreateShopModel數(shù)據(jù)源對(duì)象的errText屬性賦值,意為提示信息。該方法的返回值類型為BOOL值,有數(shù)據(jù)不合格則返回NO。此時(shí),在調(diào)用該方法的外部,應(yīng)該將tableView重新加載,因?yàn)榇藭r(shí)在該方法內(nèi)部,已將數(shù)據(jù)格式不合格的提示信息賦值給了相應(yīng)的數(shù)據(jù)源model。

  1. - (BOOL)submitCheck:(NSArray*)dataArr 
  2.  
  3.  
  4.     BOOL isSubmit=YES; 
  5.  
  6.   
  7.  
  8.     if(self.groupName.length==0){ 
  9.  
  10.         if (dataArr.count>0) { 
  11.  
  12.             CreateShopModel *cellObj=dataArr[0]; 
  13.  
  14.             cellObj.errText=@"網(wǎng)店名不能為空"
  15.  
  16.         } 
  17.  
  18.         isSubmit=NO
  19.  
  20.     } 
  21.  
  22.   
  23.  
  24.     if(self.groupName.length>0){ 
  25.  
  26.         if(dataArr.count>0){ 
  27.  
  28.             if(self.groupName.length>30){ 
  29.  
  30.                 CreateShopModel *cellObj=dataArr[0]; 
  31.  
  32.                 cellObj.errText=@"最多30個(gè)字"
  33.  
  34.                 isSubmit=NO
  35.  
  36.             } 
  37.  
  38.         } 
  39.  
  40.     } 
  41.  
  42.   
  43.  
  44.     if(self.tag.length==0){ 
  45.  
  46.         if (dataArr.count>1) { 
  47.  
  48.             CreateShopModel *cellObj=dataArr[1]; 
  49.  
  50.             cellObj.errText=@"標(biāo)簽不能為空"
  51.  
  52.         } 
  53.  
  54.         isSubmit=NO
  55.  
  56.     } 
  57.  
  58.   
  59.  
  60.     if(self.introduction.length==0){ 
  61.  
  62.         if (dataArr.count>2) { 
  63.  
  64.             CreateShopModel *cellObj=dataArr[2]; 
  65.  
  66.             cellObj.errText=@"簡(jiǎn)介不能為空"
  67.  
  68.         } 
  69.  
  70.         isSubmit=NO
  71.  
  72.     } 
  73.  
  74.   
  75.  
  76.     if(self.introduction.length>0){ 
  77.  
  78.         if(dataArr.count>2){ 
  79.  
  80.             if(self.introduction.length>30){ 
  81.  
  82.                 CreateShopModel *cellObj=dataArr[2]; 
  83.  
  84.                 cellObj.errText=@"最多500個(gè)字"
  85.  
  86.                 isSubmit=NO
  87.  
  88.             } 
  89.  
  90.         } 
  91.  
  92.     } 
  93.  
  94.   
  95.  
  96.     if(self.regionId.length==0){ 
  97.  
  98.         if (dataArr.count>3) { 
  99.  
  100.             CreateShopModel *cellObj=dataArr[3]; 
  101.  
  102.             cellObj.errText=@"市區(qū)不能為空"
  103.  
  104.         } 
  105.  
  106.         isSubmit=NO
  107.  
  108.     } 
  109.  
  110.   
  111.  
  112.     if(self.address.length==0){ 
  113.  
  114.         if (dataArr.count>4) { 
  115.  
  116.             CreateShopModel *cellObj=dataArr[4]; 
  117.  
  118.             cellObj.errText=@"地址不能為空"
  119.  
  120.         } 
  121.  
  122.         isSubmit=NO
  123.  
  124.     } 
  125.  
  126.   
  127.  
  128.     if(self.telephone.length==0){ 
  129.  
  130.         if (dataArr.count>5) { 
  131.  
  132.             CreateShopModel *cellObj=dataArr[5]; 
  133.  
  134.             cellObj.errText=@"電話不能為空"
  135.  
  136.         } 
  137.  
  138.         isSubmit=NO
  139.  
  140.     } 
  141.  
  142.   
  143.  
  144.   
  145.  
  146.     if (self.contactMail.length>0) { 
  147.  
  148.   
  149.  
  150.         if (dataArr.count>6) { 
  151.  
  152.             CreateShopModel *cellObj=dataArr[6]; 
  153.  
  154.             if(![PubicClassMethod isValidateEmail:self.contactMail]){ 
  155.  
  156.                 cellObj.errText=@"郵箱格式不合法"
  157.  
  158.                 isSubmit=NO
  159.  
  160.             } 
  161.  
  162.         } 
  163.  
  164.     } 
  165.  
  166.   
  167.  
  168.     if(self.logoUrl.length==0&&!self.logo){ 
  169.  
  170.         if (dataArr.count>7) { 
  171.  
  172.             CreateShopModel *cellObj=dataArr[7]; 
  173.  
  174.             cellObj.errText=@"logo不能為空"
  175.  
  176.         } 
  177.  
  178.         isSubmit=NO
  179.  
  180.     } 
  181.  
  182.   
  183.  
  184.     if(self.coverUrl.length==0&&!self.cover){ 
  185.  
  186.         if (dataArr.count>8) { 
  187.  
  188.             CreateShopModel *cellObj=dataArr[8]; 
  189.  
  190.             cellObj.errText=@"封面圖不能為空"
  191.  
  192.         } 
  193.  
  194.         isSubmit=NO
  195.  
  196.     } 
  197.  
  198.   
  199.  
  200.     return isSubmit; 
  201.  
  202.  

上傳圖片到七牛:

當(dāng)點(diǎn)擊了“提交”按鈕后,先校驗(yàn)數(shù)據(jù),若所填寫的數(shù)據(jù)不合格,則給出提示信息,讓用戶繼續(xù)完善數(shù)據(jù);若數(shù)據(jù)無(wú)問題,校驗(yàn)通過,則開始提交表單。但是,這里有圖片,圖片我們是上傳到七牛服務(wù)器的,提交表單是圖片項(xiàng)提交的應(yīng)該是圖片在七牛的一個(gè)url。這個(gè)邏輯我在以前的這篇筆記已經(jīng)捋過了APP上傳圖片至七牛的邏輯梳理。

但是當(dāng)時(shí)所有的邏輯都是寫在控制器里的。我們這個(gè)“創(chuàng)建網(wǎng)店”的控制器已經(jīng)很龐大了,寫在控制器里不太好。所以在這里我將上傳圖片的邏輯拆分了出去,新建了一個(gè)類`QNUploadPicManager。只暴露一個(gè)允許傳入U(xiǎn)IImage參數(shù)的接口方法,便可以通過successBlock來(lái)返回上傳到七牛成功后的url。以及通過failureBlock來(lái)返回上傳失敗后的error信息。而將所有的邏輯封裝在QNUploadPicManager內(nèi)部,這樣控制器里便精簡(jiǎn)了不少代碼,清爽了許多。

QNUploadPicManager.h 

  1. @interface QNUploadPicManager : NSObject  
  2.   
  3.  
  4. - (void)uploadImage:(UIImage *)image successBlock:(void(^)(NSString *urlStr))successBlock failureBlock:(void(^)(NSError *error))failureBlock;  
  5.   
  6.  
  7. @end  

QNUploadPicManager.m 

 

 

  

 

 

  

 

 

  

 

 

  

 

 

 

總結(jié):

這個(gè)界面比較核心的一個(gè)問題就是:要在控制器里提交表單,那怎樣把在UITableViewCell里的textField輸入的數(shù)據(jù)傳遞給控制器? 另外一個(gè)問題是一個(gè)邏輯比較復(fù)雜的界面,控制器勢(shì)必會(huì)很龐大,應(yīng)該有意的給控制器瘦身,不能把所有的邏輯都寫在控制器里。有關(guān)視圖顯示的就考慮放入U(xiǎn)ITableViewCell,有關(guān)數(shù)據(jù)的就考慮放入model。這樣既為控制器瘦身,也使代碼職責(zé)變清晰,耦合度降低。 

責(zé)任編輯:龐桂玉 來(lái)源: iOS大全
相關(guān)推薦

2016-05-09 10:41:03

算法分析開發(fā)

2017-02-23 15:37:44

OptionObject容器

2018-01-11 15:31:39

命令Linux關(guān)機(jī)

2025-02-24 08:20:00

AI代碼生成

2025-06-26 00:40:13

2017-07-10 13:09:45

前端Flexbox

2017-03-16 11:39:33

Openstack源碼姿勢(shì)

2023-01-30 07:41:43

2016-09-28 17:34:27

JavaScriptvueWeb

2020-08-31 06:54:37

注解脫敏ELK

2024-08-01 09:10:03

2021-07-12 11:35:13

Go協(xié)程Goroutine

2016-09-30 09:49:05

2016-12-12 08:48:24

2019-12-27 15:58:57

大數(shù)據(jù)IT互聯(lián)網(wǎng)

2024-09-25 08:22:06

2021-09-15 16:20:02

Spring BootFilterJava

2017-10-12 11:30:34

Spark代碼PR

2021-01-08 08:10:34

MySQL表空間回收

2019-10-30 17:06:50

AWS物聯(lián)網(wǎng)IoT
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产你懂的在线观看 | 亚洲综合天堂 | 中日韩av| 国产精品久久久99 | 9191av| 人人干97| 久久久精品高清 | 久久久久久亚洲 | 精品国产乱码久久久久久影片 | 国产成人精品久久二区二区 | 中文字幕免费中文 | 久久久久久高潮国产精品视 | 国产精品夜夜春夜夜爽久久电影 | 密色视频 | 一级美国黄色片 | 夜夜操天天艹 | 精品视频一区在线 | 久久久av | 中文字幕亚洲一区二区三区 | 青青久视频 | 午夜精品视频 | 亚洲欧美中文日韩在线v日本 | 日韩视频一区二区三区 | 欧美激情综合 | 午夜影院在线观看免费 | 色.com| 91av视频在线观看 | 岛国av免费在线观看 | www亚洲精品 | 亚洲视频1区 | 国产精品一区二区在线免费观看 | 成人国产精品久久久 | 欧美日本在线观看 | 羞羞的视频在线观看 | 亚洲国产成人精品女人久久久 | 欧美一二区 | 国产久| 香蕉视频久久久 | 色综合色综合 | 欧美11一13sex性hd | 欧美在线观看一区 |