博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift 中的运行时小技巧
阅读量:5996 次
发布时间:2019-06-20

本文共 8258 字,大约阅读时间需要 27 分钟。

Swift 运行时动态绑定属性

在iOS开发中属性封装了成员变量的setter,getter方法,在开发中会遇到很多需要类目的地方,类目可以为已有的类添加方法,但是不能添加成员变量(在创建类的时候类所占用的内存空间已经根据类的属性确定),如果需要添加属性的话,需要手动生成setter,getter方法 在没有setter,getter方法的时候生成属性就需要靠强大的运行时

在OC中我们通常这样写:

#import 
static const void *HUDKey = &HUDKey;@implementation UIView (HUDExtension)#pragma mark - 动态绑定HUD属性- (MBProgressHUD *)HUD{ return objc_getAssociatedObject(self, HUDKey);}- (void)setHUD:(MBProgressHUD * _Nullable)HUD{ objc_setAssociatedObject(self, HUDKey, HUD,OBJC_ASSOCIATION_RETAIN_NONATOMIC);}复制代码

在swift中我们通常这样写:

extension UIView {        // MARK:- RuntimeKey   动态绑属性    struct RuntimeKey {                static let kProgressHud = UnsafeRawPointer.init(bitPattern: "kProgressHud".hashValue)            }        var HUD: MBProgressHUD? {        set {            objc_setAssociatedObject(self, UIView.RuntimeKey.kProgressHud!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)        }                get {            return  objc_getAssociatedObject(self, UIView.RuntimeKey.kProgressHud!) as? MBProgressHUD        }    }}复制代码

这样便有了swift和oc一样的动态绑定属性的方法了

在我们在swift还会用到运行时的交换方法:

/// 交换方法 并且只执行一次 程序已进入就执行交换    class func exchangeMethod()  {                let sel = Selector.init(("executeReloadDataBlock"))                let firstMethod = class_getInstanceMethod(self.classForCoder(),sel)                let secondMethod = class_getInstanceMethod(self.classForCoder(),#selector(mk_reloadData))                method_exchangeImplementations(firstMethod!, secondMethod!)            }复制代码

在之前是可以写load方法进行再已进入程序就进行交换操作的,swift3.0后苹果废除了load方法,但是可以调用

open override static func initialize() {        // Method Swizzling        if self == UIScrollView.classForCoder() {            UIScrollView.exchangeMethod()        }    }    复制代码

这样来进行交换方法,但是在swift4.0以后initialize方法也被废除了,我们可以在AppDelegate中调用这个方法

通过这些简单的运行时方法可以方便我们的代码 我的项目每个列表页在没有加载到数据的时候都会有一张占位图,这当然可以通过继承来操作,继承UITableview自定义一个列表页,重写reloadData方法,在这个方法里面判断cell的个数,来操作占位图的显示

但是,通过运行时,我们会有更爽快的操作,我的项目中使用了MJRefresh,本来想使用交换reloadData的方法来进行操作的,但是我发现reloadData方法已经在MJRefresh中被交换过了(UIScrollView+MJRefresh.m 135行以后),但是这并不意味这不能操作了,我们发现在每次调用reloadData后便会调用executeReloadDataBlock这个方法,所以我们决定对这个方法进行交换,而MJRefresh也帮我们写好了计算cell的个数的方法mj_totalDataCount

/// 交换方法 并且只执行一次 程序已进入就执行交换    class func exchangeMethod()  {                let sel = Selector.init(("executeReloadDataBlock"))                let firstMethod = class_getInstanceMethod(self.classForCoder(),sel)                let secondMethod = class_getInstanceMethod(self.classForCoder(),#selector(mk_reloadData))                method_exchangeImplementations(firstMethod!, secondMethod!)            }        /// 用于替换获取cell的个数 并附加载cell个数为0时显示背景图    @objc func mk_reloadData()   {                mk_reloadData()                let count = mj_totalDataCount()                        if self.LoadingStateView != nil {//保证没有刷新的控件不受影响 例如轮播图            if count == 0 {                                                self.showLoadingView(show: true)                                var ofset : CGFloat = 0                                if self.mj_header != nil {                    if  self.mj_header.ignoredScrollViewContentInsetTop != 0 {                        ofset +=  self.mj_header.ignoredScrollViewContentInsetTop                    }                }                                               //                if self.isKind(of: UITableView.classForCoder()) {                                        let tableview : UITableView = self as! UITableView                                        if tableview.tableHeaderView != nil {                        ofset += (tableview.tableHeaderView?.height)!                    }                                    }                                LoadingStateView?.snp.updateConstraints { (maker) in                                        maker.centerX.equalTo(self.snp.centerX)                                        maker.centerY.equalTo(self.snp.centerY).offset(ofset/2)                                    }                                            }else{                                showLoadingView(show: false)                            }                    }                        // MARK:- 当scroll得contentSize小于frame是footer隐藏        if self.mj_footer != nil  {                        let footer : MJRefreshAutoNormalFooter = self.mj_footer as! MJRefreshAutoNormalFooter                        self.layoutSubviews()                                    if self.contentSize.height < self.height{                                footer.stateLabel.isHidden = true                                footer.isRefreshingTitleHidden = true                            }else{                                footer.stateLabel.isHidden = false                                footer.isRefreshingTitleHidden = false                            }                    }                            }复制代码

当然self.LoadingStateView 也是通过runtime进行动态生成的

extension UIView{            //    static var kBgonce = 0        // MARK:- RuntimeKey   动态绑属性    struct BackgroundRuntimeKey {                ///   动态绑定没有数据的背景图        static let kBackgroundLoadingStateView = UnsafeRawPointer.init(bitPattern: "kBackgroundLoadingStateView".hashValue)                static let kBackgroundLoadingHint = UnsafeRawPointer.init(bitPattern: "kBackgroundLoadingHint".hashValue)                    }        var loadingHint : String?{                set {                        showLoadingView(show: true)                        self.LoadingStateView?.hint = newValue                        objc_setAssociatedObject(self, UIScrollView.BackgroundRuntimeKey.kBackgroundLoadingHint!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)        }                get {                        return  objc_getAssociatedObject(self, UIScrollView.BackgroundRuntimeKey.kBackgroundLoadingHint!) as? String        }    }            var LoadingStateView: LoadingView? {        set {                        objc_setAssociatedObject(self, UIScrollView.BackgroundRuntimeKey.kBackgroundLoadingStateView!, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)        }                get {                        return  objc_getAssociatedObject(self, UIScrollView.BackgroundRuntimeKey.kBackgroundLoadingStateView!) as? LoadingView        }    }            /// 创建背景视图    func createLoadingStateView() {                if self.LoadingStateView != nil {            return        }                let loadView : LoadingView = LoadingView.viewFromXib() as! LoadingView                loadView.isHidden = true                let loadH : CGFloat = self.height/3                loadView.frame = CGRect.init(x: 0, y: 0, width: loadH*0.75, height: loadH)                        //        loadView.transform = CGAffineTransform(scaleX: 0.75, y: 0.75);                self.addSubview(loadView)                loadView.snp.makeConstraints { (maker) in            maker.centerX.equalTo(self.snp.centerX)                        maker.centerY.equalTo(self.snp.centerY)        }                self.LoadingStateView = loadView            }        func showLoadingView(show:Bool)  {                //如果cell的个数不为0 就不显示        if self.isKind(of: UITableView.classForCoder()) {                        if show == true {                                let tableview : UITableView = self as! UITableView                                if tableview.mj_totalDataCount() != 0 {                    return                }                            }                    }else if self.isKind(of: UICollectionView.classForCoder()){            if show == true {                let tableview : UICollectionView = self as! UICollectionView                                if tableview.mj_totalDataCount() != 0 {                                        return                }                            }        }                        if LoadingStateView == nil {            createLoadingStateView()        }                self.bringSubview(toFront: LoadingStateView!)                LoadingStateView?.isHidden = !show                            }}复制代码

这样写了只要tableview用到了MJRefresh就可以自动计算占位图的隐藏和出现,如果没有tableview的情况下可以通过

rightTable.showLoadingView(show: true) rightTable.loadingHint  = "该分类您已全部入驻"复制代码

来控制UIView的占位图,和占位文字,当然也可以控制占位图片,当然UICollectionview和UITableview同属于UIScrollview的子类,所以UICollectionview也可以用这些方法

转载地址:http://deqlx.baihongyu.com/

你可能感兴趣的文章
让VMware ESX中的虚拟机随esx开机自动启动
查看>>
rhel6.5解决包的依赖的一个处理方法
查看>>
小功能隐藏着大学问---windows的ACL带来的挑战
查看>>
RSA2012系列(4):网络战揭秘
查看>>
Puppet扩展篇6-通过横向扩展puppetmaster增加架构的灵活性
查看>>
西安OpenParty11月29日活动高清图文回顾——新增西安APEC蓝美图!
查看>>
SFB 项目经验-16-呼叫前客户端性能测试
查看>>
我是如何帮助创业公司改进企业工作的
查看>>
taglist
查看>>
UITabBarController 的使用
查看>>
卡特兰数
查看>>
epoll实现机制分析
查看>>
windows 2008 r2 安装TabsStudio
查看>>
级联分类器训练
查看>>
linux下日语语言包安装
查看>>
Java内存回收 - 落日之心的日志 - 网易博客
查看>>
微软BI 之SSIS 系列 - 导出数据到 Excel 2013 的实现
查看>>
php 学习路线 赵兴壮2014年4月28 日 加油
查看>>
高精确度且线程分离的定时器——多媒体定时器
查看>>
Linux命令工具基础04 磁盘管理
查看>>