美文网首页
iOS | Build a Log Explore View

iOS | Build a Log Explore View

作者: 清無 | 来源:发表于2023-07-20 09:09 被阅读0次
    @interface DemoLogsView : UIWindow
    
    + (instancetype)shared;
    - (void)setup;
    - (void)appendLog:(NSString *)format, ...;
    - (void)clearLogs;
    
    @end
    
    @interface DemoLogsView()<UITableViewDelegate, UITableViewDataSource>
    
    @property (strong, nonatomic) UITableView *logsTb;
    @property (strong, nonatomic) UIButton *toggleBtn;
    @property (strong, nonatomic) UIButton *exportBtn;
    @property (strong, nonatomic) UIButton *clearBtn;
    
    @end
    
    @implementation DemoLogsView
    {
        NSMutableArray *_logsData;
        CGRect _toggleBtnOffFrame;
    }
    
    #pragma mark - TableView Protocols
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return _logsData.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        static NSString *RID = @"LogCell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RID];
        if(!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RID];
        }
        cell.contentView.backgroundColor = UIColor.blackColor;
        cell.textLabel.textColor = UIColor.whiteColor;
        cell.textLabel.numberOfLines = 0;
        cell.textLabel.font = [UIFont fontWithName:@"Courier New" size:13];
        cell.textLabel.text = _logsData[indexPath.row];
        return cell;
    }
    
    static double buttonSize = 40;
    
    - (void)setup {
        [self toggleExpand:NO];
        [self setHidden:NO];
    }
    
    - (CGRect)toggleButtonFrame {
        CGFloat y = CGRectGetMaxY(UIScreen.mainScreen.bounds) - buttonSize - 20;
        if (@available(iOS 11.0, *)) {
            y -= self.safeAreaInsets.bottom;
            y += 20;
        }
        return CGRectMake(20, y, buttonSize, buttonSize);
    }
    
    - (void)toggleExpand:(BOOL)expand {
        if(CGPointEqualToPoint(_toggleBtnOffFrame.origin, CGPointZero)) {
            _toggleBtnOffFrame = [self toggleButtonFrame];
        }
        else if(expand) {
            _toggleBtnOffFrame = self.frame;
        }
        
        _logsTb.hidden = !expand;
        _exportBtn.hidden = !expand;
        _clearBtn.hidden = !expand;
        [_toggleBtn setTitle:expand ? @"❌" : @"📝" forState:UIControlStateNormal];
        
        self.frame = expand ? UIScreen.mainScreen.bounds : _toggleBtnOffFrame;
        _logsTb.frame = self.bounds;
        _toggleBtn.frame = expand ? [self toggleButtonFrame] : self.bounds;
    }
    
    + (NSString *)logDateString {
        static dispatch_once_t onceToken;
        static NSDateFormatter *df;
        dispatch_once(&onceToken, ^{
            df = [NSDateFormatter new];
            df.dateFormat = @"yyyy-MM-dd HH:mm:ss:SSS";
        });
        return [df stringFromDate:NSDate.date];
    }
    
    - (void)appendLog:(NSString *)format, ...{
        if(![format isKindOfClass:NSString.class]) {
            format = [NSString stringWithFormat:@"%@",format];
        }
        va_list args;
        va_start(args, format);
        NSString *logStr = [[NSString alloc] initWithFormat:format arguments:args];
        logStr = [[self.class logDateString] stringByAppendingFormat:@" %@\n",logStr];
        va_end(args);
        dispatch_async(dispatch_get_main_queue(), ^{
            @synchronized (self) {
                [self->_logsData addObject:logStr];
                NSIndexPath *lastRow = [NSIndexPath indexPathForRow:self->_logsData.count-1 inSection:0];
                [self.logsTb insertRowsAtIndexPaths:@[
                    lastRow
                ] withRowAnimation:UITableViewRowAnimationNone];
                [self.logsTb scrollToRowAtIndexPath:lastRow atScrollPosition:UITableViewScrollPositionBottom animated:NO];
            }
        });
    }
    
    - (void)clearLogs {
        dispatch_async(dispatch_get_main_queue(), ^{
            @synchronized (self) {
                [self->_logsData removeAllObjects];
            }
            [self.logsTb reloadData];
        });
    }
    
    + (instancetype)shared {
        static dispatch_once_t onceToken;
        static DemoLogsView *obj;
        dispatch_once(&onceToken, ^{
            obj = [[self alloc] initWithFrame:UIScreen.mainScreen.bounds];
        });
        return obj;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if(self = [super initWithFrame:frame]) {
            self.windowLevel = UIWindowLevelStatusBar + 100;
            self.backgroundColor = UIColor.clearColor;
            
            [self addSubview:self.logsTb];
            [self addSubview:self.toggleBtn];
            [self addSubview:self.exportBtn];
            [self addSubview:self.clearBtn];
            [self setupLogs];
            
            _logsData = @[].mutableCopy;
        }
        return self;
    }
    
    + (void)load {
        [NSUserDefaults.standardUserDefaults setValue:@"1" forKey:@"CCISMessageLogTestSwitch"];
        [NSUserDefaults.standardUserDefaults synchronize];
    }
    
    - (void)layoutSubviews {
        [super layoutSubviews];
        
        {
            CGFloat y = 20;
            if (@available(iOS 11.0, *)) {
                y += self.safeAreaInsets.bottom;
            }
            _exportBtn.frame = CGRectMake(CGRectGetMaxX(self.bounds)-20-buttonSize, y, buttonSize, buttonSize);
            _clearBtn.frame = CGRectMake(CGRectGetMinX(_exportBtn.frame)-buttonSize-15, CGRectGetMinY(_exportBtn.frame), buttonSize, buttonSize);
        }
    }
    
    - (void)setupLogs {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateLogs:) name:@"CCISLogNotification" object:nil];
        
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
        [self addGestureRecognizer:pan];
    }
    
    - (void)pan:(UIPanGestureRecognizer *)panGestureRecognizer{
        if(!_logsTb.hidden) {
            return;
        }
        CGFloat W = UIScreen.mainScreen.bounds.size.width;
        CGFloat H = UIScreen.mainScreen.bounds.size.height;
        //1、获得拖动位移
        CGPoint offsetPoint = [panGestureRecognizer translationInView:panGestureRecognizer.view];
        //2、清空拖动位移
        [panGestureRecognizer setTranslation:CGPointZero inView:panGestureRecognizer.view];
        //3、重新设置控件位置
        UIView *panView = panGestureRecognizer.view;
        CGFloat newX = panView.center.x+offsetPoint.x;
        CGFloat newY = panView.center.y+offsetPoint.y;
        if (newX < buttonSize/2) {
            newX = buttonSize/2;
        }
        if (newX > W - buttonSize/2) {
            newX = W - buttonSize/2;
        }
        if (newY < buttonSize/2) {
            newY = buttonSize/2;
        }
        if (newY > H - buttonSize/2) {
            newY = H - buttonSize/2;
        }
        panView.center = CGPointMake(newX, newY);
    }
    
    - (void)updateLogs:(NSNotification *)noti{
        [self appendLog:noti.userInfo[@"content"]];
    }
    
    - (void)closeView {
        [self toggleExpand:_logsTb.isHidden];
    }
    
    + (UIButton *)makeRounderButton:(NSString *)title target:(id)target action:(SEL)selector {
        UIButton *v = [UIButton buttonWithType:UIButtonTypeCustom];
        v.frame = CGRectMake(0, 0, buttonSize, buttonSize);
        v.layer.borderColor = UIColor.blackColor.CGColor;
        v.layer.borderWidth = 1;
        v.layer.cornerRadius = buttonSize/2;
    //    v.layer.shadowRadius = 5;
    //    v.layer.shadowOpacity = 0.5;
    //    v.layer.shadowOffset = CGSizeZero;
    //    v.layer.shadowColor = UIColor.blackColor.CGColor;
        [v addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside];
        [v setTitle:title forState:UIControlStateNormal];
        return v;
    }
    
    - (void)exportLogs:(UIButton *)sender {
        // 显示分享
        void(^shareLogsFile)(NSURL *file) = ^(NSURL *file){
            [self toggleExpand:NO];
            
            UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:@[
                file
            ] applicationActivities:nil];
            [avc setCompletionWithItemsHandler:^(UIActivityType __nullable activityType, BOOL completed, NSArray * __nullable returnedItems, NSError * __nullable activityError){
                if(completed) {
                    [NSFileManager.defaultManager removeItemAtURL:file error:nil];
                }
            }];
            UIWindow *window = UIApplication.sharedApplication.delegate.window ?: UIApplication.sharedApplication.keyWindow;
            UIViewController *vc = window.rootViewController;
            if([vc isKindOfClass:UINavigationController.class]) {
                vc = ((UINavigationController *)vc).visibleViewController;
            }
            [vc presentViewController:avc animated:YES completion:nil];
        };
        
        // 将日志写到本地沙盒
        void(^writeLogsToLocal)(void) = ^{
            if(!self || !self->_logsData.count) { return; }
            NSMutableString *logs = @"".mutableCopy;
            for (id log in self->_logsData) {
                [logs appendString:log];
            }
            NSString *url = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
            id logFile = [NSString stringWithFormat:@"%@.log",[self.class logDateString]];
            url = [url stringByAppendingPathComponent:logFile];
            BOOL written =  [[logs dataUsingEncoding:NSUTF8StringEncoding] writeToFile:url atomically:YES];
            if(written) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    shareLogsFile([NSURL fileURLWithPath:url]);
                });
            }
            else {
                [self appendLog:@"写日志文件到本地失败!"];
            }
        };
        
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
            writeLogsToLocal();
        });
    }
    
    - (UIButton *)clearBtn {
        if(!_clearBtn) {
            UIButton *v = [self.class makeRounderButton:@"🗑️" target:self action:@selector(clearLogs)];
            _clearBtn = v;
        }
        return _clearBtn;
    }
    
    - (UIButton *)exportBtn {
        if(!_exportBtn) {
            UIButton *v = [self.class makeRounderButton:@"📤" target:self action:@selector(exportLogs:)];
            _exportBtn = v;
        }
        return _exportBtn;
    }
    
    - (UIButton *)toggleBtn {
        if(!_toggleBtn) {
            UIButton *v = [self.class makeRounderButton:@"" target:self action:@selector(closeView)];
            _toggleBtn = v;
        }
        return _toggleBtn;
    }
    
    - (UITableView *)logsTb {
        if(!_logsTb) {
            UITableView *tv = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
            tv.rowHeight = UITableViewAutomaticDimension;
            tv.estimatedRowHeight = 44;
            tv.delegate = self;
            tv.dataSource = self;
            tv.allowsSelection = NO;
            tv.backgroundColor = UIColor.blackColor;
            tv.contentInset = UIEdgeInsetsMake(buttonSize, 0, buttonSize, 0);
            _logsTb = tv;
        }
        return _logsTb;
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:iOS | Build a Log Explore View

          本文链接:https://www.haomeiwen.com/subject/iexaudtx.html