iOS開發筆記 - 界面調試神器Reveal

Reveal是iOS開發工具中的神器之一,它能夠在應用程序運行過程中調試應用程序界面。 通過Reveal我們可以連接到應用程序,并允許開發者編輯各種用戶界面參數,?結果會馬上在用戶界面上呈現。就像Web開發人員用?瀏覽器提供的開發人員工具調試頁面一樣,Reveal允許開發者在不修改代碼、不重新構建項目、不重新部署應用程序的情況下就能夠調試iOS應用的用戶界面。

安裝Reveal

Reveal下載地址:http://revealapp.com/download/

集成指南

集成Reveal無需添加任何代碼,無需引入任何頭文件。庫將會在應用啟動時自動加載,并在應用內部啟動必要的Reveal服務。下面介紹三種集成方式。

靜態連接

將Reveal的靜態庫文件連接入應用,是最簡單快捷地啟用Reveal調試界面的方式。

提示: 不要將Reveal庫文件隨著正式應用一起發布到App Store。

下面的步驟將會展示如何通過構建配置,而把Reveal靜態庫文件,僅連接到調試構建的流程中。

  1. 在Xcode中打開您的iOS工程。
  2. 啟動Reveal并選擇Help → Show Reveal Library in Finder,這將會打開Finder窗口,并顯示一個名為iOS-Libraries的文件夾。
  3. Reveal.framework 文件拖入Xcode中的Project Navigator面板。
  4. 在下圖所顯示的Add to targets對話框中,選擇所有您希望與Reveal集成的target。?注意,在如下圖所示的窗口中選中Copy items if needed,它會把 Reveal.framework 拷貝到項目中,如果你這樣做了,那么當更新Reveal至新版本時,也依照上述步驟更新Reveal.framework文件。
  5. 點擊Finish。


  6. 選擇Build Settings標簽,在Other Linker Flags的Debug配置項中加入如下配置。
-ObjC -lz -framework Reveal 
  1. 如果您使用的是Xcode 7, 請確認 Reveal.framework 所在的目錄在您的工程配置項 “Framework Search Paths” 中。你可以在如下圖所示的位置添加下面的內容。
$(inherited)"$(SYSTEM_APPS_DIR)/Reveal.app/Contents/SharedSupport/iOS-Libraries" 
  1. 在Xcode中,構建并運行您的應用。如果應用運行于真實設備之上,請確保此設備與正在運行Reveal的Mac機器,處于同一WiFi網絡中。

如果一切正常運行,請切換到Reveal應用,此時您的應用應會出現在應用選擇器的下拉列表當中。選中您的應用,確認可以看到此時正在模擬器(或設備)中運行的應用界面截圖。

動態加載

動態加載允許iOS應用在運行時,可以按需地加載第三方庫。采用這種方式,庫文件無需連接入應用的可執行文件,而是被加入到了應用Bundle中去,從而在運行時能按需加載。這種方式使得開發者可以在個人應用中,完全自由地控制Reveal庫的加載,以及其服務的啟動與停止。

將Reveal加入您的Xcode工程,使得您團隊中的其他成員無需任何額外的配置,就可以使用Reveal。

提示: 永遠不要將包含Reveal動態庫文件的應用提交給Apple的App Store。Apple不允許將含有動態加載庫文件的iOS應用發布到App Store中。

  1. 在Xcode中打開您的iOS工程。
  2. 啟動Reveal并選擇Help → Show Reveal Library in Finder,這將會打開Finder窗口,并顯示一個名為iOS-Libraries的文件夾。
  3. libReveal.dylib 文件拖入Xcode中的Project Navigator面板。
  4. 在下圖所顯示的Add to targets對話框中,?不選中任何一個target。這確保了Xcode不會在編譯時連接動態庫文件。可選步驟:選中Copy items if needed,將會把 libReveal.dylib 拷貝到工程中。如果你這樣做了,那么當更新Reveal至新版本時,也依照上述步驟更新該文件。
  5. 點擊Finish。


  6. Copy Bundle Resources配置區域中,加入libReveal.dylib。
  7. Link Binary With Libraries配置項中:
  • 如果已有libReveal.dylib,請將其移除,在編譯時不應該鏈接dylib文件。
  • 如果下列系統框架與庫文件還不存在,請將他們加入:
    • libz.tdb
    • CFNetwork.framework
    • QuartzCore.framework
    • CoreGraphics.framework

說明:如果Xcode使用自動鏈接?幫助你自動添加了這些框架,那么就不需要手動添加了。

  1. 為了能在debugger之外,將庫文件動態地載入設備上的應用,您需要在構建過程中加入對libReveal.dylib文件的code sign。

    進入target的Build Phases標簽頁,選擇Editor → Add Build Phase → Add Run Script菜單。在Run Script階段中加入以下內容:
set -e if [ -n "${CODE_SIGN_IDENTITY}" ]; then codesign -fs "${CODE_SIGN_IDENTITY}" "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/libReveal.dylib" fi 
  1. 將下面的代碼加入到項目中合適的類文件中(例如將其放到UIApplicationDelegate中),適當修改使之滿足您的需要:

    Swift:

    // MARK: - Reveal
    func loadReveal() {
        if NSClassFromString("IBARevealLoader") == nil {
            let revealLibName = "libReveal" // or "libReveal-tvOS" for tvOS targets
            let revealLibExtension = "dylib"
            var error: String?

            if let dylibPath = NSBundle.mainBundle().pathForResource(revealLibName, ofType: revealLibExtension) {
                print("Loading dynamic library \(dylibPath)")

                let revealLib = dlopen(dylibPath, RTLD_NOW)
                if revealLib == nil {
                    error = String(UTF8String: dlerror())
                }
            } else {
                error = "File not found."
            }

            if error != nil {
                let alert = UIAlertController(title: "Reveal library could not be loaded",
                                            message: "\(revealLibName).\(revealLibExtension) failed to load with error: \(error!)",
                                     preferredStyle: .Alert)
                alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
                UIApplication.sharedApplication().windows.first?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
            }
        }
    }

Objective-C:

#pragma mark - Reveal

 - (void)loadReveal
 {
     if (NSClassFromString(@"IBARevealLoader") == nil)
     {
         NSString *revealLibName = @"libReveal"; // or @"libReveal-tvOS" for tvOS targets
         NSString *revealLibExtension = @"dylib";
         NSString *error;
         NSString *dyLibPath = [[NSBundle mainBundle] pathForResource:revealLibName ofType:revealLibExtension];

         if (dyLibPath != nil)
         {
             NSLog(@"Loading dynamic library: %@", dyLibPath);
             void *revealLib = dlopen([dyLibPath cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW);

             if (revealLib == NULL)
             {
                 error = [NSString stringWithUTF8String:dlerror()];
             }
         }
         else
         {
             error = @"File not found.";
         }

         if (error != nil)
         {
             NSString *message = [NSString stringWithFormat:@"%@.%@ failed to load with error: %@", revealLibName, revealLibExtension, error];
             UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Reveal library could not be loaded"
                                                                            message:message
                                                                     preferredStyle:UIAlertControllerStyleAlert];
             [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
             [[[[[UIApplication sharedApplication] windows] firstObject] rootViewController] presentViewController:alert animated:YES completion:nil];
         }
     }
 }

警告: 不要在發布構建中調用此方法,確保僅是在應用的調試過程中加載libReveal.dylib。

  1. 可以在UIApplicationDelegateapplicationDidBecomeActive:方法中調用上面的loadReveal方法,以確保Reveal庫盡早地被加載進來。
    Swift:
func applicationDidBecomeActive:(application: UIApplication) {
    self.loadReveal()
}
**Objective-C:**

```Objective-C
(void)applicationDidBecomeActive:(UIApplication *)application {
[self loadReveal];
}
```

提示: 上面這樣做的好處是會讓Reveal服務在應用啟動的同時也自動啟動。

如果您不希望如上述步驟自動啟動Reveal服務,也可以以手動的方式來啟動,例如通過一個Debug按鈕。在應用啟動后,自己調用loadReveal方法,然后再分發一個名為IBARevealRequestStart的NSNotification:
**Swift:**

```
func startReveal() {
    NSNotificationCenter.defaultCenter().postNotificationName("IBARevealRequestStart", object: nil)
}
```

**Objective-C:**
```
- (void)startReveal
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStart" object:nil];
}
```
  1. 在Xcode中構建并運行您的應用。如果一切正常運行,請切換到Reveal應用,此時您的應用應會出現在應用選擇器的下拉列表當中。選中您的應用,確認可以看到此時正在模擬器(或設備)中運行的應用界面截圖。


CocoaPods

CocoaPods是一款針對iOS與OS X項目的依賴管理系統,它大大簡化了以往Xcode工程里對第三方庫的依賴管理與配置工作。

CocoaPods提供了Podspec用于將Reveal集成入您的項目。

提示: 不要將連接了Reveal庫文件的應用用于正式發布。下面的指南描述了一種使用構建配置來使Reveal靜態庫文件僅在調試構建中連接的方式。
說明:此說明要求您在之前已經在項目中配置好了CocoaPods,如果對Cocoapods不了解,可以參考我的《iOS開發筆記 - 使用CocoaPods管理第三方庫》。

  1. 將下面內容加入你的Podfile中:

    pod 'Reveal-iOS-SDK', :configurations => ['Debug']
    
  2. 在項目的根目錄下執行pod install命令(如果之前已經在項目中使用了CocoaPods,請執行pod update命令)。

從您的Xcode項目中移除Reveal

根據您實際所選擇的Reveal集成方式,請根據下述相關步驟來移除Reveal。一旦庫文件成功的移除后,下面的內容將不再會在您的應用啟動時出現在Xcode控制臺。

INFO: Reveal Server started (Protocol Version X).

靜態連接

  1. 打開您的Xcode工程。
  2. Project Navigator刪除 Reveal.framework 的引用。
  3. 在Xcode的 Project Navigator中選中您的工程,對于每一個集成了Reveal得target,請選擇 Build Settings 標簽頁,將下面內容從 Debug 配置中的 Other Linked Flags 設置中移除:
    • -framework Reveal
    • -ObjC and -lz (刪除前請確認此配置內容僅是用于Reveal)。
  4. 運行應用,確認Reveal沒有和應用連接上。

動態連接

  1. 打開您的Xcode工程。
  2. 從 Project Navigator 中刪除 libReveal.dylib 的引用。
  3. 在Xcode的 Project Navigator中選中您的工程,對于每一個集成了Reveal得target,選擇 Build Phases 標簽頁,如果下列庫文件僅供Reveal使用的話,請將它們從 Link Binary With Libraries 配置中移除:
    • libz.dylib
    • CFNetwork.framework
    • QuartzCore.framework
    • CoreGraphics.framework
  4. 將自定義的codesign內容從 Build Phases 下的 Run Script 中刪除。
  5. loadReveal / startReveal 方法從您的代碼中刪除。
  6. 運行應用,確認Reveal沒有和應用連接上。

CocoaPods

  1. 在您的Podfile文件中刪除下面這行內容:
    pod 'Reveal-iOS-SDK', :configurations => ['Debug']
    
  2. 在項目的根目錄下執行pod update命令。
  3. 如果您的Podfile中只有Reveal-iOS-SDK一個pod依賴,請根據此說明,將CocoaPods從項目中完全移除。
  4. 運行應用,確認Reveal沒有和應用連接上。

顯示效果

上圖是Reveal的運行界面,其界面主要分成3部分:

  • 左邊部分是整個界面的層級關系,在這里可以以樹形級層的方式來查看整個界面元素。
  • 中間部分是一個可視化的查看區域,用戶可以在這里切換2D或3D的查看方式,這里看到的也是程序運行的實時界面。
  • 右邊部邊是控件的詳細參數查看區域,當我們選中某一個具體的控件時,右邊就可以顯示出該控件的具體的參數列表。我們除了可以查看這些參數值是否正確外,還可以嘗試修改這些值。所有的修改都可以實時反應到中間的實時預覽區域。

重要提示

  1. 不要將連接了Reveal庫文件的應用正式發布。 Reveal的檢視機制,會將您應用的許多內部信息暴露出來,而這將很可能導致您的應用被Apple審查團隊拒絕發布。Reveal的目的僅用于內部開發與應用調試。
  2. 當iOS應用進入后臺后,Reveal服務將會自動停止。 當應用重新打開時,它又會自動啟動。
  3. Reveal支持基于iOS 7及更新版本而編譯的應用。 構建配置中的iOS Deployment Target也必須是'iOS 7.0'或更新版本。 如果iOS版本太舊,您有可能會在構建應用過程中遇到連接錯誤。
  4. Reveal使用Bonjour協議來連接運行時的iOS應用。 如果您的iOS應用是運行在真實的設備之上,那么此設備也需要處在同一個網絡之內,以便電腦上的Reveal應用能夠與之連接。如果您在連接應用時仍遇到問題,請先檢查防火墻和代理設置,以確保它們沒有阻礙通訊。

用Reveal調試其它應用界面

如果你的設備越獄了,那么還可以用Reveal來"調試"其它應用界面,什么時候會有這種奇怪的需求呢?當我們想學習別人是如何實現界面效果的時候,這一點對一個開發者來說會比較誘人。
設備的/Library/MobileSubstrate/DynamicLibraries 下存放著所有在系統啟動時就需要加載的動態鏈接庫,所以我們只需要將Reveal的動態鏈接庫上傳到該目錄即可。

對于越獄的設備,我們可以在安裝OpenSSH之后,用scp來上傳該文件。具體步驟如下:

  • 將libReveal.dylib 上傳到 /Library/MobileSubstrate/DynamicLibraries
  • 如果libReveal.dylib沒有執行權限,用chmod +x libReveal.dylib命令,給其增加執行權限
  • 執行killall SpringBoard重啟桌面

致謝

以上內容根據千鋒教育成都校區林天祥老師提供的資料整理而成,在此表示感謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容