摘要:为我们提供了模块来控制页面的导航。本文旨在探究的导航机制,然后实现一个供参考。承载页面的控制器,必须包含在一个中,否则导航无效。组件的,根据命名判断为根组件实际情况也是如此。在导航方面,它们的差异很大。
Weex为我们提供了navigator模块来控制页面的导航。Navigator模块究竟是怎么运作的,官方没有给我们一个感性的认识。本文旨在探究weex的导航机制,然后实现一个DEMO供参考。
推入一个页面,类似原生的pushViewController:animated:`。文档告诉我们这么做:
navigator.push({ url: "http://dotwe.org/raw/dist/519962541fcf6acd911986357ad9c2ed.js", animated: "true" })
顺藤摸瓜,找到原生模块WXNavigatorModule,push方法是这样定义的:
- (void)push:(NSDictionary *)param callback:(WXModuleCallback)callback { idnavigator = [self navigator]; UIViewController *container = self.weexInstance.viewController; [navigator pushViewControllerWithParam:param completion:^(NSString *code, NSDictionary *responseData) { if (callback && code) { callback(code); } } withContainer:container]; }
核心代码在WXNavigationProtocol协议的默认实现中。于是乎我们切换到官方实现类WXNavigationDefaultImpl,关键的代码都在这里了。不出所料,是基于UINavigationController的。
WXBaseViewController *vc = [[WXBaseViewController alloc] initWithSourceURL:[NSURL URLWithString:param[@"url"]]]; vc.hidesBottomBarWhenPushed = YES; [container.navigationController pushViewController:vc animated:animated]; [self callback:block code:MSG_SUCCESS data:nil];
按照weex的设计原则,主视图会附加在一个UIViewController上。由这个UIViewController控制页面的加载和展示。承载weex页面的控制器,必须包含在一个UINavigationController中,否则导航无效。
Weex提供了基础的容器控制器类WXBaseViewController。在初始化时提供javascript代码的地址,它会从这个地址获取代码并展示页面。WXNavigatorModule默认使用WXBaseViewController来展示新的页面。我们可能需要对导航进行定制,或者用一个我们自己实现的控制器代替官方版本。只需要两步就可以做到:
实现自己的weex容器控制器。
实现自己的WXNavigationProtocol协议类,替换官方版本。
我实现了WXViewController。这里偷个懒,直接继承官方的。我在新控制器中加入了自动刷新逻辑。你会好奇我为什么不调用super的viewDidLoad方法?这是因为父类的实现中会隐藏导航栏(而且还有动画),我不想要这样的效果,也不明白这么设计的作用是什么。于是就通过子类覆盖了这个逻辑。
@interface WXViewController (Private) @property (nonatomic, strong) NSURL *sourceURL; - (void)_renderWithURL:(NSURL *)sourceURL; @end @interface WXViewController ()@property (nonatomic, strong) SRWebSocket *hotReloadSocket; @end @implementation WXViewController - (void)dealloc { #if DEBUG [self.hotReloadSocket close]; #endif } - (void)viewDidLoad { void (*viewDidLoad)(id, SEL) = (void (*)(id, SEL))class_getMethodImplementation([UIViewController class], @selector(viewDidLoad)); viewDidLoad(self, @selector(viewDidLoad)); self.view.backgroundColor = [UIColor whiteColor]; self.automaticallyAdjustsScrollViewInsets = NO; [self _renderWithURL:self.sourceURL]; #if DEBUG NSString *hotReloadURL = @"ws://127.0.0.1:8082"; if (hotReloadURL){ _hotReloadSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:hotReloadURL]]; _hotReloadSocket.delegate = self; [_hotReloadSocket open]; } #endif } - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { if ([@"refresh" isEqualToString:message]) { [self refreshWeex]; } } @end
接下来就是WXNavigationImpl了。我只需要修改一个方法,于是同样选择了继承WXNavigationDefaultImpl。这个类的头文件没有公开怎么办?拷贝WXNavigationDefaultImpl.h到自己的项目就行啦。下面是我的实现,实际上只替换了容器控制器,其他代码不变。
@interface WXNavigationImpl (Private) - (void)callback:(WXNavigationResultBlock)block code:(NSString *)code data:(NSDictionary *)reposonData; @end @implementation WXNavigationImpl - (void)pushViewControllerWithParam:(NSDictionary *)param completion:(WXNavigationResultBlock)block withContainer:(UIViewController *)container { if (0 == [param count] || !param[@"url"] || !container) { [self callback:block code:MSG_PARAM_ERR data:nil]; return; } BOOL animated = YES; NSString *obj = [[param objectForKey:@"animated"] lowercaseString]; if (obj && [obj isEqualToString:@"false"]) { animated = NO; } WXViewController *vc = [[WXViewController alloc]initWithSourceURL:[NSURL URLWithString:param[@"url"]]]; vc.hidesBottomBarWhenPushed = YES; [container.navigationController pushViewController:vc animated:animated]; [self callback:block code:MSG_SUCCESS data:nil]; } @end
然后,在[WXSDKEngine initSDKEnvironment]调用后替换默认的handler:
[WXSDKEngine registerHandler:[WXNavigationImpl new] withProtocol:@protocol(WXNavigationProtocol)];
设置根视图控制器,这里我们使用了UINavigationController保证导航能够被支持。
NSURL *url = [NSURL URLWithString:@"http://127.0.0.1:8081/index.weex.js"]; UIViewController *demo = [[WXViewController alloc] initWithSourceURL:url]; [[UIApplication sharedApplication] delegate].window.rootViewController = [[UINavigationController alloc] initWithRootViewController:demo];
在index.vue代码中,添加一个按钮到屏幕中间,然后绑定一个点击事件。这里为了演示的目的,我们选择跳转到同样的地址。
onclick: function (e) { const navigator = weex.requireModule("navigator") navigator.push({ url: "http://127.0.0.1:8081/index.weex.js" }) }
我们启动打包服务器weex preview index.vue,编译运行iOS项目试一下,完美!
可是我的手又痒了,想要在导航栏上添加一个按钮。我在WXNavigatorModule中找到了方法定义:- (void)setNavBarRightItem:(NSDictionary *)param callback:(WXModuleCallback)callback。不过需要传一个字(对)典(象),没有文档只好翻源码了。源码比较简单这里就不解释了。因为界面一开始展示的时候就需要显示导航按钮,我们使用beforeCreate生命周期方法。
beforeCreate: function () { navigator.setNavBarRightItem({ title: "fun", // 编程很有乐趣 titleColor: "blue" // 不设置就是透明的看不见 }) }
按钮可以显示,点击事件怎么解决呢?在网上搜了一下一无所获,还是啃代码自力更生吧。导航按钮的点击事件绑定到了WXNavigationDefaultImpl的- (void)onClickBarButton:(id)sender方法。最核心的一行代码,是触发了一个事件。
[[WXSDKManager bridgeMgr] fireEvent:button.instanceId ref:WX_SDK_ROOT_REF type:eventType params:nil domChanges:nil];
看到fireEvent方法有些懵,这里我解释一下前三个参数:
instanceId:页面的ID。避免给一个控制器的事件跑到另一个控制器。我猜测weex组件的ID只能保证在一个页面中唯一。
ref:组件的ID,根据命名判断为根组件(实际情况也是如此)。
eventType:事件类型,这里为clickrightitem。
既然事件给了根组件,我们只需要把点击事件绑定到根组件就可以啦。至于点击触发什么效果随意啦。
...最后效果是这样的。PS:导航按钮fun会在页面切换动画完毕才显示出来,暂时忍了吧。
这里我们提一下weex的“兄弟”React Native。在导航方面,它们的差异很大。
weex:基于UINavigationController,更贴近原生。缺陷是传参只能字符串,调试还需多终端。
React Native:基于主视图,切换动画由js管理。缺陷是用户体验不一致,埋点还需小心思。
各有千秋吧。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/16955.html
摘要:我们参考小程序的设计思路进行了优化升级,为每一个需要特有化配置的页面添加一个格式的配置文件,配置文件包括导航栏的配置页面级别的配置跳转的配置等,将配置工程化标准化。设置导航栏按钮包含按钮样式的数组通过完成按钮事件的回调。一、背景1.为什么是Weex在公司快速发展的大环境下,App的更新迭代高速、高频,技术团队平均两周便可诞生一款中型App,但App团队只有6个人(iOS 、Android各3...
摘要:使用值来作路由。原生应用本身就是多页的场景,页面间状态的隔离比共享更重要一些。使用开发的是原生应用,页面栈的管理使用的也是原生的特性,没有但是有模块可以实现页面的前进和后退等操作。 系列文章的目录在 ? 这里 (由于 我比较懒 最近一段时间在忙其他事,系列文章拖了好久终于又更新了。。。) 什么是 vue-router ? vue-router 官方文档 vue-router 是针对 V...
摘要:系列文章的目录在这里是一个使用开发的原生应用项目,可以实现同一份代码在三端中运行。项目使用了管理依赖,在启动项目之前应该配置好命令,然后进入目录执行以下脚本安装依赖项目目录和目录中存放着各自平台的原生项目,页面源码都在目录中。 系列文章的目录在 ? 这里 weex-hackernews 是一个使用 Weex + Vue 开发的原生应用项目,可以实现同一份代码在三端中运行。不仅用到了 W...
摘要:首先了解一下淘宝双促中的长列表模块,也就是类型的模块。在模块渲染数据时,会将数据发送给页面的,判断这是一组数据时进行真实数据的渲染小结第一次接受淘宝双促的考验,在化性能稳定性等多个方面表现优异,达成了预定的目标。 showImg(https://segmentfault.com/img/remote/1460000009614810); Rax is a universal JavaS...
摘要:首先了解一下淘宝双促中的长列表模块,也就是类型的模块。在模块渲染数据时,会将数据发送给页面的,判断这是一组数据时进行真实数据的渲染小结第一次接受淘宝双促的考验,在化性能稳定性等多个方面表现优异,达成了预定的目标。 showImg(https://segmentfault.com/img/remote/1460000009614810); Rax is a universal JavaS...
阅读 3211·2021-10-11 10:58
阅读 1783·2019-08-30 15:54
阅读 3483·2019-08-29 13:07
阅读 3016·2019-08-29 12:39
阅读 1665·2019-08-26 12:13
阅读 1397·2019-08-23 18:31
阅读 2058·2019-08-23 18:05
阅读 1724·2019-08-23 18:00