在移动开发当中,APP的界面的编写和调试占了相当的一部分时间,那么在不借助Xcode之外的第三方工具的情况下来调试UI是一件相对来说比较耗费时间的工作,一般情况下,你除了使用Xcode的自带的UI调试器之外,只能使用输出View结构的方式来调试UI的问题。这个在节奏紧凑的开发过程当中是非常不爽的一件事。专业的事情应该交给专业的工具来做,对于调试UI的这件事情来说,应该交给Reveal这个工具。
Reveal是一个程序界面调试工具,可以调试iOS apps和tvOS apps。使用Reveal,我们可以在开发时动态地查看和修改应用程序的界面。避免每次修改UI的时候都要重新运行程序。接下来按照规矩,如下图,能用图说明清楚的就不用文字来表达了,文字的表现力不够直观。
1、如“Reveal程序运行截图”所示,我使用的Reveal官方提供的项目来作为调试Demo,最左边的是Demo的在模拟器的运行效果,右边的App就是Reveal,我第一眼看到这个Reveal有种惊艳的感觉。App的左上方是我此刻调试的模拟器,左下方是我此刻查看UI细节信息的UIView,App的中间部分就是此刻正在分析的设备界面,App的最右边是不是看着和Xcode的Xib编辑器有点类似,没错这个就是可以动态查看和修改UI的地方。这个修改的方式和Xcode的Xib修改Ui细节类似,相信你对这个已经很有感觉了,只要稍加查看就可以上手的。
2、如“Reveal查看UI图”所示,Reveal查看UI的维度也是可以选择的,可以有二维视角,也可以有三维视角。我选择了一个三维视角,如上图所示左边点击展开UIView的层级,点击对应的View查看这个View的对应约束,用这样的方式来查看UI的约束是不是太直观了。
说了这么多,最重要的还没有说出来呢,那就是这个Reveal这么叼,那我要怎么使用它来调试我自己的App界面,或者查看其他知名App的UI布局实现信息。接下来还是先说说怎么用它来调试我们自己的App界面。
使用Reveal来调试模拟器中的我们的App的界面是最简单的。只需要添加对应的断点即可。
1、在Xcode项目中,选择“View → Navigators → Show Breakpoint Navigator”。
2、在左边底部面板,点击"+"号按钮,然后选择“Add Symbolic Breakpoint”。
3、在Symbol字段里面填入“UIApplicationMain”。
4、点击“Add Action”按钮,并确认一下“Action”是设置到“Debugger Command”。
5、在Action下的文本框中贴入如下表达式
expr (Class)NSClassFromString(@"IBARevealLoader") == nil ? (void *)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib", 0x2) : ((void*)0)
6、检查选项“Automatically continue after evaluating actions”是否勾选了。
7、右键点击新建的断点,然后选择“
Move Breakpoint To → User
”保存给所有的项目使用。
8、编译,在模拟器上运行你的程序,然后打开Reveal,查看你的布局。
使用真机调试我们的App界面,如果你的真机是没有越狱的设备,那么使用Reveal来调试UI的步骤是最麻烦的。
1、如“拖库进项目”图所示,把Reveal的库拖到我们项目中来。
2、如“选择添加的方法”所示,不要把库加到我们App的target里面。
3、如“添加到Bundle Resource”图所示,将Reveal的库添加到Bundle Resource。
4、如“选择添加到Bundle Resource”所示,选择对应的Reveal的库到Bundle Resource。
5、如“添加Reveal库成功”图所示,当Bundle Resources出现了Reveal库的时候就添加成功了。
6、如“添加对应的系统库”所示,添加Reveal运行时所需要的系统库。
7、如“添加Reveal运行脚本”所示,接下来是添加Reveal的运行脚本,是不是感觉到麻烦了,如果你有更好调试非越狱真机的方法欢迎告知我,谢谢!
8、如“写入Reveal对应的脚本”所示,写入Reveal运行时候需要的脚本,脚本内容如下:
set -e
if [ -n "${CODE_SIGN_IDENTITY}" ]; then
codesign -fs "${CODE_SIGN_IDENTITY}" "${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}/libReveal.dylib"
fi
9、如“加载方法”图所示,需要在AppDelegate里面写对应的Reveal加载方法,我使用的是Swift版本的,当然也有OC版本的。然后如“在生命周期方法里面调用加载方法”图所示,需要在生命周期方法里面调用Reveal的加载方法。各个版本加载方法现提供如下(需要注意的是不要在发布版本去加载Reveal,因为它仅适合调试):
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:
#import <dlfcn.h>
#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];
}
}
}
10、保证你的电脑和你的真机在同一个网段内,然后运行你的App,在Reveal中选择你的设备,稍等片刻,你的Reveal就会出现对应的UI界面了,如“成功运行结果”界面所示(我的运行设备是iPad)。使用Cmd+R快捷键可以刷新你的Reveal界面。
福利福利福利!!!越狱设备福利!!!
非越狱真机调试是最麻烦的,以前真机还可以使用断点来调试的,现在都不能用了。不再纠结这个了,如果你的设备是越狱设备,那么恭喜你,你要使用Reveal简直是轻松加esay。点开链接一键配置,用Reveal Loader配合Reveal调试App会有惊喜。这个是Richard Heard这位开发者开发的一个Reveal插件,你只需要安装这个插件,保证你的电脑和你的真机在同一个网段内,然后选择你想要调试的任何App(对,没错,不是你自己家的App也可以搞,只有你想不到,没有你看不到的,哈哈哈,巨大福利)。
感谢你看完了本文,因为我感觉自己写的文字加上图片内容很多了,哈哈!这篇文章是来源于我自己的工作实践经验,在接触和使用使用Reveal之前,我一直都是使用打印View结构的方式来调试UI,但是这个方法的输出很不友好,因为没有没有接触到特别好用的工具,所以也不觉得这个打印的方法有多耗费时间。但是现在我不这么想了,根据我的经验判断,但凡不能马上看出问题的UI布局,我都会马上使用Reveal,因为省时省力,而且它也挺有意思的。
看完我的博客,如果你觉得我写的文章对你有一丁点儿帮助的话,那么请你在下面点个赞,让我知道这文章有起了它应该起的作用,谢谢!!!
参考资料:
Reveal的项目:https://github.com/revealapp/Revert
Reveal Loader安装使用 :http://bbs.iosre.com/t/reveal-loader-reveal-app/187
不修改Xcode工程来使用Reveal: http://support.revealapp.com/kb/getting-started/integrating-reveal-load-reveal-without-changing-your-xcode-project
Xcode配置Reveal:http://support.revealapp.com/kb/getting-started/integrating-reveal-add-reveal-to-your-xcode-project