美文网首页
UIScrollView、UIView、UILabel、UIIm

UIScrollView、UIView、UILabel、UIIm

作者: define南拳 | 来源:发表于2017-11-27 14:23 被阅读0次

    记录使用runtime修改UIScrollview的经历

    今天突然发现一个问题,UITableView中添加的几个按钮,点击后push到其他ViewController中,结果按钮的选中效果不显示,查了一下UIScrollView的API,发现了delaysContentTouches这个属性

    @property (nonatomic) BOOL delaysContentTouches;// default is YES. if NO, we immediately call -touchesShouldBegin:withEvent:inContentView:. this has no effect on presses

    这个属性默认为YES,也就是会延迟发送touchesShouldbegin这个消息,也就导致了按钮的选中效果没有及时显示出来

    那么如何设置一下delaysContentTouches这个属性为NO就好了

    _scrollView.delaysContentTouches=NO;

    bingo,成功!!!

    那么问题来了,项目中有很多的TableView、ScrollView、WebView,挨个的去设置ScrollView的这个属性,未免改动太大,而且风险较高,此时我想到了runtime,那么如何用runtime实现这个效果呢,该写到那个方法中呢?

    大家都知道+(void)load和+(void)initialize方法的吧,通俗解释就是load和initialize都是在类加载进内存的时候就会执行,也就是说在main函数调用之前执行。

    题外话:load和initialize两个方法都不在main函数的autoreleasepool中,是在main函数之前加载到内存中的,所以,对于这两个方法内初始化的私有变量需要程序员们来处理,怎么处理呢?ARC的话只需要@autoreleasepool封装就好了

    我们选择在load中实现,首先因为initialize是在该类或者类对象被调用的时候才会执行,其次,initialize只会调用一次,而且我们要用category实现,类的每一个category类中都会执行各自的load类方法;再者,load我们要对默认值进行修改,应当尽量提前的替换掉方法块才可以

    废话不多说,上代码

    + (void)load {

            static dispatch_once_t onceToken;

            dispatch_once(&onceToken, ^{

                    Class class = [self class];

                    SEL originInitSel = @selector(initWithFrame:);

                    SEL newInitSel = @selector(xxxx_initWithFrame:);

                    Method originalMethod = class_getInstanceMethod(class, originInitSel);

                    Method swizzledMethod = class_getInstanceMethod(class, newInitSel);

                    BOOL didAddMethod =class_addMethod(class,originInitSel,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));

                    if (didAddMethod) {

                            class_replaceMethod(class,newInitSel,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));

                    } else {

                            method_exchangeImplementations(originalMethod, swizzledMethod);

                    }

        });

    }

    /**

    重置系统的默认值

    让scrollview中的按钮可以随时显示按钮的按下动作

    **/

    - (id)xxxx_initWithFrame:(CGRect)frame{

            UIScrollView* vscroll = [self xxxx_initWithFrame:frame];///一定要注意这里

            vscroll.delaysContentTouches = NO;

            return vscroll;

    }

    @end

    盗用其他同学的图片:

    这是苹果的消息传递的概要原理 这是Swizzle的概要原理

    我在这里也描述一下我个人的理解:

    1.在编译阶段,llvm会生成一个方法名(字符串、SEL)、类(字符串、class name)和函数指针(IMP)之间的映射表(hashtable)

    2.消息传递的概要过程:

        通过方法名(SEL)查找对应的函数指针(IMP)

    3.调用方法块(IMP)

    那么Swizzle其实就是变更了方法名(SEL)和函数指针(IMP)之间的映射关系。

    其实理解起来很简单~不明白的小伙伴可以在评论区交流一下,欢迎拍砖

    UIView/UIImageView也可以用类似的方法修改任意的默认值咯


    疑问:在我们的iOS开发过程中,如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。做到优化内存,节省资源的效果,这是为什么呢?

    我的qq是:58901119 多交流哟~

    相关文章

      网友评论

          本文标题:UIScrollView、UIView、UILabel、UIIm

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