美文网首页
iOS数据加载失败展示图placeHolder

iOS数据加载失败展示图placeHolder

作者: 蚂蚁安然 | 来源:发表于2018-08-31 14:28 被阅读0次

    引言

    这个小demo主要针对的UITableVIew, UICollectionVIew和UIWebview等等继承UIScrellVIew的控件,原理就是通过Runtime添加一个属性,然后在通过方法交换监听row的添加,来自动显示placeHolder,我准备了一个基本的样式,当然为了方便大家和一个app里面会有不同的样式的这种情况,我添加自定义的view添加方法.那么问题来了我为什么要写这么一个demo呢,原因是我的一个同为程序员的同学(龙哥说的就是你)说我写的展示空页面的代码太low,这就不能忍了,所以写了一个这种小工具(方便统一管理)

    git

    Untitled.gif

    基本使用

    1.自定义空界面添加

           let emptyView = Bundle.main.loadNibNamed(
            "MyEmptyView", owner: self, options: nil)?.last as! MyEmptyView
        emptyView.reloadBtn.addTarget(
            self,
            action: #selector(reloadBtnAction(_:)),
            for: .touchUpInside)
        emptyView.frame = view.bounds
        //空数据界面显示
        let placeHolder = MagiPlaceHolder.createCustomPlaceHolderView(emptyView)
        tableView.magiRefresh.placeHolder = placeHolder
        tableView.magiRefresh.placeHolder?.tapBlankViewClosure = {
            print("点击界面空白区域")
        }
        tableView.magiRefresh.showPlaceHolder()
    

    注意自己创建的View一定要调用
    MagiPlaceHolder.createCustomPlaceHolderView(emptyView)
    将view包装起来

    2.使用我写好的基本页面

       let placeHolder = MagiPlaceHolder.createPlaceHolderViewWithAction(
            imageName: "net_error_tip",
            title: "暂无数据,点击重新加载",
            detailTitle: "",
            refreshBtnTitle: "点击刷新",
            target: self,
            action: #selector(reloadBtnAction))
    
        placeHolder.titleLabTextColor = UIColor.red
        placeHolder.actionBtnFont = UIFont.systemFont(ofSize: 19)
        placeHolder.contentViewOffset = -50
        placeHolder.actionBtnBackGroundColor = .white
        placeHolder.actionBtnBorderWidth = 0.7
        placeHolder.actionBtnBorderColor = UIColor.gray
        placeHolder.actionBtnCornerRadius = 10
        placeHolder.tapBlankViewClosure = {
            print("点击空白")
        }
        collectionView?.magiRefresh.placeHolder = placeHolder
    

    可以看见placeHolder的点击事件有两种方式:
    一种是addtarget的方式这种方式有可能会引起循环引用需要注意(当然我这里是处理过的);
    另一种是闭包的方式;
    其实都一样主要是适合大家的代码习惯

    后记

    这篇文章也不是为了讲解怎么在Siwft中使用runtime的,我就不在这里多说了,代码很简单,有想了解可以点击这里

    Swift---runtime

    考虑再三还是将代码放进来吧不然这个简书的字数也太少了

    这是我写的runtime方法交换的封装,代码可以直接使用很简单不是吗?

    /// 交换方法
    /// - Parameters:
    ///   - selector: 被交换的方法
    ///   - replace: 用于交换的方法
    ///   - classType: 所属类型
    static func exchangeMethod(selector: Selector,
                               replace: Selector,
                               class classType: AnyClass) {
        let select1 = selector
        let select2 = replace
        let select1Method = class_getInstanceMethod(classType, select1)
        let select2Method = class_getInstanceMethod(classType, select2)
        guard let selectMetho1 = select1Method,
            let selectMethod2 = select2Method else {
                return
        }
        let didAddMethod  = class_addMethod(classType,
                                            select1,
                                            method_getImplementation(selectMethod2),
                                            method_getTypeEncoding(selectMethod2))
        if didAddMethod {
            class_replaceMethod(classType,
                                select2,
                                method_getImplementation(selectMetho1),
                                method_getTypeEncoding(selectMetho1))
        } else {
            method_exchangeImplementations(selectMetho1, selectMethod2)
        }
    }
    

    属性列表的获取

    /// 获取属性列表
    ///
    /// - Parameter classType: 所属类型
    /// - Returns: 属性列表
    static func properties(from classType: AnyClass) -> [objc_property_t] {
        var propNum: UInt32 = 0
        let properties = class_copyPropertyList(classType, &propNum)
        var list = [objc_property_t]()
        for index in 0..<Int(propNum) {
            if let prop = properties?[index]{
                list.append(prop)
            }
        }
        free(properties)
        return list
    }
    

    好吧现在看看是如何添加属性在分类中的

    extension UIScrollView {
    
    struct RuntimeKey {
        static let magiRefresh = UnsafeRawPointer.init(
            bitPattern: "magiRefresh".hashValue)
    }
    
    var magiRefresh: Magi {
        set {
            objc_setAssociatedObject(
                self,
                RuntimeKey.magiRefresh!,
                newValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            if magiRefresh.scrollView == nil {
                magiRefresh.scrollView = self
            }
        }
        get {
            if let objc = objc_getAssociatedObject(
                self,
                RuntimeKey.magiRefresh!)
                as? Magi {
                return objc
            }
            else {
                let objc = Magi()
                objc_setAssociatedObject(
                    self,
                    RuntimeKey.magiRefresh!,
                    objc,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                if objc.scrollView == nil {
                    objc.scrollView = self
                }
                return objc
            }
        }
    }
    
    }
    

    需要注意的是我添加的属性不是可选的所以会有在getter方法中自己生成了一个新的对象直接添加,magiRefresh我打算当做一个管理对象,它的里面有placeHolder等属性和方法---是不是很nice我觉得这种设计很具有Swift风格谁的方法谁管理,也不会弄混系统的方法和自己的方法

    好吧,多说一点

    struct RuntimeKey {
    static let magiRefresh = UnsafeRawPointer.init(
        bitPattern: "magiRefresh".hashValue)
    }
    

    这段代码 UnsafeRawPointer.init( bitPattern: "magiRefresh".hashValue)其代表意义就是指针,在Swift中认为之中是一种不安全的类型,所以你也看到了他的命名不安全.
    当然我也会在之后不断添加magiRefresh这个对象的属性比如添加下拉刷新,将我之前写的框架添加进来,现在他只是一个placeHolder

    相关文章

      网友评论

          本文标题:iOS数据加载失败展示图placeHolder

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