让你的iOS应用程序支持运行JavaScript脚本:JavaScriptCore框架详解(四)

简介: 让你的iOS应用程序支持运行JavaScript脚本:JavaScriptCore框架详解

八、Hybird App 构建思路


   Hybird App是指混合模式移动应用,即其中既包含原生的结构有内嵌有Web的组件。这种App不仅性能和用户体验可以达到和原生所差无几的程度,更大的优势在于bug修复快,版本迭代无需发版。3月8日苹果给许多开发者发送了一封警告邮件,主要是提示开发者下载脚本动态更改App原本行为的做法将会被提审拒绝。其实这次邮件所提内容和Hybird App并无太大关系(对ReactNative也没有影响),苹果警告的是网络下发脚本并且使用runtime动态修改Native行为的应用,Hybird App的实质并没有修改原Native的行为,而是将下发的资源进行加载和界面渲染,类似WebView。


   关于混合开发,我们有两种模式:


   1.Native内嵌WebView,通过JS与OC交互实现业务无缝的衔接。


   无论是UIWebView还是WKWebKit,我们都可以在其中拿到当前的JSContext,然是使用前面介绍的方法便可以实现数据互通与交互。这种方式是最简单的混合开发,但其性能和原生相比要差一些。示意图如下:


image.png


   2.下发JS脚本,使用类似ReactNative的框架进行原生渲染


   这是一种效率非常高的混合开发模式,并且ReactNative也本身支持android和iOS公用一套代码。我们也可以使用JavaScriptCore自己实现一套解析逻辑,使用JavaScript来编写Native应用,要完整实现这样一套东西太复杂了,我们也没有能力完成一个如此庞大的工程,但是我们可以做一个小Demo来模拟其原理,这样可以更好的帮助我们理解Hybird App的构建原理。


我们打算实现这样的功能:通过下发JS脚本创建原生的UILabel标签与UIButton控件,首先编写JS代码如下:


(function(){

   console.log("ProgectInit");

   //JS脚本加载完成后 自动render界面

   return render();

})();


//JS标签类

function Label(rect,text,color){

   this.rect = rect;

   this.text = text;

   this.color = color;

   this.typeName = "Label";

}

//JS按钮类

function Button(rect,text,callFunc){

   this.rect = rect;

   this.text = text;

   this.callFunc = callFunc;

   this.typeName = "Button";

}

//JS Rect类

function Rect(x,y,width,height){

   this.x = x;

   this.y = y;

   this.width = width;

   this.height = height;

}

//JS颜色类

function Color(r,g,b,a){

   this.r = r;

   this.g = g;

   this.b = b;

   this.a = a;

}

//渲染方法 界面的渲染写在这里面

function render(){

   var rect = new Rect(20,100,280,30);

   var color = new Color(1,0,0,1);

   var label = new Label(rect,"Hello World",color);

   var rect2 = new Rect(20,150,280,30);

   var color2 = new Color(0,1,0,1);

   var label2 = new Label(rect2,"Hello Native",color2);

   var rect3 = new Rect(20,200,280,30);

   var color3 = new Color(0,0,1,1);

   var label3 = new Label(rect3,"Hello JavaScript",color3);

   var rect4 = new Rect(20,240,280,30);

   var button = new Button(rect4,"我是一个按钮",function(){

                           var randColor = new Color(Math.random(),Math.random(),Math.random(),1);

                           Globle.changeBackgroundColor(randColor);

                           });

   //将控件以数组形式返回

   return [label,label2,label3,button];

}


创建一个Objective-C类绑定到JS全局对象上,作为OC方法的桥接器:


//.h

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

#import <JavaScriptCore/JavaScriptCore.h>

@protocol GloblePrptocol <JSExport>

-(void)changeBackgroundColor:(JSValue *)value;

@end

@interface Globle : NSObject<GloblePrptocol>

@property(nonatomic,weak)UIViewController * ownerController;

@end

//.m

#import "Globle.h"


@implementation Globle

-(void)changeBackgroundColor:(JSValue *)value{

   self.ownerController.view.backgroundColor = [UIColor colorWithRed:value[@"r"].toDouble green:value[@"g"].toDouble blue:value[@"b"].toDouble alpha:value[@"a"].toDouble];

}

@end

在ViewController中实现一个界面渲染的render解释方法,并建立按钮的方法转换,如下:


//

//  ViewController.m

//  JavaScriptCoreTest

//

//  Created by vip on 17/3/6.

//  Copyright © 2017年 jaki. All rights reserved.

//


#import "ViewController.h"

#import <JavaScriptCore/JavaScriptCore.h>

#import "Globle.h"

@interface ViewController ()


@property(nonatomic,strong)JSContext * jsContext;

@property(nonatomic,strong)NSMutableArray * actionArray;

@property(nonatomic,strong)Globle * globle;

@end


@implementation ViewController


- (void)viewDidLoad {

   [super viewDidLoad];

   //创建JS运行环境

   self.jsContext = [JSContext new];

   //绑定桥接器

   self.globle =  [Globle new];

   self.globle.ownerController = self;

   self.jsContext[@"Globle"] = self.globle;

   self.actionArray = [NSMutableArray array];

   [self render];

}

//界面渲染解释器

-(void)render{

   NSString * path = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"js"];

   NSData * jsData = [[NSData alloc]initWithContentsOfFile:path];

   NSString * jsCode = [[NSString alloc]initWithData:jsData encoding:NSUTF8StringEncoding];

   JSValue * jsVlaue = [self.jsContext evaluateScript:jsCode];

   for (int i=0; i<jsVlaue.toArray.count; i++) {

       JSValue * subValue = [jsVlaue objectAtIndexedSubscript:i];

       if ([[subValue objectForKeyedSubscript:@"typeName"].toString isEqualToString:@"Label"]) {

           UILabel * label = [UILabel new];

           label.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);

           label.text = subValue[@"text"].toString;

           label.textColor = [UIColor colorWithRed:subValue[@"color"][@"r"].toDouble green:subValue[@"color"][@"g"].toDouble blue:subValue[@"color"][@"b"].toDouble alpha:subValue[@"color"][@"a"].toDouble];

           [self.view addSubview:label];

       }else if ([[subValue objectForKeyedSubscript:@"typeName"].toString isEqualToString:@"Button"]){

           UIButton * button = [UIButton buttonWithType:UIButtonTypeSystem];

           button.frame = CGRectMake(subValue[@"rect"][@"x"].toDouble, subValue[@"rect"][@"y"].toDouble, subValue[@"rect"][@"width"].toDouble, subValue[@"rect"][@"height"].toDouble);

           [button setTitle:subValue[@"text"].toString forState:UIControlStateNormal];

           button.tag = self.actionArray.count;

           [button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];

           [self.actionArray addObject:subValue[@"callFunc"]];

           [self.view addSubview:button];

         

       }

   }

}

//按钮转换方法

-(void)buttonAction:(UIButton *)btn{

   JSValue * action  = self.actionArray[btn.tag];

   //执行JS方法

   [action callWithArguments:nil];

}




@end

运行工程,效果如下图所示,点击按钮即可实现简单的界面颜色切换:

image.png



上面的示例工程我只实现了UILabel类与UIButton类的JS-OC转换,如果将原生控件和JS对象再进行一层绑定,并且实现大部分JS类与原生类和他们内部的属性,则我们就开发了一套Hybird App开发框架,但并没有这个必要,如果你对更多兴趣,可以深入学习下ReactNative。


   文中的示例Demo我放在了Github上,地址如下:https://github.com/ZYHshao/Demo-Hybird

目录
相关文章
|
1月前
|
搜索推荐 数据管理 定位技术
iOS应用开发中有多种主流框架
iOS应用开发中有多种主流框架
180 60
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
157 4
|
9天前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
92 66
|
1月前
|
JSON 移动开发 JavaScript
在浏览器执行js脚本的两种方式
【10月更文挑战第20天】本文介绍了在浏览器中执行HTTP请求的两种方式:`fetch`和`XMLHttpRequest`。`fetch`支持GET和POST请求,返回Promise对象,可以方便地处理异步操作。`XMLHttpRequest`则通过回调函数处理请求结果,适用于需要兼容旧浏览器的场景。文中还提供了具体的代码示例。
在浏览器执行js脚本的两种方式
|
1月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
33 2
|
1月前
|
iOS开发 开发者
探索iOS开发中的SwiftUI框架
【10月更文挑战第39天】在苹果的生态系统中,SwiftUI框架以其声明式语法和易用性成为开发者的新宠。本文将深入SwiftUI的核心概念,通过实际案例展示如何利用这一框架快速构建用户界面,并探讨其对iOS应用开发流程的影响。
|
1月前
|
JSON 前端开发 API
探索iOS开发之旅:打造你的第一个天气应用
【10月更文挑战第36天】在这篇文章中,我们将踏上一段激动人心的旅程,一起构建属于我们自己的iOS天气应用。通过这个实战项目,你将学习到如何从零开始搭建一个iOS应用,掌握基本的用户界面设计、网络请求处理以及数据解析等核心技能。无论你是编程新手还是希望扩展你的iOS开发技能,这个项目都将为你提供宝贵的实践经验。准备好了吗?让我们开始吧!
|
1月前
|
Swift iOS开发 UED
如何使用Swift和UIKit在iOS应用中实现自定义按钮动画
本文通过一个具体案例,介绍如何使用Swift和UIKit在iOS应用中实现自定义按钮动画。当用户点击按钮时,按钮将从圆形变为椭圆形,颜色从蓝色渐变到绿色;释放按钮时,动画以相反方式恢复。通过UIView的动画方法和弹簧动画效果,实现平滑自然的过渡。
59 1
|
1月前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。
|
2月前
|
移动开发 网络协议 小程序
基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v9.1版已发布
RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题
65 5