在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 響應(yīng)式視圖
與四軸無人機的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項目介紹
Swift 的強大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學習
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學習的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機與照片
音頻 API 一覽
交互式動畫
常見的后臺實踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機項目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學無止境
XCTest 測試實戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機捕捉
語言標簽
同步案例學習
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機工作原理
Build 過程

響應(yīng)式視圖

在任何應(yīng)用當中將界面做好都不是一件容易的事情。在一個小小的四邊形中呈現(xiàn)內(nèi)容以及互動的結(jié)合看似容易,其實就算是在很小的應(yīng)用當中也很容易寫出混亂不堪的視圖代碼。在有很多工程師合作的復(fù)雜項目當中,比如 Facebook 的新鮮事頁面,這些視圖的開發(fā)和維護是有相當難度的。

最近我一直在開發(fā)一個叫做 Components 的庫來簡化 iOS View 的開發(fā)。它強調(diào)單項數(shù)據(jù)流動:從不可變的模型到不可變的”組件”,這些組件描述了視圖應(yīng)該如何被設(shè)置。這個庫從現(xiàn)在網(wǎng)絡(luò)開發(fā)中很流行的 React Javascript 庫 中汲取了很多靈感。React 通過一個叫做 “虛擬 DOM” 的概念來抽象化對 DOM 處理。同樣地,Components 會抽象化對 UIView 層次的處理。

在這篇文章中我會著重說明使用 Components 在 iOS 上來呈現(xiàn)視圖的一些好處,并且分享一些我學習到的經(jīng)驗。相信在大家自己的應(yīng)用中也能夠用得上。

零數(shù)學布局

假設(shè)我們有四個子視圖而且我們想將它們垂直的分布,水平方向上使用全寬。經(jīng)典的辦法是去實現(xiàn) -layoutSubviews-sizeThatFits: 這兩個函數(shù),這樣做需要 52 行代碼. 因為其中有很多數(shù)學運算,第一眼看上去不是很容易看出來是在豎直地擺放視圖。在這兩個函數(shù)中有點重復(fù)的地方,所以在未來的修改中保持同步并不容易。

如果我們使用蘋果的自動布局 API,我們可以獲得小小的改進:34行代碼.。同時數(shù)學運算以及重復(fù)的代碼問題亦可以解決!但是我們卻換來了另外一些問題:自動布局設(shè)置起來很困難,[^1] 調(diào)試起來也很費力,[^2] 并且復(fù)雜的層次會讓運行時性能打一些折扣。[^3]

^1 Interface Builder 簡化了自動布局,但是因為 XIBs 文件難以融合,你很難在大的團隊里面使用它們。

^2 有很多關(guān)于如何調(diào)試自動布局的文章博客

^3 我們用自動布局制作了一個很簡單的新鮮事頁面,做到 60 幀每秒是非常的困難。

Components 從 CSS Flexbox specification 的布局系統(tǒng)中中吸取了靈感。我不會介紹太多的細節(jié),如想進一步學習請參照 Mozilla 的高質(zhì)量教程 。因為 Flexbox 大幅簡化了布局,相對應(yīng)的 Components 僅僅需要18行代碼。你也不需要使用任何數(shù)學運算以及基于字符串的視覺格式語言。

用下面的代碼就可以依靠 Components 來做到同樣的垂直擺放視圖,對于不熟悉的人們來說,句型看上去可能會很奇怪 -- 稍后再來解釋:

@implementation FBStoryComponent

+ (instancetype)newWithStory:(FBStory *)story
{
  return [super newWithComponent:
          [FBStackLayoutComponent
           newWithView:{}
           size:{}
           style:{.alignItems = FBStackLayoutAlignItemsStretch}
           children:{
             {[FBHeaderComponent newWithStory:story]},
             {[FBMessageComponent newWithStory:story]},
             {[FBAttachmentComponent newWithStory:story]},
             {[FBLikeBarComponent newWithStory:story]},
           }]];
}

@end

那些個波形括號!

沒錯,我們用的是 Objective-C++。聚合實例化給我們一個簡明并且類安全的方法來指明樣式結(jié)構(gòu)。以下是另外幾個有效的 style: 值:

style:{} // default values
style:{.justifyContent = FBStackLayoutJustifyContentCenter}
style:{
  .direction = FBStackLayoutDirectionHorizontal,
  .spacing = 10,
}

使用像 std:vectorstd:unordered_map 這樣的標準庫中的容器比我們在 Objective-C 中使用相對應(yīng)容器有更強的類型安全性。我們同時能夠用棧來調(diào)用臨時視圖數(shù)據(jù)結(jié)構(gòu),提升性能。

Components 在句型風格上還有另外一些有些奇怪的地方 (為了簡介而使用 +newWith... 代替 -initWith...,以及非常規(guī)的縮進等),這要在更多的上下文中才解釋得通 --- 這個話題單獨可以再寫一篇文章?,F(xiàn)在我們回到主題。

聲明式而不是命令式

就算是全新的句型,也不難看懂我們擺放視圖的 Components 版本。用一個重要的原因是:它是聲明式的而不是命令式的。

大多數(shù)的 iOS 視圖代碼讀起來感覺像是一系列的指令:

  • 建立一個新的 header 視圖。
  • 將其存進 _headerView 實例變量。
  • 加入視圖中
  • 加入限制將頭視圖的左右兩邊和父視圖對齊。
  • ...對其他視圖做相似的操作
  • 加入更多擺放視圖用的限制

而 Components 的代碼是聲明式的:

  • 一個 story 視圖是通過將四個組件垂直擺放并且左右拉升來做到的。

將這兩者的區(qū)別想象成給工人們列出所有材料和指示的清單,和僅僅給他們一張藍圖的區(qū)別。延伸一下這個比喻,一個建筑師不應(yīng)該在工地上四處奔走來告訴建筑工人如何去干他們的活 -- 這樣的話會太過于混亂。宣言性的技巧著重于什么需要被完成,而不是如何去完成它;結(jié)果是,你得以將精力集中在要解決的問題上而不是實現(xiàn)細節(jié)上。

使用 Components 的時候,不用去操心本地變量和屬性。你不需要在創(chuàng)建視圖的地方,添加限制的地方和使用模型來配置視圖的地方來回跳躍。所有的事情就在你面前好好的放著。

我的建議是:永遠傾向于聲明式風格而不是命令式風格,這樣一來代碼更易于讀懂,也更易于維護。

混合優(yōu)于繼承

小小測驗:以下代碼是干什么的?

- (void)loadView {
  self.view = [self newFeedView];
}

- (UIView *)newFeedView {
  return [[FBFeedView alloc] init];
}

如果使用了繼承,那它可以是在做任何事情。可能 -newFeedView 在子類中被重寫了,返回了一個完全不同的視圖。又或許 -loadView 被重寫去調(diào)用了一個不同的函數(shù)。在大規(guī)模的代碼庫中大量使用子類會使得閱讀代碼和理解它們實際做了什么變得困難。[^4] 繼承產(chǎn)生的問題在我們使用 Components 改寫新鮮事頁面之前經(jīng)常發(fā)生,比如 FBHorizontalScrollerView 有很多子類重寫了不同的方法,這使得超類難以閱讀和重構(gòu)。

^4 objc.io 在以前介紹過這個主題,這篇維基百科文章 也做了很好地介紹。

Components 永遠都是被混合的,從來不會被繼承。將它們想象成小的基礎(chǔ)模塊,你可以將它們拼裝在一起組成非常棒的東西。

但是對混合的大量使用會造成非常深的層次,而深的 UIView 層次會將滑動變得非常緩慢。有一點需要特別指明的是,其實是存在那種完全不需要為其創(chuàng)建視圖的 component 的。[^5] 在實踐中,大多數(shù)的組件是不需要視圖的。 就拿 FBStackLayoutComponent 來作例子;它將它的子視圖碼放在一起,但是它并不需要在層級中的一個視圖去執(zhí)行這項任務(wù)。

^5 相同地,在 React 中,也并非每一個組件都會創(chuàng)造一個相應(yīng)的 DOM 元素。

盡管新鮮事頁面的組件層次有好幾十層,但是得到的視圖層其實才有三層。我們獲取了所有混合帶來的好處卻沒有付出什么代價。

如果說我從龐大的代碼庫中學到一樣?xùn)|西的話,就是不要使用繼承!轉(zhuǎn)而使用混合或者其他的模式。

自動回收

使用 UITableView 時的重要一步是 cell 的回收:少量的 UITableViewCell 實例會被反復(fù)地利用。這是實現(xiàn)驚人的滑動速度得以實現(xiàn)的重要原因。

但是,要想在多工程師分享的代碼庫中妥當?shù)鼗厥諒?fù)雜的 cells 并不容易。在開始使用 Components 之前,我們曾添加一個功能來逐漸淡出一個故事的一部分界面,但是我們忘記了在回收時重設(shè) alpha 的值,這樣一來其他的故事也被隨機的淡化了!另一個例子,忘記妥善地重設(shè) hidden 屬性導(dǎo)致隨機地丟失或者覆蓋某些內(nèi)容。

如果使用 Components,你永遠不需要擔心回收。庫會來很好地管理它。不同于寫祈使性的代碼來正確地設(shè)置可能在任何狀態(tài)中所回收的視圖,你只需要指明一個視圖狀態(tài)即可。庫會計算出完成這項任務(wù)所需的最少步驟。

一次優(yōu)化,處處受益

因為所有對視圖的處理全都由 Components 的代碼完成,我們得以通過優(yōu)化一個算法來提升各個地方的速度。相較于修改 400 個 UIView 子類并心中默念:“這可是一個龐大的項目”來說,優(yōu)化一個地方并且處處受益要來的有意義的多。

比如說,我們加入了一個優(yōu)化來確保在重新設(shè)置視圖的時候,除非值確實被改變了,否則不去使用屬性的 setter (比如 -setText)。盡管大多數(shù)的 setter 在值沒有變化的情況下還是非常有效率的,但我們還是在性能上得到了提升。另外一個優(yōu)化確保了只有在必要的情況下才重新排序視圖 (通過使用 -exchangeSubviewAtIndex:withSubviewAtIndex:),因為這項操作相對來說成本很高。

最好的部分是,這些優(yōu)化并不需要任何人去改變寫代碼的方式。開發(fā)者們能夠?qū)W⑼瓿扇蝿?wù)而不是了解高成本的操作并學會去避免他們 - 這是一個對整個團隊來說非常大的幫助。

動畫的挑戰(zhàn)

沒有一個框架能夠解決所有的問題,響應(yīng)式 (reactive) 的界面框架中一個有挑戰(zhàn)性的問題是實現(xiàn)動畫相較于使用傳統(tǒng)視圖框架要更困難一些。

響應(yīng)式的界面開發(fā)鼓勵將狀態(tài)之間的切換明確化。舉個例子,有一個界面會刪減一部分文本內(nèi)容,但是允許用戶按一個按鈕來展開并且查看全部的文本。這個可以輕易通過兩個狀態(tài)來做到:{Collapsed, Expanded}。

但是如果你想把展開文本做成動畫,或者讓用戶自己通過拖拽去精確地控制顯示多少文本內(nèi)容,那么就不可能只是用兩個狀態(tài)了。有數(shù)以百計的狀態(tài)對應(yīng)著動畫中某個時刻有多少文本會被顯示出來。響應(yīng)式框架要求你在開始的時候就把狀態(tài)變化安排好,正是因為這一點動畫才變得如此困難。

我們開發(fā)了兩種手段來管理 Components 中的動畫:

  • 可以使用一個叫做 animationsFromPreviousComponent: 的 API 來宣言性地表達靜態(tài)的動畫。比如說,一個組件可以指明在第一次出現(xiàn)的時候使用漸入的效果。
  • 動態(tài)動畫可以通過使用一個 “逃出窗” 來回到傳統(tǒng)的祈使和可變的代碼來完成。你不會得到宣言性代碼和狀態(tài)管理明確化帶來的好處,但是你可以自由地使用 UIKit 的威力。

我們的設(shè)想是開發(fā)強大的工具去用宣言性的代碼來寫出簡約的動態(tài)動畫,我們只是還沒有完成這個計劃而已。

React Native

在 Facebook,我們最近宣布了 React Native,一個運用 React Javascript 庫來處理本地應(yīng)用中 UIView 層次的框架,與網(wǎng)頁版不同,這個庫使管理的是 UIView 而非網(wǎng)頁中的 DOM 元素。要告訴大家的是,Components 庫并不是 React Native,而是一個單獨的項目,雖然這可能讓人有些驚訝。

它們的區(qū)別是什么?其實很簡單:當我們用 Components 重建新鮮事頁面的時候 React Native 還沒有被發(fā)明出來。在 Facebook, 每一個人都非??春?React Native 的前景。并且已經(jīng)應(yīng)用在Mobile Ads ManagerGroups兩個應(yīng)用中使用了。

和所有框架一樣,也存在取舍;比如說,Components 選擇使用 Objective-C++ 是因為它的類型安全性和性能,但是 React Native 對 Javascript 的運用讓在開發(fā)環(huán)境下即時更新成為可能。這些項目經(jīng)常會分享一些推動兩者共同進步的創(chuàng)意。

AsyncDisplayKit

那么用來驅(qū)動 Facebook Paper 應(yīng)用的 UI 框架 AsyncDisplayKit 呢?它增添了在后臺線程計算和渲染的能力,讓你無需面對使用 UIKit 主線程會遇到的麻煩。

從設(shè)計哲學的角度上來說,AsyncDisplayKit 和 UIKit 的關(guān)聯(lián)比和 React 要更強。不像 React,AsyncDisplayKit 沒有強調(diào)使用宣言性句法,混合以及不可變性。

像 AsyncDisplayKit 一樣,Components 在后臺線程進行組件創(chuàng)造和分布 (這個很容易,因為我們的模型對象和組件本身全都是不可變的 - 不可能出現(xiàn)競態(tài)條件!)

AsyncDisplayKit 能夠進行復(fù)雜的手勢驅(qū)動的動畫,這一點正是 Components 的弱項所在。這樣一來做選擇就很容易了:如果你在設(shè)計一個復(fù)雜的手勢驅(qū)動的界面,AsyncDisplayKit 應(yīng)該是正確的選擇。如果你的界面看起來和 Facebook 的新鮮事頁面更類似,那么 Components 是恰當?shù)倪x擇。

Components 的未來

Components 庫在所有的顯示大量信息的頁面都會用到 (新鮮事,時間軸,群,事件,頁面和搜索等等) 并且正在快速地在 Facebook 應(yīng)用的其他部分被應(yīng)用起來。用簡潔,宣言性,可混合的組件是非常有趣的。

你可能覺得 Components 中的一些東西聽起來很瘋狂。但是用點時間消化一下,你可能會自己挑戰(zhàn)之前的一些假設(shè),但是這些東西我們用著非常好而且對你們可能也有幫助。如果你想學習更多,可以看看這個演講,它深入地討論了 Components 的一些細節(jié)。為什么用 React? 的博文和它鏈接的資源都是非常好的參考。

我們非常想和社區(qū)分享 Components 背后的代碼,而且我們馬上要著手去做。如果你有想法要分享,隨時都可以聯(lián)系我 - 尤其是關(guān)于動畫的想法!

上一篇:Android Intents下一篇:Views