站在巨人的肩膀上实现实时浏览log文件方案
iOS本地写log方案,如下,就可以把通过NSLog打印的东东,写入文件。(我们一般使用NSLog记录错误的比较多)!
// 将NSlog打印信息保存到Document目录下的文件中
- (void)redirectNSlogToDocumentFolder
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:@"xxxxx.log"];
NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:fileName];
// 先删除已经存在的文件
NSFileManager *defaultManager = [NSFileManager defaultManager];
[defaultManager removeItemAtPath:logFilePath error:nil];
// 将log输入到文件
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
}
这里是把 stdout 写入文件中,通过定义,我们就可以发现,stdout也是个文件句柄。但是如何查看这个文件呢。除了使用pc工具之外,我们可以手动撸一个小公举
提供一个思路:拉大神的代码改造一番:
代码解释:循环遍历沙盒目录,如果是路径就深入遍历,如果是文件,就提供查看的功能。
@interface PAirSandbox : NSObject
+ (instancetype)sharedInstance;
- (void)enableSwipe;
- (void)showSandboxBrowser;
@end
实现文件如下:
#import "PAirSandbox.h"
#import <UIKit/UIKit.h>
#import "Masonry.h"
#import "AppDelegate.h"
@interface __logVC : UIViewController
@property (nonatomic, strong) UITextView *textView;
@property (nonatomic, strong) UIButton *backButton;
@end
@implementation __logVC
- (UITextView *)textView {
if (!_textView) {
_textView = [UITextView new];
_textView.backgroundColor = [UIColor blackColor];
_textView.textColor = [UIColor whiteColor];
_textView.editable = NO;
_textView.font = [UIFont fontWithName:@"Menlo-Regular" size:15];
}
return _textView;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.textView];
[_textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.offset(0);
make.top.offset(80);
}];
_backButton = [UIButton new];
[_backButton setTitle:@"返回"
forState:(UIControlStateNormal)];
[_backButton addTarget:self
action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_backButton];
[_backButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(15);
make.top.offset(20);
make.size.mas_equalTo(CGSizeMake(60, 60));
}];
[_backButton setBackgroundColor:[UIColor grayColor]];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)back {
[self dismissViewControllerAnimated:YES
completion:nil];
}
@end
#define ASThemeColor [UIColor colorWithWhite:0.2 alpha:1.0]
#define ASWindowPadding 20
#pragma mark- ASFileItem
typedef enum : NSUInteger {
ASFileItemUp,
ASFileItemDirectory,
ASFileItemFile,
} ASFileItemType;
@interface ASFileItem : NSObject
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* path;
@property (nonatomic, assign) ASFileItemType type;
@end
@implementation ASFileItem
@end
#pragma mark- ASTableViewCell
@interface PAirSandboxCell : UITableViewCell
@property (nonatomic, strong) UILabel* lbName;
@end
@implementation PAirSandboxCell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
self.selectionStyle = UITableViewCellSelectionStyleNone;
int cellWidth = [UIScreen mainScreen].bounds.size.width - 2*ASWindowPadding;
_lbName = [UILabel new];
_lbName.backgroundColor = [UIColor clearColor];
_lbName.font = [UIFont systemFontOfSize:13];
_lbName.textAlignment = NSTextAlignmentLeft;
_lbName.frame = CGRectMake(10, 30, cellWidth - 20, 15);
_lbName.textColor = [UIColor blackColor];
[self addSubview:_lbName];
UIView* line = [UIView new];
line.backgroundColor = ASThemeColor;
line.frame = CGRectMake(10, 47, cellWidth - 20, 1);
[self addSubview:line];
}
return self;
}
- (void)renderWithItem:(ASFileItem*)item
{
_lbName.text = item.name;
}
@end
#pragma mark- ASViewController
@interface ASViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UITableView* tableView;
@property (nonatomic, strong) UIButton* btnClose;
@property (nonatomic, strong) NSArray* items;
@property (nonatomic, copy) NSString* rootPath;
@end
@implementation ASViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self prepareCtrl];
[self loadPath:nil];
}
- (void)prepareCtrl
{
self.view.backgroundColor = [UIColor whiteColor];
_btnClose = [UIButton new];
[self.view addSubview:_btnClose];
_btnClose.backgroundColor = ASThemeColor;
[_btnClose setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_btnClose setTitle:@"Close" forState:UIControlStateNormal];
[_btnClose addTarget:self action:@selector(btnCloseClick) forControlEvents:UIControlEventTouchUpInside];
_tableView = [UITableView new];
[self.view addSubview:_tableView];
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.delegate = self;
_tableView.dataSource = self;
_items = @[];
_rootPath = NSHomeDirectory();
}
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
int viewWidth = [UIScreen mainScreen].bounds.size.width - 2*ASWindowPadding;
int closeWidth = 60;
int closeHeight = 28;
_btnClose.frame = CGRectMake(viewWidth-closeWidth-4, 4, closeWidth, closeHeight);
CGRect tableFrame = self.view.frame;
tableFrame.origin.y += (closeHeight+4);
tableFrame.size.height -= (closeHeight+4);
_tableView.frame = tableFrame;
}
- (void)btnCloseClick
{
self.view.window.hidden = true;
}
- (void)loadPath:(NSString*)filePath
{
NSMutableArray* files = @[].mutableCopy;
NSFileManager* fm = [NSFileManager defaultManager];
NSString* targetPath = filePath;
if (targetPath.length == 0 || [targetPath isEqualToString:_rootPath]) {
targetPath = _rootPath;
}
else
{
ASFileItem* file = [ASFileItem new];
file.name = @"..";
file.type = ASFileItemUp;
file.path = filePath;
[files addObject:file];
}
NSError* err = nil;
NSArray* paths = [fm contentsOfDirectoryAtPath:targetPath error:&err];
for (NSString* path in paths) {
if ([[path lastPathComponent] hasPrefix:@"."]) {
continue;
}
BOOL isDir = false;
NSString* fullPath = [targetPath stringByAppendingPathComponent:path];
[fm fileExistsAtPath:fullPath isDirectory:&isDir];
ASFileItem* file = [ASFileItem new];
file.path = fullPath;
if (isDir) {
file.type = ASFileItemDirectory;
file.name = [NSString stringWithFormat:@"%@ %@", @"", path];
}
else
{
file.type = ASFileItemFile;
file.name = [NSString stringWithFormat:@"%@ %@", @"", path];
}
[files addObject:file];
}
_items = files.copy;
[_tableView reloadData];
}
#pragma mark- UITableViewDelegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row > _items.count-1) {
return [UITableViewCell new];
}
ASFileItem* item = [_items objectAtIndex:indexPath.row];
static NSString* cellIdentifier = @"PAirSandboxCell";
PAirSandboxCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[PAirSandboxCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
[cell renderWithItem:item];
return cell;
}
#pragma mark- UITableViewDataSource
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 48;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row > _items.count-1) {
return;
}
[tableView deselectRowAtIndexPath:indexPath animated:false];
ASFileItem* item = [_items objectAtIndex:indexPath.row];
if (item.type == ASFileItemUp) {
[self loadPath:[item.path stringByDeletingLastPathComponent]];
}
else if(item.type == ASFileItemFile) {
[self sharePath:item.path];
}
else if(item.type == ASFileItemDirectory) {
[self loadPath:item.path];
}
}
- (void)sharePath:(NSString*)path
{
NSURL *url = [NSURL fileURLWithPath:path];
//弹出框
UIAlertController *actionController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionPushToRoom = [UIAlertAction actionWithTitle:@"浏览文件" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *str = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
if (!str) {
str = @"暂时没log || 文件不是文本内容";
}
__logVC *vc = [__logVC new];
vc.textView.text = str;
UIViewController *rvc = (UIViewController *)GetAppDelegate.window.rootViewController;
[rvc presentModalViewController:vc animated:YES];
self.view.window.hidden = true;
}];
UIAlertAction *actionCancel = [UIAlertAction actionWithTitle:@"分享文件" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
NSArray *objectsToShare = @[url];
UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];
NSArray *excludedActivities = @[UIActivityTypePostToTwitter, UIActivityTypePostToFacebook,
UIActivityTypePostToWeibo,
UIActivityTypeMessage, UIActivityTypeMail,
UIActivityTypePrint, UIActivityTypeCopyToPasteboard,
UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo];
controller.excludedActivityTypes = excludedActivities;
if ([(NSString *)[UIDevice currentDevice].model hasPrefix:@"iPad"]) {
controller.popoverPresentationController.sourceView = self.view;
controller.popoverPresentationController.sourceRect = CGRectMake([UIScreen mainScreen].bounds.size.width * 0.5, [UIScreen mainScreen].bounds.size.height, 10, 10);
}
[self presentViewController:controller animated:YES completion:nil];
}];
[actionController addAction:actionPushToRoom];
[actionController addAction:actionCancel];
[self presentViewController:actionController animated:YES completion:nil];
}
@end
#pragma mark- PAirSandbox
@interface PAirSandbox ()
@property (nonatomic, strong) UIWindow* window;
@property (nonatomic, strong) ASViewController* ctrl;
@end
@implementation PAirSandbox
+ (instancetype)sharedInstance
{
static PAirSandbox* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [PAirSandbox new];
});
return instance;
}
- (void)enableSwipe
{
UISwipeGestureRecognizer* swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(onSwipeDetected:)];
swipeGesture.numberOfTouchesRequired = 1;
swipeGesture.direction = (UISwipeGestureRecognizerDirectionLeft);
[[UIApplication sharedApplication].keyWindow addGestureRecognizer:swipeGesture];
}
- (void)onSwipeDetected:(UISwipeGestureRecognizer*)gs
{
[self showSandboxBrowser];
}
- (void)showSandboxBrowser {
if (_window == nil) {
_window = [UIWindow new];
CGRect keyFrame = [UIScreen mainScreen].bounds;
keyFrame.origin.y += 64;
keyFrame.size.height -= 64;
_window.frame = CGRectInset(keyFrame, ASWindowPadding, ASWindowPadding);
_window.backgroundColor = [UIColor whiteColor];
_window.layer.borderColor = ASThemeColor.CGColor;
_window.layer.borderWidth = 2.0;
_window.windowLevel = UIWindowLevelStatusBar;
_ctrl = [ASViewController new];
_window.rootViewController = _ctrl;
}
_window.hidden = false;
}
@end
如图:
[图片上传中...(000002.png-c1266f-1525945990445-0)] 000002.png 000003.pngEND
图片浏览的功能还没实现。但是应付平常的查看log功能,绰绰有余了!!
使用
如下代码就可以弹出了:
[[PAirSandbox sharedInstance] showSandboxBrowser];
改进
git 上有个开源库,叫 ICTextView
,支持 文本编辑,支持搜索文字以及高亮显示
ICTextView
支持字符串/正则表达式搜索以及高亮
可定制化
没有使用委托方法
我们可以拿过来直接改造展示的textView
,效果如图:
这样我们的沙盒浏览工具就支持搜索功能了!
要引入的文件就相对多了点:
文件目录
网友评论