官方文檔連接:texturegroup.org/docs/gettin…css
Texture 的基本單位是 Node
,ASDisplayNode
是 UIView
和 CALayer
的抽象,它與 UIKit
的不一樣在於:html
Node
能夠異步繪製,且線程安全,你能夠在在異步線程中進行實例化和配置它們的層級結構。前端
爲了保持用戶界面的流暢,你的 App 應該以 1/60 秒的幀率呈現, 這意味着主線程有 1/ 60 秒來處理一幀,也就是說,主線程須要在 16 毫秒內來執行全部的佈局和繪圖代碼,而因爲一些系統級別的開銷,你的佈局繪圖代碼通常執行超過 10 毫秒,就可能引發掉幀。node
Texture
可讓你將圖像解碼、文本渲染和其餘消耗性能的 UI 操做從主線程剝離,以保證主線程能夠流暢的響應用戶的交互,Texture
還有其餘的使用竅門,咱們稍後會講到。ios
若是你會使用 UIView
,那麼你就已經知道如何使用 Node
了,UIView
中絕大部分的方法 Node
都有映射,而且 UIView
和 CALayer
大多數的屬性,也是可使用的。當屬性和方法的命名有差別時,例如 .clipsToBounds
和 .masksToBounds
,Node
默認使用 UIView
的命名,惟一例外的是 Node
使用 .position
替代了 .center
。css3
固然,你也能夠經過 node.view
或 node.layer
直接調用原生屬性和方法,但要確保它會在主線程上執行!git
Texture 已經提供了多種多樣的 Node
來替換你習慣使用的大部分 UIKit 組件,如今你已經能夠徹底經過 Texture 開發大規模的 App 。github
當你將 Texture 集成到一個項目中時,一個常見的錯誤是將 Node
直接添加到已有的視圖中,這樣作的結果是你的節點在渲染時會閃爍。swift
正確的作法是,你應該把節點添加到 Node 容器中,由 Node 容器負責管理這些節點,你能夠把 Node 容器理解爲 UIKit 和 ASDK 之間的橋樑。xcode
Texture 的佈局很是強大,相對於傳統的 Frame,AutoLayout 等方式而言比較獨特,但對前端工做者並不陌生,Texture 的佈局基於 CSS FlexBox。它提供了指定自定義節點的大小和其子節點佈局的聲明方式,當全部的節點同時被渲染時,經過爲每一個節點提供的 ASLayoutSpec
異步計算 size 和佈局。
它和 UIStackView 很像,但支持低版本 iOS。
Texture 提供了在 UIKit 或 Foundation 中沒法找到的各類高級開發功能,咱們的開發人員發現,Texture 能夠簡化它們的架構,從而提升了開發效率。
完整列表即將推出...
沒放出來講個啥?
若是你第一次使用 Texture,咱們建議你看看 ASDKgram 示例,咱們已經撰寫了一個 Guide(即將推出),指導你如何一步一步將 Texture 集成到一個 App 中。
沒放出來講個啥?
若是您遇到任何問題,請到咱們的 GitHub 或 Slack 尋求幫助。
在 Slack 社區,和 Texture 的核心團隊及 700+ Texture 開發者一塊兒,實時 Debug、獲取更新和進行交流,點此註冊。
查看咱們的示例庫,若是你第一次使用 Texture,咱們建議你從 ASDKgram 開始,這個示例同時使用 UIKit 和 Texture 實現了照片Feed,你能夠對比查看它們的區別,它的特色是:
Texture 強大的佈局系統基於CSS FlexBox 模型,這些網站對於學習 FlexBox 的基本知識很是有用。
Texture 可使用 CocoaPods 和 Carthage 安裝,使用時不要忘記導入頭文件:
#import <AsyncDisplayKit/AsyncDisplayKit.h>複製代碼
若是你使用 Swift 開發,可使用 Objective-C bridging header 進行橋接,若是你在安裝中遇到任何問題,請在 GitHub 或 Slack 聯繫咱們。
使用 CocoaPods 安裝,將如下內容添加到 Podfile:
target 'MyApp' do
pod "Texture"
end複製代碼
徹底退出 Xcode,打開終端,cd 到項目目錄,執行下面的命令:
pod install複製代碼
若是要更新 Texture,打開終端,cd 到項目目錄,執行下面的命令:
pod update Texture複製代碼
不要忘記使用 .xcworkspace
打開,而不是 .xcodeproj
。
使用迦太基須要建立一個Cartfile 列表,而後執行
carthage update
,將依賴項下載到Cathage/Checkouts
文件夾中,並將它們構建到位於Carthage/Build
文件夾中,開發人員須手工集成到項目中。
Texture 也能夠經過迦太基安裝,將如下內容添加到 Cartfile 以獲取最新的 release 版本:
github "texturegroup/texture"複製代碼
或者獲取主幹:
github "texturegroup/texture" "master"複製代碼
Texture 有本身的 Cartfile,指明瞭本身的依賴項,Texture 所需的依賴會自動安裝,安裝 Texture 依賴須要使用終端,在Carthage/Checkouts 目錄下,執行:
carthage update複製代碼
確認 Texture、PINRemoteImage (3.0.0-beta.2)、PINCache 所有獲取和構建,Texture 的 Cartfile 會自動處理這些依賴關係。
打開 Xcode,將所需的框架拖到 TARGETS - General -Linked Frameworks and Libraries
。
Texture 不支持 Carthage 更輕量的使用方式,你須要手動添加項目文件, 這是由於 Texture 的依賴 PINCache 尚未項目文件,
PINCache 是 PINRemoteImage 一個嵌套的依賴。
若是沒有 PINRemoteImage 和 PINCache,你將沒法完整的使用 Texture 圖像功能集。
請閱讀 GitHub 上的官方發佈說明。
將如下內容添加到你的 pod 文件中:
pod 'Texture', '>= 2.0'複製代碼
在終端執行:
pod repo update
pod update Texture複製代碼
一旦你更新到2.0,你會看到許多棄用警告。 別擔憂!這些警告是安全的,由於咱們已經橋接了全部舊的 API,以便在遷移到新的 API 以前對 2.0 進行測試。
若是你的 App 沒法構建成功而不是僅顯示警告,那麼你的項目中可能出現錯誤,你有幾個選擇:
ASBaseDefines.h
中的第 74 行更改成 # define ASDISPLAYNODE_WARN_DEPRECATED 0
當你的 App 構建成功並運行,你須要測試它以確保一切正常工做。 若是你發現任何問題,請嘗試在該區域採用新的 API 並從新測試。
你可能會注意到一個關鍵的變化:
ASStackLayoutSpec的.alignItems
屬性默認值更改成 ASStackLayoutAlignItemsStretch
而不是 ASStackLayoutAlignItemsStart
,這可能會在 UI 中形成失真。
若是還有其餘問題,請提交 GitHub issue,咱們很樂意幫助你!
異步併發渲染和 FlexBox 佈局已經很是強大,但 Texture 作到的不止於此,另一個重要的層面是智能預加載的思想。
正如在開始使用 Texture 這節中講的那樣,在一個節點容器的上下文以外使用一個節點有一些弊端的。這是由於全部的節點都具備當前界面狀態的概念,這個狀態命名爲 interfaceState
。
interfaceState
這個屬性是不斷更新的,它的更新由 ASRangeController
所控制,ASRangeController
又由全部的節點容器在內部建立和維護。
在容器外部使用的節點不會被 ASRangeController
更新狀態,所以這有時會致使閃爍,緣由是這些容器外的節點由於狀態的錯誤,在節點被渲染到屏幕後又進行了一次渲染。
當將節點添加到滾動或分頁界面時,它們一般位於如下範圍中的一個。這意味着當滾動視圖被滾動時,它們的界面狀態將隨着它們的移動而更新。
一個節點的所處的範圍會是如下範圍中的一個:
界面範圍 | 描述 |
---|---|
Preload | 節點還不可見,這時節點收集外部源,外部源多是 API 或者本地磁盤。 |
Display | 節點開始渲染,包括文本的光柵化已經圖像解碼等。 |
Visible | 節點可見,在屏幕上至少擁有一個像素。 |
每一個範圍的大小以整個屏幕的尺寸做爲參照, 默認的 size 在許多用例中都能很好地工做,你也能夠經過在滾動節點上設置範圍參數來調整它們。
在上面的一個滾動集合的示例圖片中,用戶正在向下滾動。正如你所看到的,用戶滾動方向區域(領先方向)要比用戶離開方向區域(尾隨方向)大得多。爲了保持內存的最佳使用,當用戶改變滾動方向時,兩個區域會動態地交換。這使你沒必要考慮滾動方向的變化,只需關注領先方向和尾隨方向的區域大小。
在這張圖中,你能夠看到智能的預加載是如何進行工做的,你能夠看到在一個垂直的滾動容器中,雖然有些節點還未在設備屏幕中出現,可是它有一個範圍控制器,屏幕外的節點處在 Preload 數據準備範圍和 Display 渲染準備範圍。
當用戶滾動時,節點在這三個範圍中切換,並經過加載數據,渲染等做出適當的反應。你建立的節點子類能夠經過實現相應的回調方法進入此機制。
class TZYNode: ASDisplayNode {
override func didEnterPreloadState() {
print("進入數據加載範圍")
}
override func didExitPreloadState() {
print("離開數據加載範圍")
}
override func didEnterDisplayState() {
print("進入渲染範圍")
}
override func didExitDisplayState() {
print("離開渲染範圍")
}
override func didEnterVisibleState() {
print("進入可見範圍")
}
override func didExitVisibleState() {
print("離開可見範圍")
}
}複製代碼
咱們強烈建議你在節點容器中使用 Texture 的節點, Texture 提供如下節點容器:
節點容器 | 等價於 UIKit |
---|---|
ASCollectionNode | 代替 UICollectionView |
ASPagerNode | 代替UIPageViewController |
ASTableNode | 代替UITableView |
ASViewController | 代替UIViewController |
ASNavigationController | 代替UINavigationController,實現 ASVisibility 協議。 |
ASTabBarController | 代替UITabBarController,實現 ASVisibility 協議。 |
示例代碼和特定示例項目會在每一個節點容器的文檔中突出顯示。
節點容器自動管理其子節點實現智能預加載,這意味着節點的全部佈局計算,數據讀取,解碼和渲染都將會異步完成,這就是爲何咱們建議將節點放進節點容器中使用的緣由。
請注意,儘管你能夠直接使用節點而不加入節點容器,但除非你添加其餘回調,不然這個容器外的節點只會在屏幕出現時纔會開始渲染。如同 UIKit 所作的那樣,這可能致使性能降低和內容閃爍。
在 UIKit 組件上使用節點的一個主要優勢是,全部的節點都預先在主線程佈局並繪製,這樣主線程就能夠當即響應用戶交互事件,而無需先處理控件的渲染,Texture 提供如下節點:
節點 | 等價於 UIKit |
---|---|
ASDisplayNode | 代替 UIView,全部的 Node 都繼承自 ASDisplayNode。 |
ASCellNode | 代替 UITableViewCell&UICollectionViewCell,須要和 ASTableNode,ASCollectionNode 和 ASPagerNode 共同使用。 |
ASScrollNode | 代替 UIScrollView,這個節點對於建立自定義的,包含其餘節點的可滾動區域很是有用。 |
ASEditableTextNode | 代替 UITextView。 |
ASTextNode | 代替 UILabel。 |
ASImageNode | 代替 UIImage。 |
ASNetworkImageNode | 代替 UIImage。 |
ASMultiplexImageNode | 代替 UIImage。 |
ASVideoNode | 代替 AVPlayerLayer。 |
ASVideoPlayerNode | 代替 UIMoviePlayer。 |
ASControlNode | 代替 UIControl。 |
ASButtonNode | 代替 UIButton。 |
ASMapNode | 代替 MKMapView。 |
儘管與 UIKit 組件大體至關,但通常而言,Texture 節點提供了更高級的功能和便利。 例如,ASNetworkImageNode
能夠自動加載網絡圖片和進行緩存管理,甚至支持漸進式 JPEG 和動畫 GIF。
AsyncDisplayKitOverview 示例應用程序給出了上面列出的每一個節點的基本實現。
全部的 Texture 節點都繼承自 ASDisplayNode
。
原圖使用花體英文,查看原圖。
右側的節點是 UIKit 元素的封裝。 例如,ASScrollNode
封裝了一個 UIScrollView
,而 ASCollectionNode
封裝了一個 UICollectionView
。 liveMapMode
中的 ASMapNode
是 UIMapView
的封裝。
liveMapMode 未在此節和以前章節出現,不清楚是否爲筆誤。
建立子類時最重要的區別是你使用的是 ASViewController
仍是 ASDisplayNode
,這聽起來很明顯,可是由於這其中有一些微妙的差別,因此記住這點仍是至關重要的。
雖然實例化節點與 UIView
相似,但須要遵循一些原則,以確保你充分利用了它的能力,並確保節點按照預期的方式運行。
在使用 initNodeBlocks
時,這個方法會後臺線程上被調用。可是,由於沒有其餘方法會在 init
完成以前運行,因此這個方法不須要加鎖。
須要記住的最重要的一點是,init
方法必須可以在任何隊列上調用。最值得注意的是,你永遠不該該在節點初始化方法中初始化任何 UIKit 對象,以及調用 node.layer
node.view.x
等與view
或 layer
有關的操做,也不該該在這個方法中爲節點添加手勢,這些事件應該在 didLoad
方法中進行。
這個方法在概念上相似於 UIViewController
的 -viewDidLoad
方法,當後臺視圖初始化完成時,它會被調用一次,它保證會在主線程上被調用,是執行任何 UIKit 代碼合適的地方,例如添加手勢識,更改 view
和 layer
,初始化 UIKit 對象。
該方法定義了節點的佈局,並在後臺線程上進行了大量的計算。此方法是你聲明、建立和修改 ASLayoutSpec
佈局描述對象的地方,該對象描述了節點的 size,以及其子節點的 size 和 position,是你放置大部分佈局代碼的地方。
ASLayoutSpec
對象直到在此方法中返回前是可變的。 在這以後,這個對象將不可改變,須要注意的是你不須要緩存 ASLayoutSpec
對象以備後用,咱們建議你在必要時從新建立佈局描述。
因爲它在後臺線程上運行,所以你不能在這個方法中調用 node.view
或 node.layer
以及它們的屬性。 此外,除非你明確知道本身在作什麼,不然不要在此方法中建立其餘節點。 另外,重寫此方法並不必定須要調用 super
方法。
在此方法中調用 super
將,會使用 layoutSpec
對象計算佈局,全部子節點都將計算其 size 和 position。
-layout
在概念上相似於 UIViewController
的 -viewwilllayoutsubview
,這是一個更改 hidden
屬性、修改 view
屬性、設置背景顏色的好地方。你能夠在 -layoutspec:
方法中設定背景顏色,但這可能會存在時序問題。若是你須要使用原生的 UIView
,能夠在這裏設置它們的 frame
,無論怎樣,你始終可使用 -initWithViewBlock:
建立節點,並在其餘地方的後臺線程中進行調整。
這個方法在主線程上被調用,若是你使用的是 ASLayoutSpec
,那麼你不該該過多地依賴這個方法,由於在主線程上進行佈局是很是可取的,須要這個方法的子類小於 1/10。
使用 -layout
的一個重要用途是你須要子節點的 size 是精確的。舉例來講,當你但願一個 collectionNode
能夠鋪面屏幕,這種狀況不被 ASLayoutSpec
很好的支持,此時最簡單的作法是在這個方法中手動設定 frame
:
subnode.frame = self.bounds複製代碼
若是你但願在 ASViewController
中獲得相同的效果,那麼你能夠在 -viewWillLayoutSubviews
中作一樣的事情,不過若是你的節點經過 initWithNode:
進行實例化,它會自動作到這一點。
ASViewController
是一個常規的 UIViewController
子類,它具備管理節點的特殊功能。由於它是一個 UIViewController
子類,因此全部的方法都在主線程上被調用,而且你應該在主線程上建立至少一個 ASViewController
。
這個方法在 ASViewController
的生命週期開始時被調用一次,與 UIViewController
的初始化同樣,你最好不要在這個方法中訪問 self.view
或 self.node.view
,由於這樣會強制建立 view
。 這些操做能夠在 -viewDidLoad
中進行,-viewDidLoad
能夠執行任何 view
的訪問。
ASViewController
指定的構造器是 initWithNode:
,一個典型的構造器看起來就像下面的代碼。請注意下面的代碼,在調用 super
以前,ASViewController
的節點是如何被建立的,ASViewController
管理節點相似於 UIViewController
管理視圖,可是它的初始化方式有所區別:
class TZYVC: ASViewController<ASDisplayNode> {
init() {
let pagerNode = ASPagerNode()
super.init(node: pagerNode)
pagerNode.setDataSource(self)
pagerNode.setDelegate(self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension TZYVC: ASPagerDataSource {
func numberOfPages(in pagerNode: ASPagerNode) -> Int {
return 10
}
}
extension TZYVC: ASPagerDelegate {
}複製代碼
咱們建議你不要使用這個方法,由於與 -viewDidLoad
相比,它沒有什麼特別的優點,而且有一些缺點。 可是,只要不將 self.view
屬性設置爲不一樣的值,它能夠安全的使用。 它的 super
方法會將其封裝的 UIViewController
的 view
設置爲 ASViewController
的 node.view
。
這個方法在 -loadView
以後被執行,這是 ASViewController
生命週期中,你能夠訪問 node.view
最先的方法,你能夠在這份方法中任意修改 view
和 layer
或添加手勢,這個方法在其所屬的生命週期中,只會執行一次。
因此佈局代碼不該該放在這個方法中,由於當界面重繪時,這裏的代碼不會被再次調用。UIViewController
中這個方法也是一樣的,在這種方法中放置佈局代碼是一種不太好的作法,即便你的佈局不會由於交互發生變化。
這個方法會與節點的 -layout
同時調用,它可能在 ASViewController
的生命週期中被屢次調用,當 ASViewController
的節點的邊界發生改變,如旋轉、分割屏幕、鍵盤彈出等行爲,或者當視圖的層次結構發生變化,如子節點添加、刪除或改變大小時,這個方法將被調用。
由於它不常常被調用,可是調用就表明頁面須要重繪,所以全部的佈局代碼最好都放在這個方法中,即便是不直接依賴於 size 的 UI 代碼也應放在這裏。
viewWillAppear
在 ASViewController
的節點出如今屏幕上以前被調用,這是節點從屏幕出現的最先時間,viewDidDisappear
在控制器從視圖層次結構中移除以後被調用,這是節點從屏幕消失的最先時機,這兩個方法提供了一個很好的時機來啓動或中止與控制器相關的動畫,這也是一個保存和記錄用戶行爲日誌的好地方。
儘管這些方法可能被屢次調用,而且是能夠執行佈局代碼的,可是這兩個方法不會在全部須要重繪的時候被調用,所以除了特定的動畫設置以外,不該該用於執行核心的佈局代碼。