美文网首页
JOBridge之四使用方法(可用代替JSPatch)

JOBridge之四使用方法(可用代替JSPatch)

作者: 吸血鬼de晚餐 | 来源:发表于2019-03-26 16:13 被阅读0次

    本博客为原创为 吸血鬼de晚餐,转载随意,但请标明出处。
    博客原文 JOBridge之四使用方法(可用代替JSPatch)

    CSDN真恶心🤢,把我之前的一些博客爬取下来,然后挂在一些僵尸账号就成了其原创博文,甚至连发布时间都不改的!

    JSPatch被苹果爸爸封了以后hotfix怎么办?业务需求需要尽快上线怎么办?可以尝试使用JOBridge。其使用了和JSPatch不一样的实现方案,全部由OC实现,比JSPatch功能更强,性能也更好,语法上也基本保持一致(之后我会给出一些示例),当然最关键的是苹果爸爸还不知道!

    原理方案

    请移步本人博客
    JOBridge之一任意方法的Swizzle(可用代替JSPatch) https://www.jianshu.com/p/905e06eeda7b
    JOBridge之二JS注册类和访问所有Native方法(可用代替JSPatch) https://www.jianshu.com/p/f457528fedeb
    JOBridge之三C函数OC化和语法增加以及优化(可用代替JSPatch) https://www.jianshu.com/p/c1161f61ed96

    使用方法

    开始之前,先说明一下通用调用桥。

    *JOBridge通用调用桥:

    在js的Object.prototype中添加了一个_oc_的方法,其负责转换处理js传递过来的参数,然后调用OC的对应的方法,最后将返回值回传给js。这种方式被我称为JOBridge通用调用桥。通过该桥,JOBridge将OC几乎所有的方法开放给了js,C方法也可以通过其开放给js(需要预先挖坑,C函数入口地址需要硬编译)。

    OC端:

    1、执行js
    [JOBridge bridge];//初始化
    [JOBridge evaluateScript:script];//执行js
    

    这两句就可以把JS代码跑起来了。

    2、扩展C方法(需要的时候再使用)

    OC方法一般都是可以直接调用的,C方法使用之前需要挖坑。通过C方法OC化,已经使挖坑变得比较简单了。扩展有三种方式:

    第一种不替换存储字典,放在全局默认C方法容器对象下,调用registerObject:name:needTransform:并向js中注册对象JC,js中用JC.test()来访问。其中needTransform表示是否使用通用调用桥处理参数和调用,如果需要的话,之前需要调用JOMapCFunction注册OC的方法并给以签名。

    #if __arm64__
    
    @interface JOCPlugin : JOCFunction
    @end
    void test(id obj) { NSLog(@"%@", obj); }
    
    
    static NSMutableDictionary *JOTest;
    
    @implementation JOCPlugin
    + (void)load {
        [self registerPlugin];
    }
    
    + (void)initPlugin {//重写本方法,JOBridge初始化时会调用本方法注册
        JOMapCFunction(test, JOSigns(void, id));
        [self registerObject:JOMakeObj([self class]) name:@"JC" needTransform:YES];
    }
    @end
    #endif
    

    第二种替换存储字典(需要重写pluginStore方法),比如这里将“test”方法和对应签名关系,放在字典JCTest中,在js里用JCTest.test()访问。needTransform同理。

    #if __arm64__
    @interface JOCPluginTest : JOCFunction
    @end
    
    void test(id obj) { NSLog(@"%@", obj); }
    
    static NSMutableDictionary *JOTest;
    
    @implementation JOCPluginTest
    + (void)load {
        JOTest = [NSMutableDictionary dictionary];
        [self registerPlugin];
    }
    
    + (void)initPlugin {
        JOMapCFunction(test, JOSigns(void, id));
        [self registerObject:JOMakeObj([self class]) name:@"JCTest" needTransform:YES];
    }
    
    + (NSMutableDictionary *)pluginStore {
        return JOTest;
    }
    @end
    #endif
    
    3、扩展全局常量

    这里放在JCTest1下(需要重写pluginStore方法),在js中用JCTest1.RGB(), JCTest1.RED_VALUE 访问。这种方式也可以提供方法调用,其不使用通用调用桥处理参数,所以需要自己解析参数组装参数,较为麻烦,但可以比较灵活的提供一些特殊的方法。

    #if __arm64__
    @interface JOCPluginTest1 : JOPluginBase
    @end
    
    
    static NSMutableDictionary *JOTest1;
    
    @implementation JOCPluginTest1
    + (void)load {
        JOTest1 = [NSMutableDictionary dictionary];
        [self registerPlugin];
    }
    
    + (void)initPlugin {
        [self pluginStore][@"RGB"] = ^(JSValue *jsvalue) {
            uint32_t hex = [jsvalue toUInt32] ;
            return JOMakeObj([UIColor colorWithRed:(((hex & 0xFF0000) >> 16))/255.0 green:(((hex & 0xFF00) >> 8))/255.0 blue:((hex & 0xFF))/255.0 alpha:1.0]);
        };
        [self pluginStore][@"RED_VALUE"] = @(0xFF0000);
        [self registerObject:[self pluginStore] name:@"JGTest1" needTransform:NO];
    
    }
    
    + (NSMutableDictionary *)pluginStore {
        return JOTest1;
    }
    @end
    #endif
    
    

    JS端:

    JS的语法我尽量让其类似于JSPatch,减少学习成本,但是还是有很许多不一样的地方,而且内容比较多,一点一点说明。

    本博客为原创为 吸血鬼de晚餐,转载随意,但请标明出处。

    全局方法

    __weak/__strong/__bridge_id/__bridge_pointer:这些方法也就不用多说了。

    JOC方法

    主功能对象JOC,包含最常见和通用功能,其不走JOBridge的通用调用桥。

    1、interface:最重要的方法,其用于注册类,替换方法等,所有功能都依赖其实现。语法JOC.interface("类名:父类名<协议名>", 属性字典, 对象方法字典, 类方法字典);实例如下

    ;JOC.interface('UserView : UIView', {
        watchView:['id','strong'],
     },
     {
         initWithFrame_:function(frame) {
                    //创建对象返回
         },
    },
    null);
    

    协议最多32个,也可以省略。属性字典,前者属性名字,后面是属性关键字,暂不支持非OC对象,所以全部使用

    ['id','strong']就行了。方法字典,key就是方法名字,下划线_会替换成:__替换成_。js调用本方法语法为userView.initWithFrame_(frame);

    2、class:通过该方法访问已有的类Class。例如:JOC.class("UIView").new()。可以创建一个UIView对象。Foundation和UIKit常用的类都已经作为常量开放给js,直接使用类名即可访问,例如:UIView.new();

    具体参见:

    #define JO_UIKit_Classes @[@"UIImage",@"UIColor",@"UIView",@"UITableView",@"UILabel",@"UIButton",\
    @"UIImageView",@"UIApplication",@"UITableViewCell",@"UIAlertView",@"UINavigationController",\
    @"UIViewController",@"UIFont",@"UIScreen",@"UIScrollView",@"UINavigationItem",@"UINavigationBar",\
    @"UIWebView",@"UIWindow",@"UITextView",@"UITextField",@"UITapGestureRecognizer",@"UITabBarController",\
    @"UITabBar",@"UISwitch",@"UISlider",@"UISearchBar",@"UIProgressView",@"UICollectionView",\
    @"UICollectionViewCell",@"UIBarButtonItem",@"UIAppearance",@"UIAlertController"]
    
    #define JO_Foundation_Classes @[@"NSTimer",@"NSMutableArray",@"NSArray",@"NSDictionary",@"NSMutableDictionary",\
    @"NSString",@"NSUserDefaults",@"NSURL",@"NSURLRequest",@"NSData",@"NSDate",@"NSCharacterSet",@"NSMutableString",\
    @"NSDateFormatter",@"NSError",@"NSException",@"NSFileManager",@"NSJSONSerialization",@"NSLock",@"NSNotification",\
    @"NSRegularExpression",@"NSSet",@"NSThread",@"NSURLResponse",@"NSValue",@"NSAttributedString",@"NSURLSession",\
    @"NSPredicate",@"NSTask",@"NSLocale",@"NSInvocation",@"NSMethodSignature",@"NSMutableAttributedString",@"NSBundle"]
    

    3、storeObject:将新对象存储对象容器中。这个需要举例具体说明。

    initWithFrame_:function(frame) {
        var inited = self.JOSUPER_initWithFrame_(frame);
        if (inited) {
            JOC.storeObject(self, inited);
            return self;
        }
        return null;
     },
    

    通过调用父类的JOSUPER_initWithFrame_创建得到了一个新的对象inited,但是因为是在js语言中,其无法直接赋值给self,所以需要通过storeObject来间接赋值给self。js中拿到的OC对象或数据都是经过包装的,比如这里的self和inited都是由JOObj对象,其内部引用着self。storeObject就是将JOObj中的引用对象改为inited所引用的对象。

    4、retain/release:手动计数器管理,不用多说

    5、new:快捷对象创建,实例:JOC.new("UIView"),可以用来代替JOC.class("UIView").new()或者JOC.class("UIView").alloc().init()

    6、structToObject:有些情况下需要访问结构体内部的数据,需要将其转成js能识别的对象。比如:CGRect转成{'x':0,'y':0,'width':375,'height':44}。目前支持几种常见的结构体:CGRect,CGPoint,CGSize,NSRange,CGAffineTransform,UIEdgeInsets,UIOffset。如果js不访问结构体内部数据,则不需要调用该方法,直接当普通参数传递即可。

    7、getPointerValue/setPointerValue:获取指针对象JOPointerObj中包装的指针值,同理赋值也一样。实例:

    array.enumerateObjectsUsingBlock_(['@@@Q*',function(obj, idx, stop) 
    {
         JOC.log('obj: ' + JOC.getString(obj) + ' idx: ' + idx + ' stop: ' + stop);
         JOC.setPointerValue(stop, true);
    }]);
    

    调用NSArray的枚举,在回调中把stop设置为true。

    8、getPointerDoubleValue/setPointerDoubleValue:和上面的同理,数据类型为浮点数。

    9、malloc/free:分配/释放内存。实例:

    var ptr = JOC.malloc(8);
    JOC.setPointerDoubleValue(ptr, 123.56);
    JOC.log('double value: ' + JOC.getPointerDoubleValue(ptr));
    JOC.free(ptr);
    

    这里分配了一个8Byte的内存空间,调用setPointerDoubleValue设置值,然后输出,最后调用free。其主要用途是用来解决指针参数传递子函数,子函数修改数据的问题。但限于autoreleasepool的问题 ,对于OC对象无法支持,目前只支持基础类型。

    例如:

    var changePtr = function(ptr) {
        JOC.setPointerValue(ptr, 100);
    };
    function() {
        var ptr = JOC.malloc(8);
          changePtr(p);
        JOC.free(ptr);
    }();
    

    10、array:创建OC数组。JOC.array(['Bill', 'UserInfo'])

    11、selector:获取selector,参数selector名字字符串。

    12、log:日志输出

    本博客为原创为 吸血鬼de晚餐,转载随意,但请标明出处。

    新增对象方法:

    有签名的情况下替换方法,只需要在 JOC.interface("类名:父类名<协议名>", 属性字典, 对象方法字典, 类方法字典); 中的 对象方法字典 添加对应的key->function就行了,如果想新写一个方法怎么办呢?添加一个key->array[sign string, function],实例如下:

    FitIphone5_:['d@:d', function(value) {
            return JOG.SCREEN_WIDTH > 320 ? value : value - 10;
    }],
    

    这里新增了一个名为FitIphone5:方法,然后js通过对应对象obj.FitIphone5_(width)来调用。可以看到这里和替换不同的是,FitIphone5_对应的是一个数组,第一个元素是签名字符串,第二个元素对应的函数。至于为什么搞成数组,呃…是因为懒,最初随手定义的,后来就懒得改了。

    JS调用OC方法规则:

    如果使用JOBridge通用调用桥,selector和参数需要遵循一些约定。

    参数规则:

    1、第一个参数固定的是selectorName。这个参数不用手动传递,由脚本根据规则生成,沿用了JSPatch的办法。例如:

    tableView.setDelegate_(self),会被转成tableView._oc_('setDelegate_',self)

    注意末尾"_"不能省略。

    2、第二个参数不同的情况不太一样,有可能作为特殊参数,目前只有一种特殊情况,就是调用有匿名参数函数时进行签名,其他情况下当普通参数即可。举个例子:

    var aString = NSString.JOVAR_stringWithFormat_('@@:@@@i@Bd','this is %@  %@ %d %@ %d %.2f', 'a', 'test', 90, self, true ,329.78398);
    

    '@@:@@@i@Bd'为签名,JOBridge通用调用桥会根据该签名去解析参数,'this is %@ %@ %d %@ %d %.2f' 为格式化字符串,后面则是剩余参数。

    手动签名请用简单方式,不要带参数长度,或者复杂的符号,因为这里用的是最简单的签名识别方法,复杂的识别方法还没有实现。OC对象全部用@,其他的基础类型对应就行。

    3、余下的参数位都是普通参数,依次传递即可。

    4、注意:为了让JOBridge运行的更快,对于Number/NSNumber,String/NSString我都沿用原数据,其可以被js和OC直接共享使用,没有转成JOObj封装对象,所以必要时需要注意其带来的局限性和副作用,比如:不能使用__bridge_pointer转指针。

    5、block参数:

    如果需要传递block参数,也和类新增方法语法类似,例如:

    self.anLable().mas__makeConstraints_(
        ['v@@', function(make) {
            make.left().equalTo()(self.anIcon().mas__right()).valueOffset()(5);
            make.top().equalTo()(self.accountLable().mas__bottom()).valueOffset()(self.FitIphone5_(17));
            make.width().equalTo()(150);
            make.height().equalTo()(20);
        }]
    );
    

    如果只是创建局部的调用,由js自己使用,不需要传递给JOBridge,那就创建一个function直接使用即可。

    6、返回block调用:

    make.left().equalTo()(self.anIcon().mas__right()).valueOffset()(5);

    在上面的语句中,equalTo()和valueOffset()都返回的是block,再次调用即可。容易出错,需要多加小心。

    selector规则:

    1、js所有对OC对象属性,方法的使用都是通过方法调用来完成的(除了字典数据)。

    2、若走JOBridge通用调用桥,OC方法中所有的变成___变成_。例如:

    nav.navigationBar().setBackgroundImage_forBarMetrics_(image, JOG.UIBarMetricsDefault);

    3、所有的赋值操作需要调用对应的setter方法、JOSetIvarJOSetIvaI或其他有赋值功能的方法完成。等号赋值只对js变量有效。

    4、调用父类方法请在前面添加前缀JOSUPER_

    5、调用变长参数(匿名参数)方法添加前缀JOVAR_

    6、特殊方法JOGetIvarJOSetIvar。在某些情况下,我们需要访问类的成员变量,而又不想通过getter,setter的方式,那么就需要使用这俩方法了。实例如下:
    我们重写tableView的getter方法,在该方法体里面在使用getter访问自己,就会死循环,所以需要另外一种方法来完成。

    tableView:function() {
        if (!self.JOGetIvar('_tableView')) {
            var rect = JC.CGRectMake(0, 64, JOG.SCREEN_WIDTH, JOG.SCREEN_HEIGHT - 64);
            var tableView = UITableView.alloc().initWithFrame_style_(rect, 
                                                                   JOG.UITableViewStyleGrouped);
            self.view().addSubview_(tableView);
            tableView.setDelegate_(self);
            tableView.setBounces_(false);
            tableView.setDataSource_(self);
            tableView.setSectionFooterHeight_(4);
            tableView.setSectionHeaderHeight_(4);
            tableView.setBackgroundColor_(JOG.RGB(0xF5F5F9));
            tableView.setTableHeaderView_(
                                UIView.alloc().initWithFrame_(JC.CGRectMake(0,0,375,0.01)));
            tableView.setSeparatorStyle_(JOG.UITableViewCellSeparatorStyleNone);
            self.JOSetIvar('_tableView', tableView);
        }
        return self.JOGetIvar('_tableView');
    },
    

    以上是通过懒加载的方式创建tableView,其通过self.JOGetIvar('_tableView')来获取成员对象是否已经有值,没有则创建,完成后调用self.JOSetIvar('_tableView', tableView);赋值。

    _tableView,成员变量name,如果没有手动修改名字,默认都是_+name。

    6、特殊方法JOGetIvarIJOSetIvaI。这两者和上面两者类似,不同之处在于其负责操作基础数据类型。

    C函数调用

    JC (C方法在js中关联的全局对象)

    看过JOBridge实现原理就知道,C函数会被转成OC方法来调用,然后走JOBridge通用调用桥。默认添加在了全局对象JC名下,目前选取了GCD,Foundation,UIKit,CGRect,CGPath,CGColor,CGContext,CGAffineTransform,CGImage,CGBitmapContext,Math大部分常用函数,CoreFundation方法太多就只添加了几个。如果有需要,可以参照上面的扩展C方法,自行添加。

    举些例子

    //ImageContext
    JC.UIGraphicsBeginImageContextWithOptions(JC.CGSizeMake(r.width, r.height), false, UIScreen.mainScreen().scale());
    //这是一个包裹指针的JOPointerObj对象,js不用处理,直接传给C/OC即可
    var context = JC.UIGraphicsGetCurrentContext();
    JC.CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor());
    JC.CGContextSetLineWidth(context, 2);
    
    //CGD
    var aself = __weak(self);
    JC.dispatch__async(JC.dispatch__get__main__queue(),['v@',function()
    {
        JOC.log('dispatch_async is work ' + aself);
    }]);
    //CGRect
    JC.CGRectMake(0, 0, JOG.SCREEN_WIDTH, 45);
    //Math
    JC.pow(10, -5 * JC.sinh(numTime * JOG.M_PI_2))
    
    JOG (OC全局常量在js中的关联对象)

    Foundation和UIKit下的绝大多数全局常量都被包含在内,顺带加上了GCD,方便使用。

    JOG.M_PI_2;
    //Foundation
    JOG.NSFontAttributeName;
    JOG.NSForegroundColorAttributeName;
    JOG.NSCalendarUnitSecond;
    JOG.NSUTF8StringEncoding;
    //UIKit
    JOG.UIStatusBarStyleDefault;
    JOG.UITableViewCellAccessoryDisclosureIndicator;
    JOG.UIApplicationStateBackground;
    JOG.UIControlStateNormal;
    //GCD
    JOG.DISPATCH_SOURCE_TYPE_TIMER;
    JOG.DISPATCH_QUEUE_SERIAL
    

    另外JOG下还可以添加不走"JOBridge通用调用桥"的方法。例如:

    JOG.RGB(0xFF0000);
    

    对应实现见下方,其需要手动处理参数然后调用对应UIColor创建方法,最后调用JOGetObj包装成JOObj对象返回。

    pluginStore[@"RGB"] = ^(JSValue *jsvalue) {
        uint32_t hex = [jsvalue toUInt32] ;
        return JOGetObj([UIColor colorWithRed:(((hex & 0xFF0000) >> 16))/255.0 green:(((hex & 0xFF00) >> 8))/255.0 blue:((hex & 0xFF))/255.0 alpha:1.0]);
    };
    

    null处理

    对于可能为空的数据,调用方法之前,一定要检查null,否则会js报错。

    本博客为原创为 吸血鬼de晚餐,转载随意,但请标明出处。

    实例一:

    简单的UITableView使用

    ;JOC.interface('JDViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>',
    {
       data:['NSMutableArray','strong'],
       tableView:['UITableView','strong'],
    }, {
       viewDidLoad:function() {
           var arr = JOC.class('NSMutableArray').alloc().init();
           
           arr.addObject_('这是第1个cell');
           arr.addObject_('这是第2个cell');
           arr.addObject_('这是第3个cell');
           self.setData_(arr);
           
           self.setTitle_('JDViewController');
           self.view().setBackgroundColor_(JOC.class('UIColor').whiteColor());
           
           
           var rect = JC.CGRectMake(0, 60, 375, 500);
           var r = JOC.structToObject(rect);
           JOC.log(r.x + ' ' + r.y + '  ' + r.width + ' ' + r.height);
           
           var tableView = JOC.class('UITableView').alloc()
           .initWithFrame_style_(rect,JOG.UITableViewStyleGrouped);
           tableView.setDelegate_(self);
           tableView.setDataSource_(self);
           self.view().addSubview_(tableView);
           self.setTableView_(tableView);
       },
                   
       viewDidAppear_:function(animated) {
          
       },
                   
       numberOfSectionsInTableView_: function(tableView) {
           return 1;
       },
                   
       tableView_numberOfRowsInSection_: function(tableView, section) {
           return self.data().count();
       },
                   
       tableView_cellForRowAtIndexPath_: function(tableView, indexPath) {
           JOC.log('tableVIew: '+ tableView);
           
           var cell = tableView.dequeueReusableCellWithIdentifier_('cell')
           if (!cell) {
               cell = JOC.class('UITableViewCell').alloc()
               .initWithStyle_reuseIdentifier_(JOG.UITableViewCellStyleValue1, 'cell')
           }
                   
           cell.textLabel().setText_(self.data().objectAtIndex_(indexPath.row()));
           return cell;
       },
                   
       tableView_heightForRowAtIndexPath_: function(tableView, indexPath) {
           return 88;
       },
                   
       tableView_didSelectRowAtIndexPath_: function(tableView, indexPath) {
           var alertView = JOC.class('UIAlertView').alloc()
           .initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles_
           ('Alert',
            'cell has been clicked',
            self,
            'OK',
            null);
            
           alertView.show();
       },
    });
    
    

    实例二

    使用CoreGraphics画一只简单的时钟。

    ;JOC.interface('WatchView : UIView', {
        timer:['id','strong'],
        time:['id','strong'],
        back:['id','strong'],
     },
     {
        init : function() {
            var inited = self.JOSUPER_init();
            if (inited) {
                JOC.storeObject(self, inited);
                self.setTime_(NSMutableDictionary.dictionary());
    
                var timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(1, self, JOC.selector('timeTick'), null, true);
                timer.fire();
                self.JOSetIvar('_timer', timer);
            }
            return self;
        },
        invalidate: ['v@:', function() {
            self.timer().invalidate();
        }],
        timeTick : ['v@:', function() {
            var calendar = JOC.class('NSCalendar').currentCalendar();
            var unitFlags = JOG.NSCalendarUnitYear | JOG.NSCalendarUnitMonth | JOG.NSCalendarUnitDay | JOG.NSCalendarUnitHour | JOG.NSCalendarUnitMinute | JOG.NSCalendarUnitSecond;
                            
            var comps = calendar.components_fromDate_(unitFlags, NSDate.date());
            var hour = comps.hour();
    
            self.time().setValue_forKey_(parseInt(hour/12) ? hour - 12 : hour, 'hour');
            self.time().setValue_forKey_(comps.minute(), 'min');
            self.time().setValue_forKey_(comps.second(), 'sec');
    
            self.setNeedsDisplay();
         }],
        drawRect_ : function(rect) {
            self.setBackgroundColor_(UIColor.blackColor());
            var back = self.back();
            var r = JOC.structToObject(rect);
            var centerX = r.width/2.0;
            var centerY = r.height/2.0;
    
            if (!back) {
                JC.UIGraphicsBeginImageContextWithOptions(JC.CGSizeMake(r.width, r.height), false, UIScreen.mainScreen().scale());
                var context = JC.UIGraphicsGetCurrentContext();
                JC.CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor());
                JC.CGContextSetLineWidth(context, 2);
                JC.CGContextMoveToPoint(context, centerX, centerY - 0.5);
                JC.CGContextAddLineToPoint(context, centerX, centerY + 0.5)
                JC.CGContextSetLineCap(context, 1);
                JC.CGContextStrokePath(context);
                self.drawWithContext_angle_rotateCenterX_Y_(context, JOG.M_PI_2, centerX, centerY);
                back = JC.UIGraphicsGetImageFromCurrentImageContext();
                self.setBack_(back);
                JC.UIGraphicsEndImageContext();
            }
            back = self.back();
            if (back) {
                back.drawInRect_(JC.CGRectMake(0,0,r.width, r.height));
            }
            var context = JC.UIGraphicsGetCurrentContext();
            self.drawIndicatorWithContext_angle_rotateCenterX_Y_(context, JOG.M_PI_2, centerX, centerY);
        }, 
        drawIndicatorWithContext_angle_rotateCenterX_Y_ : ['v@:*ddd',function(context, angle, x, y) {
            var time = self.JOGetIvar('_time');
            var sec = time.objectForKey_('sec');
            var min = time.objectForKey_('min');
            var hour = time.objectForKey_('hour');
            JC.CGContextSaveGState(context);
            self.rotateWithContext_angle_fromCenterX_Y_(context, angle, x, y);
    
            
            JC.CGContextSaveGState(context);
            JC.CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor());
            self.rotateWithContext_angle_fromCenterX_Y_(context, 2 * JOG.M_PI * sec / 60, x, y);
            JC.CGContextSetLineWidth(context, 0.5);
            JC.CGContextMoveToPoint(context, x / 3.0, y);
            JC.CGContextAddLineToPoint(context, x, y - 1);
            JC.CGContextAddLineToPoint(context, x + x / 8.0, y);
            JC.CGContextAddLineToPoint(context, x, y + 1);
            JC.CGContextAddLineToPoint(context, x / 3.0, y);
            JC.CGContextStrokePath(context);
            JC.CGContextRestoreGState(context);
    
    
            JC.CGContextSaveGState(context);
            JC.CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor());
            self.rotateWithContext_angle_fromCenterX_Y_(context, 2 * JOG.M_PI * min / 60 + 2 * JOG.M_PI * sec / 60.0 / 60.0, x, y);
            JC.CGContextSetLineWidth(context, 0.5);
            JC.CGContextMoveToPoint(context, x / 2.0, y);
            JC.CGContextAddLineToPoint(context, x, y - 3);
            JC.CGContextAddLineToPoint(context, x + x / 10.0, y);
            JC.CGContextAddLineToPoint(context, x, y + 3);
            JC.CGContextAddLineToPoint(context, x / 2.0, y);
            JC.CGContextStrokePath(context);
            JC.CGContextRestoreGState(context);
    
            JC.CGContextSaveGState(context);
            JC.CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor());
            self.rotateWithContext_angle_fromCenterX_Y_(context, 2 * JOG.M_PI * hour / 12 + 2 * JOG.M_PI * min / 60.0 / 12.0, x, y);
            JC.CGContextSetLineWidth(context, 0.5);
            JC.CGContextMoveToPoint(context, x / 1.5, y);
            JC.CGContextAddLineToPoint(context, x, y - 5);
            JC.CGContextAddLineToPoint(context, x + x / 15.0, y);
            JC.CGContextAddLineToPoint(context, x, y + 5);
            JC.CGContextAddLineToPoint(context, x / 1.5, y);
            JC.CGContextStrokePath(context);
            JC.CGContextRestoreGState(context);
    
            JC.CGContextRestoreGState(context);
    
        }],
    
        drawWithContext_angle_rotateCenterX_Y_ : ['v@:*ddd',function(context, angle, centerX, centerY) {
            JC.CGContextSaveGState(context);
            self.rotateWithContext_angle_fromCenterX_Y_(context, angle, centerX, centerY);
            JC.CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor());
            var font = UIFont.fontWithName_size_('Helvetica', centerX/6);
            var stringAttrs = NSDictionary.dictionaryWithObjects_forKeys_(JOC.array([font, UIColor.whiteColor()]) , JOC.array([JOG.NSFontAttributeName, JOG.NSForegroundColorAttributeName]));
    
            for (var i = 0; i < 60; ++i) {
                var width = 0;
                var x = 0;
                var len = 0;
                var attrStr = null;
                if (i % 15 == 0) {
                    width = centerX/30;
                    x = 0;
                    len = centerX/10;
                    var str = NSString.JOVAR_stringWithFormat_('@@:@q','%d', (parseInt(i/5) != 0 ? parseInt(i/5) : 12));
                    attrStr = NSAttributedString.alloc().initWithString_attributes_(str, stringAttrs);
                } else if (i % 5 == 0) {
                    width = centerX/50;
                    x = centerX/30;
                    len = centerX/10;
                    attrStr = NSAttributedString.alloc().initWithString_attributes_(NSString.JOVAR_stringWithFormat_('@@:@q','%d', parseInt(i/5)), stringAttrs);
                } else {
                    width = centerX/100;
                    x = centerX/20;
                    len = centerX/10;
                }
                JC.CGContextSetLineWidth(context, width);
                JC.CGContextMoveToPoint(context, x, centerY);
                JC.CGContextAddLineToPoint(context, len, centerY);
                JC.CGContextStrokePath(context);
    
                if (attrStr) {
                    attrStr.drawAtPoint_(JC.CGPointMake(centerX/5, centerY - centerX/15));
                }
                self.rotateWithContext_angle_fromCenterX_Y_(context, 2 * JOG.M_PI / 60.0 , centerX, centerY);
            }
            JC.CGContextRestoreGState(context);
        }],
      
        rotateWithContext_angle_fromCenterX_Y_ : ['v@:*ddd',function(context, angle, x, y) {
            JC.CGContextTranslateCTM(context, x, y);
            JC.CGContextRotateCTM(context, angle);
            JC.CGContextTranslateCTM(context, -x, -y);
        }]
     });
    

    相关文章

      网友评论

          本文标题:JOBridge之四使用方法(可用代替JSPatch)

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