美文网首页收藏swiftiOS开发常用知识点Swift
Swift 空数据界面显示模块封装实现

Swift 空数据界面显示模块封装实现

作者: 平凡之路561 | 来源:发表于2018-07-09 14:03 被阅读0次

    一个Swift语言封装的EmptyView显示库,可作用于WKWebView、UITableView、UICollectionView

    示例

    • WKWebView


      webViewEmptyGIF.gif
    • UITableView


      tableViewEmptyGIF.gif
    • UICollectionView
    collectionViewEmptyGIF.gif

    引言:

    项目开发过程中当网络断开或者数据获取失败导致的界面显示为空的情况下,我们常会用到图片加文字加刷新按钮、文字加刷新按钮或者纯文本提醒的空界面显示,所以对该功能实现的封装封装就显得很有必要。

    该技术封装模块使用Swift语言,参考OC封装模块的内部实现逻辑,利用runtime的系统方法交换机制,实现在WKWebView网页加载界面、UITableView、UICollectionView列表视图等界面数据获取失败情况下的提醒显示和刷新操作功能。

    一:内部实现原理

    1、通过runtime key值关联HDEmptyView显示对象

    创建UIScrollView的extension对象UIScrollView+Empty类,通过runtime key值关联HDEmptyView显示界面对象ly_emptyView ,该对象可根据调用界面的参数设置来控制空界面显示的内容、布局样式、颜色等。

        struct RuntimeKey {
            static let kEmptyViewKey = UnsafeRawPointer.init(bitPattern: "kEmptyViewKey".hashValue)
        }
        
        public var ly_emptyView: HDEmptyView? {
            set {
                objc_setAssociatedObject(self, RuntimeKey.kEmptyViewKey!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                for view in self.subviews {
                    if view.isKind(of: HDEmptyView.classForCoder()) {
                        view.removeFromSuperview()
                    }
                }
             self.addSubview(ly_emptyView!)
             self.ly_emptyView?.isHidden = true
            }
            get {
                return objc_getAssociatedObject(self, RuntimeKey.kEmptyViewKey!) as? HDEmptyView
            }
        }
        
    

    2、WKWebView 调用显隐方法

    如果是WKWebView的空数据界面显示,根据界面加载成功或失败的情况,调用显示/隐藏空界面方法

        public func ly_showEmptyView() {
            self.ly_emptyView?.superview?.layoutSubviews()
            self.ly_emptyView?.isHidden = false
            //始终保持显示在最上层
            if self.ly_emptyView != nil {
                self.bringSubview(toFront: self.ly_emptyView!)
            }
        }
        
        public func ly_hideEmptyView() {
            self.ly_emptyView?.isHidden = true
        }  
    

    3、列表视图显隐控制

    如果是UITableView、UICollectionView则根据DataSource判断是否自动显示emptyView

    首先获取当前列表视图上cell的个数

       //MARK: - Private Method
        fileprivate func totalDataCount() -> NSInteger {
            var totalCount: NSInteger = 0
            if self.isKind(of: UITableView.classForCoder()) {
                let tableView = self as? UITableView
                if (tableView?.numberOfSections)! >= 1 {
                    for section in 0...(tableView?.numberOfSections)!-1 {
                        totalCount += (tableView?.numberOfRows(inSection: section))!
                    }
                }
            }
            else if self.isKind(of: UICollectionView.classForCoder()) {
                let collectionView = self as? UICollectionView
                if (collectionView?.numberOfSections)! >= 1 {
                    for section in 0...(collectionView?.numberOfSections)!-1 {
                        totalCount += (collectionView?.numberOfItems(inSection: section))!
                    }
                }
            }
            return totalCount
        }
    

    然后根据cell的个数判断是否显示 emptyView

        fileprivate func getDataAndSet() {
            if self.totalDataCount() == 0 {
                show()
            } else {
                hide()
            }
        }
        
        fileprivate func show() {
            if self.ly_emptyView?.autoShowEmptyView == false {
                self.ly_emptyView?.isHidden = true
                return
            }
            ly_showEmptyView()
        }
    
        fileprivate func hide() {
            if self.ly_emptyView?.autoShowEmptyView == false {
                self.ly_emptyView?.isHidden = true
                return
            }
            ly_hideEmptyView()
        }
    

    4、列表视图的方法交换与界面刷新显示

        private static let swizzleMethod: Void = {
            //insertSections
            let originalSelector = #selector(insertSections(_:with:))
            let swizzledSelector = #selector(ly_insertSections(_:with:))
            HDRunTime.exchangeMethod(selector: originalSelector, replace: swizzledSelector, class: UITableView.self)
            
            //deleteSections
            let originalSelector1 = #selector(deleteSections(_:with:))
            let swizzledSelector1 = #selector(ly_deleteSections(_:with:))
            HDRunTime.exchangeMethod(selector: originalSelector1, replace: swizzledSelector1, class: UITableView.self)
            
            //insertRows
            let originalSelector2 = #selector(insertRows(at:with:))
            let swizzledSelector2 = #selector(ly_insertRowsAtIndexPaths(at:with:))
            HDRunTime.exchangeMethod(selector: originalSelector2, replace: swizzledSelector2, class: UITableView.self)
            
            //deleteRows
            let originalSelector3 = #selector(deleteRows(at:with:))
            let swizzledSelector3 = #selector(ly_deleteRowsAtIndexPaths(at:with:))
            HDRunTime.exchangeMethod(selector: originalSelector3, replace: swizzledSelector3, class: UITableView.self)
            
            //reload
            let originalSelector4 = #selector(reloadData)
            let swizzledSelector4 = #selector(ly_reloadData)
            HDRunTime.exchangeMethod(selector: originalSelector4, replace: swizzledSelector4, class: UITableView.self)
            
        }()
        
    
        //section
        @objc  func ly_insertSections(_ sections: NSIndexSet, with animation: UITableViewRowAnimation) {
            ly_insertSections(sections, with: animation)
            getDataAndSet()
        }
        
        @objc  func ly_deleteSections(_ sections: NSIndexSet, with animation: UITableViewRowAnimation) {
            ly_deleteSections(sections, with: animation)
            getDataAndSet()
        }
        
        //row
        @objc  func ly_insertRowsAtIndexPaths(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation){
            ly_insertRowsAtIndexPaths(at: indexPaths, with: animation)
            getDataAndSet()
        }
        
        @objc func ly_deleteRowsAtIndexPaths(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation){
            ly_deleteRowsAtIndexPaths(at: indexPaths, with: animation)
            getDataAndSet()
        }
        
        //reloadData
        @objc func ly_reloadData() {
            self.ly_reloadData()
            self.getDataAndSet()
        }
    

    二:使用方法

    1、创建 HDEmptyView 界面显示对象

          //创建方式一:Block回调
         let emptyV:HDEmptyView = HDEmptyView.emptyActionViewWithImageStr(imageStr: "net_error_tip", titleStr: "暂无数据,点击重新加载", detailStr: "", btnTitleStr: "点击刷新") {
                print("点击刷新")
                weakSelf?.reloadDataWithCount(count: 4)
            }
            
            //创建方式二:target/action
        let emptyV:HDEmptyView = HDEmptyView.emptyActionViewWithImageStr(imageStr: "net_error_tip", titleStr: "暂无数据,点击重新加载", detailStr: "", btnTitleStr: "点击刷新", target: self, action: #selector(reloadBtnAction)) as! HDEmptyView
     
    

    2、设置显示参数属性

            emptyV.titleLabTextColor = UIColor.red
            emptyV.actionBtnFont = UIFont.systemFont(ofSize: 19)
            emptyV.contentViewOffset = -50
            emptyV.actionBtnBackGroundColor = .white
            emptyV.actionBtnBorderWidth = 0.7
            emptyV.actionBtnBorderColor = UIColor.gray
            emptyV.actionBtnCornerRadius = 10
    

    3、赋值给当前显示对象的ly_emptyView

    webView.scrollView.ly_emptyView = emptyV
    
    tableView.ly_emptyView = emptyV
    
    collectionView.ly_emptyView = emptyV
    
    //设置点击空白区域是否有刷新操作
            self.webView.scrollView.ly_emptyView?.tapContentViewBlock = {
                //weakSelf!.loadingURL(urltring: "http://news.baidu.com/")
            }
    
    
    

    4、自定义空数据界面显示

        //自定义空数据界面显示
        func setupMyEmptyView() {
            let emptyView: MyEmptyView = Bundle.main.loadNibNamed("MyEmptyView", owner: self, options: nil)?.last as! MyEmptyView
            emptyView.reloadBtn.addTarget(self, action: #selector(reloadBtnAction(_:)), for: UIControlEvents.touchUpInside)
            emptyView.frame = view.bounds
            //空数据界面显示
            let emptyV:HDEmptyView = HDEmptyView.emptyViewWithCustomView(customView: emptyView) as! HDEmptyView
            tableView.ly_emptyView = emptyV
            tableView.ly_emptyView?.tapContentViewBlock = {
                print("点击界面空白区域")
            }
            tableView.ly_showEmptyView()
        }
        
    

    注意事项:

    是否自动显隐EmptyView的参数 autoShowEmptyView 默认设置是true,列表视图会根据界面cell的count数量自动显隐空界面。
    当设置成false时只能手动调用 ly_showEmptyView() 和 ly_hideEmptyView() 方法进行显隐操作

    下载地址

    相关文章

      网友评论

        本文标题:Swift 空数据界面显示模块封装实现

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