美文网首页
Swift 写一个日志上报组件

Swift 写一个日志上报组件

作者: Alexander | 来源:发表于2024-05-21 20:32 被阅读0次
    思路

    为了方便测试,开发者们一般都会写个日志上报的组件,通过上报的内容,很方便检查接口的情况。首先我们需要定义一个只有测试环境和预发布环境才能显示的日志浮窗,通过点击浮窗显示一个日志上报的列表弹窗。UITableView的组头我们添加一个手势,实现cell的折叠效果,当cell展开时,显示上报的日志内容。cell的大小根据内容来决定,并实现日志请求发至企业微信、复制、清除、关闭日志弹窗等功能。

    • 一、浮窗按钮
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            let pt = touches.first?.location(in: self)
            startLocation = pt
            superview?.bringSubviewToFront(self)
        }
        
        override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
            let pt = touches.first?.location(in: self)
            guard let pt = pt,
                  let startLocation = startLocation,
                  let superview = superview else {
                return
            }
            let dx = pt.x - startLocation.x
            let dy = pt.y - startLocation.y
            var newCenter = CGPoint(x: center.x  + dx, y: center.y + dy)
            
            let halfx = self.bounds.width / 2
            newCenter.x = max(halfx, newCenter.x)
            newCenter.x = min(superview.bounds.size.width - halfx, newCenter.x)
            
            let halfy = self.bounds.height / 2
            newCenter.y = max(halfy, newCenter.y)
            newCenter.y = min(superview.bounds.size.height - halfy, newCenter.y)
            center = newCenter
        }
    
        private func locationChanged() {
            guard let superview = superview else {
                return
            }
            let point = center
            if point.x > superview.frame.size.width / 2 {
                UIView.animate(withDuration: 0.2) { [weak self] in
                    guard let self = self else {return}
                    
                    self.frame = CGRect(x: superview.frame.size.width - self.frame.size.width,
                                        y: self.frame.origin.y,
                                        width: self.frame.size.width,
                                        height: self.frame.size.height)
                }
            } else {
                UIView.animate(withDuration: 0.2) { [weak self] in
                    guard let self = self else {return}
                    self.frame = CGRect(x: 0,
                                        y: self.frame.origin.y,
                                        width: self.frame.size.width,
                                        height: self.frame.size.height)
                }
            }
        }
    

    二、日志浮窗的显示

    class FSLogView {
        
        static let shared = FSLogView()
        
        // 动画时长
        private var duration: TimeInterval = 0.25;
        
        // 弹窗内容
        var contentView: UIView?
        
        // 是否允许点击阴影消失
        var dismiss: Bool = true
        
        lazy var popView: FSLogPopView = {
            let window = getWindow()
            let view = FSLogPopView(frame: CGRect(x: 0, y: 0, width: window!.frame.size.width, height: window!.frame.size.height))
            view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
            view.addTarget(self, action: #selector(popViewAction), for: .touchUpInside)
            return view
        }()
        
        private init() {}
        
        // 显示弹窗
        func show(view: UIView) {
            self.show(view: view, dismiss: false)
        }
        
        func show(view: UIView, dismiss: Bool) {
            view.center = CGPoint(x: popView.frame.size.width/2, y: popView.frame.size.height/2)
            self.showAlertView(view: view, origin: view.frame.origin, duration: 0.25, dismiss: dismiss)
        }
        
        // 隐藏弹窗
        func hidden() {
            dismissAnimate()
        }
        
        // MARK: Events
        @objc func popViewAction(sender: UIControl) {
            if dismiss {
                self.dismissAnimate()
            }
        }
    }
    
    extension FSLogView {
        
        // 显示弹窗
        fileprivate func showAlertView(view: UIView, origin: CGPoint, duration: TimeInterval, dismiss: Bool) {
            removeSubViews()
            let window = UIApplication.shared.delegate?.window
            view.frame = CGRect(origin: origin, size: view.frame.size)
            popView.addSubview(view)
            window??.addSubview(self.popView)
            contentView = view
            self.duration = duration
            self.dismiss = dismiss
            showAnimate()
        }
        
        //显示动画
        fileprivate func showAnimate() {
            self.popView.alpha = 0
            self.popView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5)
            UIView.animate(withDuration: self.duration) {
                self.popView.alpha = 1
            }
        }
        
        // 隐藏动画
        fileprivate func dismissAnimate() {
            UIView.animate(withDuration: 0.25, animations: {
                self.popView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
                self.popView.alpha = 0
            }) { (_) in
                self.removeSubViews()
            }
        }
        
        
        // 移除弹窗
        fileprivate func removeSubViews() {
            for view in self.popView.subviews {
                view.removeFromSuperview()
            }
            self.popView.removeFromSuperview()
            self.contentView = nil
        }
    
    }
    
    extension FSLogView {
    
        // 获取顶层控制器 根据window
        fileprivate func getTopVC() -> (UIViewController?) {
            let window = self.getWindow()
            let vc = window?.rootViewController
            return getTopVC(withCurrentVC: vc)
        }
        ///根据控制器获取 顶层控制器
        fileprivate func getTopVC(withCurrentVC VC :UIViewController?) -> UIViewController? {
            if VC == nil {
                print("【日志上报】找不到顶层控制器")
                return nil
            }
            if let presentVC = VC?.presentedViewController {
                //modal出来的 控制器
                return getTopVC(withCurrentVC: presentVC)
            }else if let tabVC = VC as? UITabBarController {
                // tabBar 的跟控制器
                if let selectVC = tabVC.selectedViewController {
                    return getTopVC(withCurrentVC: selectVC)
                }
                return nil
            } else if let naiVC = VC as? UINavigationController {
                // 控制器是 nav
                return getTopVC(withCurrentVC:naiVC.visibleViewController)
            } else {
                // 返回顶控制器
                return VC
            }
        }
        
        // 获取window
        fileprivate func getWindow() -> UIWindow? {
            var window: UIWindow? = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
            
            // 是否为当前显示的window
            if window?.windowLevel != UIWindow.Level.normal{
                let windows = UIApplication.shared.windows
                for  windowTemp in windows{
                    if windowTemp.windowLevel == UIWindow.Level.normal{
                        window = windowTemp
                        break
                    }
                }
            }
            return window
        }
    }
    
    
    • 三、UITableView的折叠效果
    
    class FSLogViewController: UIViewController {
        
        var sources: [FSLogModel] = FSLogItem.shared.sources
        var tableView: UITableView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            initLogController()
            logView()
        }
    
    }
    
    extension FSLogViewController {
        
        fileprivate func initLogController() {
            view.backgroundColor = .purple
            view.layer.cornerRadius = 10
            view.layer.masksToBounds = true
        }
        
        fileprivate func logView() {
            let screenWidth = UIScreen.main.bounds.width
            let topView = FSLogTopView()
            topView.frame = CGRect(x: 0, y: 0, width: screenWidth - 60, height: 50)
            topView.delegate = self
            view.addSubview(topView)
            
            tableView = UITableView(frame: CGRect(x: 0, y: 50, width: UIScreen.main.bounds.width - 60, height: view.frame.height - 50), style: .grouped)
            tableView.backgroundColor = .white
            tableView.delegate = self
            tableView.dataSource = self
            tableView.tableFooterView = UIView()
            tableView.separatorStyle = .none
            tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 150, right: 0)
            tableView.estimatedRowHeight = 40
            view.addSubview(tableView)
        }
    }
    
    extension FSLogViewController: UITableViewDelegate, UITableViewDataSource {
        
        func numberOfSections(in tableView: UITableView) -> Int {
            return sources.count
        }
        
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return sources[section].isFlod ? sources[section].contents.count : 0
        }
        
        func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
            return 44
        }
    
        func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
            let model: FSLogModel = sources[indexPath.section]
            return model.cellHeight
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = FSLogTableViewCell(style: .default, reuseIdentifier: "FSLogTableCellID")
            let model: FSLogModel = sources[indexPath.section]
            model.cellHeight = model.autoHeight(model.contents[indexPath.row])
            cell.config(model, indexPath)
            return cell
        }
        
        func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
            let header = FSLogHeaderView.headerView(self.tableView)
            header.config(sources[section])
            header.delegate = self
            header.section = section
            return header
        }
        
    }
    
    extension FSLogViewController: FSLogHeaderViewDelegate {
        
        func viewMoreRequestContents(_ header: FSLogHeaderView, section: Int) {
            let flod = sources[section].isFlod
            sources[section].isFlod = !flod!
            
            let index = IndexSet(integer: section)
            self.tableView.reloadSections(index, with: .fade)
        }
    }
    
    extension FSLogViewController: FSLogTopViewDelegate {
        
        func dismiss() {
            FSLogManager.shared.dismiss()
        }
        
        func clear() {
            sources = []
            FSLogItem.shared.sources = []
            tableView.reloadData()
        }
    }
    
    
    • 效果图


      日志上报组件

    相关文章

      网友评论

          本文标题:Swift 写一个日志上报组件

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