詳解Xcode 6的檢視除錯

發表於2015-05-04

開發者會經常遇到檢視或者Auto Layout約束中存在bug的情況,並且這種bug很難通過程式碼發現,所以開發者很有必要熟知如何進行簡單高效的檢視除錯,而Xcode 6的釋出使得檢視除錯變得前所未有的簡單。

開發者不用將frames列印到控制檯,然後在腦海中視覺化檢視的佈局,現在你可以在Xcode中檢視整個檢視層次。

本教程會帶你熟悉所有可以操作的不同選項。你做好寫程式碼的準備了嗎?這個問題有點令人煩心,因為你根本就不想寫程式碼。你可以在Xcode 6中檢查開源庫的檢視層次,瞭解它的編寫方法—最重要的是不用看任何程式碼。

開始

本教程使用Jesse Squires 編寫的JSQMessagesViewController庫,庫的UI看起來非常熟悉,類似Messages app。

先在 GitHub project page下載原始碼並解壓。

注意: 該庫使用了CocoaPods來管理和其他庫之間的依賴關係,不熟悉的話可以先看看CocoaPods tutorial

接著在終端找到解壓專案,執行 pod install 安裝所需依賴關係。開啟JSQMessages.xcworkspace並編譯,然後在iPhone5s模擬器中執行應用程式。  (可使用任意尺寸的模擬器,本教程中使用的是4英寸的模擬器,你可以選擇4英寸模擬器以便於快速瞭解教程內容)

注意: Live View Debugging僅支援在iOS 8上執行應用程式,不支援iOS 7,即便你用的是Xcode 6。

點選專案中的 Push via storyboard選項 進入與Steve Jobs 和Tim Cook訊息傳送介面執行緒,這就是你將要檢視的檢視。

回到Xcode並點選除錯欄中的Debug View Hierarchy按鈕 ,或者通過Debug\View Debugging\Capture View Hierarchy操作,效果是一樣的。

Xcode會打斷app的執行並進行除錯,該操作和你使用除錯欄上的的”pause”按鈕暫停app執行一樣。此外,Xcode會展示canvas(譯者注:以下簡稱”畫布”)而不是程式碼編輯器。Xcode在canvas上繪製了app主視窗的整個檢視層次,包括指示每個檢視邊界的細線(稱之為線框圖)。

如果往檢視層次上新增一個子檢視,也就是在當前的檢視堆疊上新增layer。由於大部分views不會疊加,所以執行app時,所有的views看起來像是大layer的一部分,下圖非常接近這種描述,不過帶著一些額外的線。

現在你所看到的是一個視覺化的檢視堆疊。在canvas中點選並拖動,會看到檢視層次的3D模型。

你可以從上、下、左、右多個角度檢視檢視層次。

注意:在嘗試的過程中,canvas可能不會像教程中展示的這樣。為確保你是在同一個頁面,請按下cmd + 6 調出Debug navigator。

在皮膚底部左側有兩個按鈕。如下圖所示,取消對這兩個按鈕的選定,否則會隱藏一些檢視。

探索檢視層次(Exploring the View Hierarchy)

最自然最常用的方式是從左邊開始探究3D模型,稍後教程為解釋為什麼要這麼做。向左拉動檢視層次,如下圖所示:

如果你想視覺化app的構建,那麼從多個角度檢視檢視層次就非常有用了。不過,在堆疊的底部(左邊)有很多空檢視,它們是什麼呢?

點選最左邊的檢視(也就是最後邊的檢視),Xcode會對其進行高亮。畫布上方的Jump Bar(跳轉欄)更新展示一個UIWindow作為最新專案–最新專案通常指出了當前選中的專案以及其class型別。

由於該app只使用一個視窗,所以可假定位於跳轉欄前邊的UIWindow 是app的主要視窗,也就是AppDelegate的 window 屬性。

不過,似乎檢查這個圖層並沒有多大意義。那下一個檢視呢?在畫布中,點選視窗最右邊的檢視(也就是最上邊的檢視),再看看跳轉欄(Jump Bar)看看有什麼不一樣的。UILayoutContainerView,甚至不是一個公開類。

這時候,檢視層次看起來是這樣的:

1.UINavigationTransitionView:導航控制器在這裡發生轉場行為的容器檢視。

2.UIViewControllerWrapperView: 包含view controller 的view屬性的封裝檢視。

3.UIView: view controller的最上層檢視 (與view controller的view 屬性一致)

4.JSQMessagesCollectionView: 工程使用collection view來展示所有訊息。

Focusing on Views of Interest(關注與除錯相關的檢視)

在除錯該特定檢視層次時,頭四個檢視(從視窗開始)實際上是視覺”噪音”,它們並無多大意義,會讓你分心,如果能過濾掉這些”噪音”檢視就最好不過了。

你當然可以這麼做!在畫布的右下角有一個雙滑塊兒滑桿,左右滑動滑塊可幫你暫時隱藏一些檢視,預設情況下,滑塊在滑桿的左右兩端。

將滑塊左端滑塊向右滑動一點,畫布中app的線框圖會一層層消失。將滑塊兒拖得更遠一點,UINavigationTransitionView也消失不見了。

根據需要將左端滑塊兒儘可能拖得遠一些,從而隱藏JSQMessagesCollectionView的父檢視。你的畫布看起來應該和下邊類似:

在右側,你會發現導航欄似乎有點讓人分心,但是它的的確確位於collection view的最上層,不方便檢視下層佈局。幸運的是你可以隱藏它。

由於你主要關於螢幕上較小的一個區域,並且導航欄上還有很多比導航欄更小的元素。這時候放大導航欄可以讓你更清楚地看到介面是如何佈局的。

使用縮放控制元件按鈕,它是一組三個的按鈕,居中展示在畫布中。

這一組按鈕有放大”+”、縮小”-“以及將檢視重置到正常的水平的”=”三種選擇,

注意:如果你使用的是觸控板,兩指捏合和縮放也能縮小和放大檢視。如果你一次性縮放過多,那麼螢幕上的內容就不能完全展示,這時候使用觸控板還是比較有用的。當然你也可以使用滑鼠滑輪進行縮放。

雖然通過縮放toolbar獲得額外的細節非常不錯,但這些檢視仍緊緊地疊加在一起,要分清誰是誰並不容易。

想要解決這個問題,可以使用畫布左下角的spacing slider,向右拖動圓形滑塊兒越遠,不同同檢視之間的間距。檢視間的間距會隨著滑塊兒拖動的距離增加而變大。

在該案例中,儘可能地向右移動滑桿兒,以避免工具欄中檢視疊加。你可以試試在畫布上拖放檢視以達到理想的效果。

畫布右下角隱藏檢視的滑桿,將右端滑塊兒移至左端,直到剩下UINavigationBar。選擇最上邊的圖層,你可以使用Jump Bar來辨認每個檢視的類。首先你會看到導航專案消失了,接著是包含它們的按鈕,然後是一些私有檢視,最後是導航欄。

啊?沒有導航欄了!

注意:旋轉畫布檢視3D檢視層次,如果最頂層檢視位於左側,那麼滑桿兒的左側滑塊兒依舊從堆疊底部移除檢視,現在是在右側。同樣,不管頂層檢視在左側還是右側,右側滑塊兒都從堆疊的頂部開始移除。

將滑塊兒從左側移至右側,檢視從右向左逐漸消失(反之亦然),這是有違直覺的,這也是讓頂層檢視位居右側,這種檢視3D模型的方式才是最自然的方式,就是我們在教程最初提到的那一點。

不幸的是,隱藏導航欄(包含_UIBackdropView根檢視)檢視也會讓螢幕底部toolbar裡的內容消失。調整縮放度或向下移動畫布才能看到發生的變化。

由於toolbar專案是螢幕上重要的一部分,你需要檢視這些內容,所以僅僅隱藏檢視直到(但不包括)剩下_UIBackdropView,導航欄堆疊看起來像是下邊這種:

More View Options(更多檢視選項)

現在已經隱藏了不相關的檢視,我們接著要從正面再看這個螢幕。你可以將3D模型拖回原位,不過有時候很難剛剛好,還好我們有其他辦法。

在3D模型的下方—縮放按鈕的左側有一組四個按鈕,從左向右數第三個按鈕是ResetViewing Area按鈕,它可以取消旋轉並從正面展示檢視層次,像在模擬器或者裝置上一樣。

畫布看起來應該和下邊的一樣:

你可能注意到你在偵錯程式中所見到的,並不全是app實際執行時的樣子。

首先,單個檢視周圍易燃有線框圖包圍,它們可以讓你檢視透檢視或者沒有任何內容的檢視,不過如果你不需要細節資訊,線框圖就會讓事情變得雜亂。

你可以使用View Mode按鈕進行選擇-在Reset Viewing Area按鈕的右側。點選檢視模式按鈕,你可以選擇只檢視線框圖、只檢視內容或者同時檢視兩者。

如果你主要是對位置感興趣,並且不大關心檢視看起來什麼樣子,那麼線框圖是非常有用的。當你想要除錯檢視的外觀時,僅展示檢視內容就非常有用了。

想要減少線框圖引起的雜亂(尤其是靠近導航欄和toolbar的地方),可將檢視模式更改為Contents ,以移除所有線框圖,僅留下app的核心部分。

接下來,是從當前檢視中忽略的幾點。

當你執行app時,你將會看到文字氣泡上的標籤,指示資訊傳送者名稱或者資訊的時間戳,以及最後一個氣泡中Golden Gate Bridge的圖片。但是偵錯程式並不會展示這些標籤和圖片。想要解決這個問題,可檢視畫布上中間一組按鈕的第一個,可展示或者隱藏省略掉的檢視。這些檢視的clipsToBounds屬性設定為了YES。

這是標籤相關的,大概由於較長的名字和日期不應當延伸到標籤的界限之外。這一點同樣應用於圖片,圖片使用圓角半徑並剪下以生成圓角圖片。點選該按鈕,你會注意到這些檢視將不再出現在Xcode中。

注意:你可能注意到可視專案周圍仍有線框圖,如果是這種情況,使用你先前使用的View Mode按鈕

開啟和關閉線框圖,問題即可解決。

你已經都瞭解了:在Xcode中近乎完美地複製了檢視層次。

So easy!

Inspecting Views(檢視檢視)

已經瞭解了最重要的部分,現在看看這些不同檢視的佈局。

你已經知道了collection view如何讓這些檢視聚集在一起,但是如果能看到這些不同元素的整體結構就更好了。當然可以啦!

按下cmd + 6 調出Debug navigator,和其他除錯會話一樣,Debug navigator提供了當前會話的文字資訊。對於檢視除錯來說,這意味著Xcode提供了所有視窗中檢視的檢視樹。展開Debug navigator的檢視樹後是這個樣子的:

注意:在Debug navigator的底部,你會看到一個選項可以控制在檢視樹中展示哪種型別的專案。蘋果的文件表示左邊的按鈕將系統檢視實現的私有元素過濾出來,不過這個按鈕在Xcode 6.2中似乎不起作用。

右邊按鈕可隱藏那些將其 hidden 屬性設定為YES的檢視,並且搜尋欄僅展示匹配搜尋條件的檢視和約束。

出於本教程的目的,取消對這兩個按鈕的選擇,並且不使用任何搜尋過濾。

這是個不錯的而開始。展開最後一個JSQMessagesCollectionViewCellOutgoing,它只有一個子檢視UIView。如果你以前使用過collection view,那你應該知道這個是講得通的,因為任何UICollectionViewCell都有一個包含cell內容的contentView 屬性。

點選但不要展開-將滑鼠放在Debug navigator的UIView 上,你會看到Xcode已經在畫布上對其高亮,這樣你就準確知道它在螢幕的什麼地方。

想要真正瞭解iOS如何放置該cell,可使用cmd + option + 4開啟Size Inspector,該導航器的頂部形象化了檢視的邊界、位置以及錨點。

不過,真正有趣的部分是應用於該檢視的Auto Layout約束列表。你可以立刻將cell的內容檢視的寬和高分別設定為312 point和170 point,並將其居中。封閉的cell同樣是312*170 point,所以內容檢視佔據了整個cell。下邊用灰色顯示的約束表示它們是指出檢視和其子檢視之間關係的約束。

想了解一個特定約束的更多細節,首先要展開檢視樹中的檢視,然後展開Constraints專案。你將會看到和Size navigator中一樣的約束列表。

點選第一個約束(self.midX的約束),並通過cmd + option + 3切換至Object inspector。你會看到一個約束概覽。編輯約束時,這一點非常類似於Interface Builder。

除了尺寸和約束資訊,你還會看到Object Inspector中特定檢視的其他資訊。回到Debug navigator,展開檢視樹中的UIView ,你會到它有3個JSQMessageLabel和兩個UIView。選中第一個JSQMessageLabel(帶有時間戳的那個),並開啟Object Inspector。

第一個部分展示了物件的類名稱和記憶體地址,第二部分展示了物件的多個公有屬性的值。

從圖中看出,標籤文字的顏色是無alpha 的0.67灰,字型大小是12pt。

針對它們如何被視覺化,其他類也有一些有用的資訊。回到Debug navigator,展開cell的根UIView 中的第二個UIView,你會看到一個UIImageView。

從檢視樹中選擇image view,並檢視Object inspector。

你正檢視的是展示使用者頭像的檢視-在該例子中是作者的首字母JSQ,你會看到常規圖片、便利的貼有標籤的圖片、較暗的圖片以及被標記的高亮,這些將在使用者點選cell時展示。

在cell的根檢視中,JSQMessageLabel的其他兩個例項當前還沒有文字,但它們被用於即將進來的訊息傳送者的名字和訊息傳送失敗時的錯誤資訊。

怎麼樣,在Xcode中除錯檢視非常簡單吧,繼續執行該app,點選Debug bar上的”Continue”按鈕,或者執行Debug\Continue,就像你在常規除錯中那樣。

Live Modifications(實時調整)

現在你已經瞭解了使用Xcode 6進行檢視除錯的基礎支援,接下來將你所學應用到一個小小的實驗中:只使用偵錯程式,確保你在本教程中使用的collection view的垂直滾動指示器為red。

你可以從以下兩點開始:

1.由於檢視除錯和其他除錯部分非常像,你可以在終端使用expr 和其他命令,但是需要重新執行專案才能看出所做的變化。更多關於這些命令的資訊,請檢視debugging apps in Xcode教程

2.由於Objective-C中的指標僅僅是記憶體地址,所以當你傳送物件時,你僅僅傳送了一個記憶體地址。這同樣適合於偵錯程式,所以類似po 0x123456abcdef這樣的命令會列印出記憶體地址中的物件描述。

多做幾次嘗試,如果出現問題,可嘗試以下解決方案:

首先要確保將檢視模式設定為”Contents”

在Debug navigator中,展開collection view的檢視樹,以便清楚知道檢視的子檢視。

collection view的最後檢視是兩個UIImageView例項,是水平方向和垂直方向上的滾動指示器。點選第二個,你會看到水平方向上的指示器在畫布中被高亮。

從Object inspector中複製圖片檢視的記憶體地址。

在這個例子中,滾動指示器的記憶體地址是0x7fde6c484640。需要做的是給該地址中的物件傳送一個setBackgroundColor:資訊可將滾動指示器著色。可使用以下程式碼:

1
expr (void)[0x7fde6c484640 setBackgroundColor:[UIColor redColor]]

繼續執行該應用程式,在滾動collection view時,滾動指示器變成了紅色。

祝賀你,你已經瞭解了使用Xcode 6進行檢視除錯的基本內容。

Old School Debugging(保守除錯)

實時除錯讓使用Xcode 6進行檢視除錯變得非常簡單,但並不意味你之前常用的除錯方法已經沒有用武之地了。事實上,除了檢視除錯外,iOS 8引入了一個非常受歡迎的技巧: _printHierarchy.

注意:你已經瞭解了Xcode 6檢視除錯的基本內容,所以如果你不喜歡可以直接跳過這個章節。不過如果你非常痴迷一些便捷的舊的技術,那不要錯過這個。

列印View Controller Hierarchy

_printHierarchy是 UIViewController 的一個私有方法,你可以用它將view controller 層次列印到控制檯。編譯並執行,選中Push via storyboard,然後點選Debug bar上的”pause”按鈕。

在終端打出以下內容並重新執行:

得到類似下面的內容:
035.png

這告訴你UINavigationController的第一個檢視控制器是一個TableViewController,你可以選擇如何推出控制器。第二個view controller是DemoMessagesViewController,或者你已經除錯過的view controller。

這個例子似乎不怎麼令人興奮,但如果你的導航控制器中有幾個view controller,或者彈出檢視中有tab bar控制器,那麼想要弄清楚這些view controller如何工作, 這個功能就非常有用了。

Printing the View Hierarchy(列印檢視層次)

如果你更喜歡文字化的檢視層次,那你可以使用UIView舊的私有recursiveDescription。這個方法列印出來的檢視層次非常類似於上邊描述的view controller層次。

開啟Views\JSQMessagesCollectionViewCellOutgoing.m 並在awakeFromNib中新增一個斷點。

編譯並執行,然後選擇Push via Storyboard。偵錯程式有問題了,因為載入了JSQMessagesCollectionViewCellOutgoing。現在在控制檯輸入以下程式碼:

這將列印出 JSQMessagesCollectionViewCellOutgoing的contentView層次, 看起來像這樣:

這是基本的,但可以幫你除錯iOS 8之前的檢視層次。

Using debugQuickLookObject(使用debugQuickLookObject)

最後,Xcode5.1引入了Debug Quick Look功能。如果你已經做好了除錯的準備,但不大想知道某段程式碼如何實現物件的特定外觀,那麼這時候這個功能就非常有用了。

你的自定義類可以實現debugQuickLookObject方法,並返回任何由Xcode展示的內容。此外,如果你已經正進行除錯,並且已經有了想要檢視的物件,你可以使用快速檢視功能,並且Xcode會以視覺化形式表示物件。

比如,NSURL對 debugQuickLookObject的實現返回了一個帶有URL 的UIWebView,你可以真實看到URL背後的東西。

關於使用Quick Look除錯的更多資訊,請檢視相關文件

下一步

以上是關於實時除錯的內容,它是一個可以幫助節省大量時間的工具,非常好用。

如果你想尋找一些更高階、功能更全面的工具,可以試試Reveal(付費),它要比Xcode的檢視除錯強大很多。你可以檢視我們相關主題的Tech Talk

相關文章