六.用戶界面

六.用戶界面

主要討論如火如何最小化更新UI所需的時間。

6.1視圖控制器

視圖控制器的生命週期: 輸入圖片說明php

建立試圖控制器時須要遵循的基本最佳實踐:html

  • 保持視圖控制器輕量。在MVC結構應用中,控制器只是紐帶,而不是存放全部業務邏輯的地方。它甚至不屬於模型。業務邏輯應該屬於服務層或業務邏輯組件。
  • 不要在視圖控制器中編寫動畫邏輯。動畫能夠在獨立的動畫類中實現,該類接受視圖做爲參數傳入,這些視圖就是用來運行動畫的視圖。而後視圖控制器會將動畫添加至視圖或轉場效果上。
  • 使用數據源和委託協議,將代碼按照數據檢索,數據更新和其餘業務邏輯進行分離。視圖控制器只能用來選擇正確的視圖,並將它們鏈接到供應源。
  • 視圖控制器響應來自視圖的事件,如按鈕點擊事件或單元格的選擇事件,而後將它們鏈接至數據接收器。
  • 視圖控制器響應來自操做系統的UI相關事件,如方向變化和低內存警告。這可能會觸發視圖的從新佈局。
  • 不要編寫自定義的init代碼。若是視圖控制器被從新切換至XIB或故事板,那init方法永遠都不會被調用。
  • 不要在視圖控制器中使用代碼手工佈局UI,也不要在視圖控制器中實現所有的UI,視圖建立和視圖佈局邏輯等操做。使用nib或者故事板。
    手工佈局代碼不會持續好久,由於應用在不斷增加,而且設計也在改變。在從新設計方面,使用interface Builder比根據像素座標來手動編寫代碼更快。 若是某一設計被分在獨立的nib和故事板,能夠比較靈活運行A/B測試https://www.zhihu.com/question/20045543,由於在不一樣的約束之間很容易選擇最終須要的。並且自定義代碼不容易適配設備。
  • 建立一個實現公共設置的基類控制器。
  • 在各視圖控制器之間,使用category建立可複用的代碼。這樣就不會被限制只能使用預約義的基類,同時還能獲得複用帶來的好處。

6.1.1視圖加載

視圖初始化時會涉及兩個方法--loadview和viewDidLoad. 當添加一個新的視圖控制器時,經過Xcode生成的模板代碼只有viewDidLoad方法。當視圖控制器的view被請求時,loadview方法會被調用,但由於它還未被建立,因此會是nil。ios

三種加載視圖方式:git

  • 從nibs(xibs)
  • 使用故事板
  • 自定義代碼建立UI

若是經過覆寫loadview方法建立了自定義UIgithub

  • 將view屬性設置大片視圖層級的跟上。
  • 確保視圖正被其餘的視圖控制器所共享。
  • 不要調用[super loadview]

iOS用戶界面:Storyboards vs. NIBs vs. Custom Code http://www.jianshu.com/p/10dd75d34a20web

6.1.2視圖層級

視圖結構和渲染包括如下步驟: (1)構造子視圖。 (2)計算並提供約束。 (3)爲子視圖遞歸地執行步驟1和步驟2. (4)遞歸渲染 viewDidAppear:方法會由於過分動畫的緣由在約300毫秒後被調用。swift

6.1.3視圖可見性

視圖控制器提供了4個生命週期方法,以接受有關視圖可視性的通知。數組

  • viewWillAppear:當視圖層級已經準備好,且視圖即將被放入視圖窗口時,此方法會被調用。 在這個時刻,過渡動畫還未開始,視圖對終端用戶也是不可見的。不要啓動任何視圖動畫,由於沒有任何做用。緩存

  • viewDidAppear: 當視圖在視圖窗口展現出來,且過渡動畫完成後,此方法會被調用。 啓動或恢復任何想要呈現給用戶的視圖動畫。服務器

  • viewWithDisappear:該方法表示視圖將要從屏幕上隱藏起來。這多是由於其餘視圖控制器想要接管屏幕,或該視圖控制器將要出棧。此方法被調用時,沒有辦法可以直接判斷這是由當前視圖控制器要出棧仍是其餘視圖控制器入棧致使的。區分的惟一方法是掃描當前視圖控制器navigationController的viewController屬性

NSInteger index = [self.navigationController.viewControllers indexOfObject:self];
	if(index == NSNotFound){
		//即將出棧,銷燬
	}else{
		//只是保存狀態,暫停
	}
  • viewDidDisappear:當上一個/下一個視圖控制器的過渡動畫完成時,此方法會被調用。

高效使用生命週期事件的最佳實踐:

  • 不要重寫loadview。
  • 將viewDidLoad做爲最後的檢查點,查看來自數據源的數據是否可用。若是可用,則更新UI。
  • 若是每次都須要展現最新的信息,那麼就用viewwillappear:更新UI元素。
  • viewDidAppear:中開始動畫。
  • viewWillDisappear:來暫停或中止動畫。
  • viewDidDisappear:銷燬內存中的複雜數據結構。也能夠在這裏註銷與視圖控制器綁定的數據源通知,以及與動畫,數據源,UI更新有關的應用事件通知中心。

6.2視圖

基本規則:

  • 儘可能減小在主線程中所作的工做。
  • 避免較大的xibs或故事板。
  • 避免在視圖層次結構中多層嵌套。在層次結構的任何位置添加視圖時,它的祖先樹節點會執行值爲YES的setNeedsLayout:方法,當事件隊列正在執行時,該設置會觸發layoutSubviews:。這個調用代價較大,由於視圖必須根據約束從新計算子視圖的位置。
  • 儘量延遲加載視圖並進行重用。
  • 對於複雜的UI而言,最好使用自定義繪圖。這樣只會觸發一個視圖進行繪製,而不是多個子視圖,同時也避免了調用代價較高的layoutsubviews和drawRect:方法。

6.2.1UILabel

(1)使用字體、字體類型以及要被渲染的文本時,計算須要的像素數目。這是一個消耗較大的過程,應儘量少地去作。 (2)檢查要被渲染的寬度。 (3)檢查numberOfLines,計算將要展現的行數。 (4)sizeToFit是否被調用?若是是,計算高度。 (5)若是sizeToFit沒有被調用,檢查當前的內容可否在給定的高度下展現出來。 (6)若是frame不夠,使用lineBreakMode肯定隱藏或截斷的位置。 (7)使用字典、類型及顏色來渲染最終展現的文本。

6.2.2UIButton

渲染按鈕的方式:

選項 優勢 缺點
自定義文本 最簡單的方式,可直接使用 一般是比較呆板,毫無裝飾的按鈕
全尺寸資源 可自定義的背景 無需代碼便可實現 可實現A/B測試—圖像可 在運行試驗時下載 圖片打包在應用中,致使包變大
可變大小資源 可自定義的背景 無需代碼便可實現 <div>可實現A/B測試—圖像可</div> <div>在運行試驗時下載</div><div>包大小的增量相對較小</div> 資源的任何更改可能都須要從新計算 重置UIEdgeInsets值
使用CALayer和 貝塞爾路徑定義繪製 徹底是自定義繪圖 任何格式的更改或升級均可能須要更新 應用

6.2.3UIImageView

iOS仍舊不支持GIF動畫,只能建立animationImages的一個數組來存放能夠生成動畫的圖片。或使用自定義編碼和第三方庫。 自定義編碼:http://www.imagemagick.org/script/index.php 第三方庫:https://github.com/mattt/AnimatedGIFImageSerialization

使用UIimage和UIimageview的最佳實踐:

  • 對於已知的圖像,使用imageNamed:方法加載圖片。它能夠確保內容只被加載至內存一次,還能夠確保多個UIImage對象間改變用途。
  • 在使用imageNamed:方法加載圖片時,使用資源包。若是應用有一堆圖標,且每一個圖標都較小時,這種方法極其有用。能夠隨意建立相關圖像的多個目錄。若是想加載一個只使用一次的大圖像,最好謹慎思考一下,考慮使用imageWithContentsOfFile:代替資源目錄和imageNamed:方法,由於資源目錄緩存了這些圖片。
  • 對於其餘圖像,使用高性能的圖像緩存庫。AFNetWorking和SDWebImage。當使用內存中的圖片時,確保正確配置了內存的使用參數。不要使用硬編碼。讓它可以自適應--使用合理的RAM百分比能夠較好的進行配置。
  • 載入的圖像與即將渲染的UIImageView大小相同。由於調整圖像大小是一個耗費較大的操做。
  • 不管使用何種技術加載圖像,在非主線程中執行,最好在一個專用隊列中執行。尤爲要在非主線程中解壓JPG/PNG圖像。
  • 確認是否真的須要使用圖像,最好使用直接繪製的自定義視圖,而不是多個圖像。

6.2.4UITableView

輸入圖片說明

最佳實踐:

  • 在數據源的cellForRowAtIndexPath:方法裏,使用dequeueReusableCellWithIdentifier:進行單元格的重用。
  • 儘量避免動態高度的單元格。若是想要使用可變高度的單元格,最好選擇較高的單元格,由於這隻須要爲較少的單元格計算高度,從而減小了計算量。
  • 若是你真的須要動態高度的單元格,那麼定義一個規則來標記單元格爲髒的。若是某個單元格是髒的,計算它的高度並緩存。在委託的heightForRowAtIndexPath:回調中繼續返回緩存的高度,直到單元格再也不被標記爲髒。
  • 當用自定義視圖重用單元格時,要避免經過調用layoutIfNeeded每次都對其進行佈局。
  • 避免透明的單元格子視圖。
  • 在快速滾動時,考慮使用界面外殼。當用戶快速滾動列表視圖時,雖然使用了全部的優化,但視圖的重用和渲染仍然須要超過16毫秒,還有可能出現偶發的丟帖現象,從而致使不流暢的體驗。在這種狀況下,使用一個界面外殼是一個較好的選擇,外殼能夠被預約義,它的惟一目的就是告訴終端用戶這些部分即將展現一些數據。當滾動速度下降,並低於閾值時,刷新最終的視圖並填充數據。可使用與列表視圖相關聯的panGestureRecognizer屬性獲取速度值。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
	CGPoint velocity = [self.tableView.panGestureRecognizer velocityInView:self.view];
	self.velocity = velocity;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	
if (fabs(self.velocity.y) > 2000){
		//返回界面外殼
	}else{
		//返回真正的單元格
	}
}
  • 避免漸變、圖像縮放以及任何屏幕外的繪製。

6.2.5UIWebView(iOS7),WKWebView(iOS8),SFSafariViewController(iOS9)

UIWebView是用於渲染未知或動態內容的最多見視圖。一般狀況下,會將web視圖指向一些內嵌的HTML或web URL. 經常使用場景:

  • 除了原生UI渲染登錄表單。若是想用CAPTCHA(驗證碼https://zh.wikipedia.org/wiki/驗證碼)篩選刷屏的機器人,就須要爲全部的格式提供支持,並將其打包至應用中,或將用戶指向一個網頁登錄URL,讓服務器生成任何須要的複雜UI。
  • 在任何應用中顯示隱私政策或使用條款。由於這些會隨着時間變化,而且須要大量的格式化,使用原生視圖不是較好的選擇。
  • 新聞或文章閱讀器,由於大部分的文章都是Web建立的,幾乎都是HTML。
  • 郵件應用。例如,初始郵件是HTML形式,當呈現消息或跟帖,以及撰寫回復時。

須要展現較小的富文本,使用UIlabel的NSAttributedString.

最佳實踐:

  • UIWebView可能比較笨重且遲鈍,因此儘可能複用web view。同時,UIWebView也因內存泄露而知名。不管什麼時候想向用戶展現新的URL,先將內容重置爲空的HTML。這樣就能確保web view不會將以前的內容展現給用戶。想要實現這一功能,在loadRequest:方法後調用loadHTMLString:baseURL:便可。
  • 實現webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 方法。要留意URL scheme。若是是http或https之外的東西,須要注意:應用應該知道如何處理這種狀況,或警告用戶該網站正試圖脫離應用。
  • 能夠經過stringByEvaluatingJavaScriptFromString:方法建立一個橋來鏈接應用和JavaScript(iOS8後WKWebView的evaluateJavaScript:completionHandler: 代替),從而字當前已經加載的web頁面執行JavaScript。
  • 實現委託的webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error方法,保持對全部可能出現的錯誤進行追蹤。若是域名與NSURLErrorDomain相等,那麼NSError對象有不一樣的意義。
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
	if ([NSURLErrorDomain isEqualToString:error.domain]){
		switch (error.code) {
			case NSURLErrorBadURL:
    //處理錯誤的URL
				break;
			case NSURLErrorTimedOut:
				//處理超時
				break;
			case NSURLErrorUnknown:
				//未知
				break;
			//其餘。。。
			default:
    break;
		}
	}
}
  • UIWebView不會通知任何的HTTP協議錯誤,例如響應是404或500錯誤。因此須要出發兩次調用,第一次使用自定義的NSURLConnection調用,而後是經過web view的調用。
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if (self.shouldValidate){
	[NSURLConnection connectionWithRequest:request delegate:self];
		return NO;
	}
	return YES;
}

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
	NSInteger status = [(NSHTTPURLResponse*)response statusCode];
	if (status >= 400){
		//展現警報或隱藏web view ---不要展現錯誤的網頁
	}else{
		self.shouldValidate = YES;
		[self.webView loadRequest:connection.originalRequest];
	}
}
  • 嵌入的UIWebView的容器應該提供一下元素。

    1.導航按鈕。 2.重載按鈕。 3.取消按鈕,用於取消當前正在加載的頁面。 4.用於展現標題的UILable. 5.退出web view的關閉按鈕。

    6.2.6 自定義視圖

輸入圖片說明

簡單複合視圖UI的基本實現可能包含如下內容: (1)UIImageView做爲頭像圖片。 (2)用UIlabel的NSAttributedText展現用戶名稱。 (3)UITextView,展現主要內容,由於裏面可能包含連接。 (4)通常數據展現UILable。

自定義視圖則經過在drawRect:中直接繪製所有的元素。

1.複合視圖 建立一個UITableViewCell的子類,勾選XIB file。而後直接在XIB排列所需元素。 針對複合視圖,在動畫過程當中使用視圖光柵化http://swift.diagon.me/shouldRasterize/

2.直接繪製 不勾選XIB文件選項,覆蓋drawRect:方法以自定義渲染元素。

優缺點比較:運行時性能和代碼維護 *直接繪圖的自定義視圖中的運行時性能更好。(經過首次初始化時間,後續初始化,滾動後的首次初始化,滾動後的第二次初始化,重用等對比) *從維護的角度來看,代碼會難以維護和發展。一旦應用穩定下來,能夠比較明確的將複合UI換成直接繪圖。

6.3自動佈局

自動佈局使用本地約束(元素彼此之間的位置關係)會比使用全局約束(相對於父視圖的位置)更快。 自動佈局衡量展現視圖和渲染視圖所需的時間,若是超過了閾值,應該考慮使用自定義代碼。閾值根據具體的應用而定。

6.4尺寸類別

ppi(像素密度)不是點到像素的比例。 尺寸類別:https://isux.tencent.com/ios9-guideline-ch1.html 尺寸類須要自動佈局。若是由於性能緣由不選擇使用自動佈局,那就不能使用尺寸類

6.5iOS8中新的交互特性

6.5.1交互式通知

交互式通知,容許用戶提供一個針對輸入的快速響應。 交互式通知的可能動做:

  • 郵件:回覆,標記爲垃圾。
  • 信息:提醒,回覆。
  • 在社交應用中評論信息:回覆評論,點贊評論。
  • 任務和提醒:稍後,標記完成。

6.5.2應用擴展

iOS8可用的應用擴展:

  • 今日:通知中心的「今日」,幫助用戶快速更新或完成某一任務。
  • 自定義鍵盤
  • 分享:容許用戶更加無縫地跨應用共享數據。
  • 行動:幫助用戶查看或改變在主應用中發起的內容。
  • 照片編輯:容許用戶編輯照片應用中的照片或視頻。
  • 文檔提供者:容許其餘應用訪問本身應用所管理的文件。
相關文章
相關標籤/搜索