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重启桌面

致谢

以上内容根据千锋教育成都校区林天祥老师提供的资料整理而成,在此表示感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容