跳到主要内容
版本:0.83

Fabric 原生组件:iOS

现在是时候编写一些 iOS 平台代码以渲染 Web 视图了。你需要遵循以下步骤:

  • 运行 Codegen。
  • 编写 RCTWebView 的代码。
  • 在程序中注册 RCTWebView

1. 运行 Codegen

你可以 手动运行 Codegen,但更简单的方法是使用你打算演示组件的应用程序来帮你运行。

cd ios
bundle install
bundle exec pod install

重要的是你会看到 Codegen 的日志输出,我们稍后会在 Xcode 中使用它来构建我们的 WebView 原生组件。

注意

你在提交生成代码到仓库时需要小心。生成代码是针对每个 React Native 版本特有的。请使用 npm 的 peerDependencies 来限制与 React Native 版本的兼容性。

3. 编写 RCTWebView

我们需要通过完成以下 5 个步骤 使用 Xcode 准备你的 iOS 项目:

  1. 打开 CocoaPods 生成的 Xcode Workspace:
cd ios
open Demo.xcworkspace
打开 Xcode Workspace
  1. 右击 app,然后选择 New Group,将新组命名为 WebView
右击 app 并选择 New Group
  1. WebView 组内,创建 NewFile from Template
使用 Cocoa Touch Class 模板创建新文件
  1. 选择 Objective-C File 模板,并命名为 RCTWebView
创建 Objective-C RCTWebView 类
  1. 重复步骤 4,创建一个名为 RCTWebView.h 的头文件。

  2. RCTWebView.m 重命名为 RCTWebView.mm,使其成为 Objective-C++ 文件。

Demo/ios
Podfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm

创建完头文件和实现文件后,即可开始编写实现代码。

下面是 RCTWebView.h 文件的代码,声明了组件接口。

Demo/RCTWebView/RCTWebView.h
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface RCTWebView : RCTViewComponentView

// 这里声明你希望从视图访问的原生方法

@end

NS_ASSUME_NONNULL_END

该类定义了一个继承自 RCTViewComponentViewRCTWebViewRCTViewComponentView 是所有原生组件的基类,由 React Native 提供。

以下是实现文件 (RCTWebView.mm) 的代码:

Demo/RCTWebView/RCTWebView.mm
#import "RCTWebView.h"

#import <react/renderer/components/AppSpec/ComponentDescriptors.h>
#import <react/renderer/components/AppSpec/EventEmitters.h>
#import <react/renderer/components/AppSpec/Props.h>
#import <react/renderer/components/AppSpec/RCTComponentViewHelpers.h>
#import <WebKit/WebKit.h>

using namespace facebook::react;

@interface RCTWebView () <RCTCustomWebViewViewProtocol, WKNavigationDelegate>
@end

@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}

-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}

- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast<CustomWebViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<CustomWebViewProps const>(props);

// 在这里处理你的属性
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}

[super updateProps:props oldProps:oldProps];
}

-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;

}

#pragma mark - WKNavigationDelegate

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}

- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};

self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}

// 事件触发器便捷方法
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast<const CustomWebViewEventEmitter &>(*_eventEmitter);
}

+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>();
}

@end

这段代码使用 Objective-C++ 编写,包含多个细节:

  • @interface 实现了两个协议:
    • 由 Codegen 生成的 RCTCustomWebViewViewProtocol
    • 由 WebKit 框架提供的 WKNavigationDelegate,用于处理 WebView 导航事件;
  • init 方法实例化 WKWebView,添加为子视图并设置 navigationDelegate
  • updateProps 方法由 React Native 在组件属性变化时调用;
  • layoutSubviews 方法描述自定义视图的布局方式;
  • webView:didFinishNavigation: 方法处理 WKWebView 页面加载完成时的操作;
  • urlIsValid:(std::string)propString 方法检查传入属性的 URL 是否有效;
  • eventEmitter 方法用于获取强类型的事件触发器实例;
  • componentDescriptorProvider 返回由 Codegen 生成的 ComponentDescriptor

添加 WebKit 框架

备注

这一步只有在我们创建 Web 视图时需要。iOS 上的 Web 组件需要链接 Apple 提供的 WebKit 框架。如果你的组件不需要访问特定 Web 功能,可以跳过此步骤。

Web 视图需要访问 Apple 通过 Xcode 和设备带来的框架之一 —— WebKit。你可以在原生代码中看到引入了 <WebKit/WebKit.h>

要在你的应用中链接 WebKit 框架,请按下列步骤操作:

  1. 在 Xcode 中点击你的项目。
  2. 选择应用目标(app target)。
  3. 选择 General 选项卡。
  4. 向下滚动,找到 "Frameworks, Libraries, and Embedded Contents" 区域,然后点击 + 按钮。
给你的应用添加 WebKit 框架 1
  1. 在搜索栏中输入 WebKit 进行筛选。
  2. 选择 WebKit 框架。
  3. 点击添加。
给你的应用添加 WebKit 框架 2