# 将 Objective-C 代码添加到 NativeScript 应用程序
在使用 NativeScript 5.1 之前,您必须创建一个框架并将其包装在插件中才能使用自定义的 Objective-C / Swift 代码。由于这可能是很多不必要的工作(特别是如果您想添加一个小功能),因此我们试图使将 Objective-C 文件直接添加为应用程序资源成为可能。
# 如何
为了将 Objective-C 文件添加到 NativeScript CLI Xcode 项目中,您需要做三件事:
- 在 App_Resources / iOS 中创建 src 目录
CLI 将查找此文件夹,以便将其内容添加到 Xcode 项目中,并编译和链接源文件。
将源文件添加到 src 文件夹中
创建模块图
该module.modulemap
文件是必需的,以便元数据生成器能够找到声明并将其添加到生成的 AST 中,以便可以从您的 JavaScript 代码访问它们。如果您不熟悉模块映射的概念,那么这里是一个不错的起点。
TIP
注意:当然,您可以将代码组织在单独的目录中,它们会像这样添加到.xcodeproj
文件树中。
# 演示版
对于这个演示中,我选择了一个伟大的教程UIViewPropertyAnimator
由丹尼尔·拉尔森。我使用本机代码教程的原因之一是作为示例,说明如何查找本机实现,然后在 NativeScript 应用程序中使用它们。
现在,如果要在 JavaScript 中实现此功能,则可能需要将大代码块包装在if (platform.ios)-block
中或创建一个插件。我将向您展示的另一种方法是在特定于平台的(本机)代码中添加特定于平台的功能。此外,在某些情况下,它的性能也可能更好-例如,在为视图添加动画时应在每个帧上执行本机调用,因此,每秒不调用该桥 60 次(最佳情况下)。
即使在这种情况下,BTW NativeScript 的表现也非常好:
用 javascript 拖动的示例
让我们从创建我们的NativeAnimator
类开始。在编写 Objective-C 代码时,现在我们需要两个文件。
不要忘记源文件需要驻留在中App_Resources/iOS/src
。大多数时候,您将必须自己创建该src
目录。
NativeAnimator.h
#import <Foundation/Foundation.h>
@interface NativeAnimator : NSObject
-(id)initWithView:(UIView*)view andParent:(UIView*)parent;
-(void)setup;
@end
我们希望对实现尽可能隐藏,这就是为什么这些是唯一NativeAnimator
公开的方法的原因。我更喜欢有一个附加的方法来附加panGestureRecognizer
,以使初始化程序仅对应有的内容负责。
-(id)initWithView:(UIView*)view andParent:(UIView*)parent {
self = [super init];
if (self) {
self.playerView = view;
self.parentView = parent;
}
return self;
}
在view
和parent
参数,我们会从我们的 JavaScript 代码通过的意见。在NativeAnimator
需要保持引用它们,以完成其工作。现在我们准备创建我们的panGestureRecognizer
:
-(void)setup {
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.parentView addGestureRecognizer:self.panGestureRecognizer];
}
这是实现的结尾:
NativeAnimator.m
#import "NativeAnimator.h"
typedef NS_ENUM(NSInteger, PlayerState) {
PlayerStateThumbnail,
PlayerStateFullscreen,
};
@interface NativeAnimator ()
@property (weak, nonatomic) UIView *parentView;
@property (weak, nonatomic) UIView *playerView;
@property (nonatomic) UIViewPropertyAnimator *playerViewAnimator;
@property (nonatomic) CGRect originalPlayerViewFrame;
@property (nonatomic) PlayerState playerState;
@property (nonatomic) UIPanGestureRecognizer *panGestureRecognizer;
@property (nonatomic) UIView* b;
@end
@implementation NativeAnimator
-(id)initWithView:(UIView*)view andParent:(UIView*)parent {
self = [super init];
if (self) {
self.playerView = view;
self.parentView = parent;
}
return self;
}
-(void)setup {
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.parentView addGestureRecognizer:self.panGestureRecognizer];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.parentView.superview];
if (recognizer.state == UIGestureRecognizerStateBegan)
{
[self panningBegan];
}
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint velocity = [recognizer velocityInView:self.parentView];
[self panningEndedWithTranslation:translation velocity:velocity];
}
else
{
CGPoint translation = [recognizer translationInView:self.parentView.superview];
[self panningChangedWithTranslation:translation];
}
}
- (void)panningBegan
{
if (self.playerViewAnimator.isRunning)
{
return;
}
CGRect targetFrame;
switch (self.playerState) {
case PlayerStateThumbnail:
self.originalPlayerViewFrame = self.playerView.frame;
targetFrame = self.parentView.frame;
break;
case PlayerStateFullscreen:
targetFrame = self.originalPlayerViewFrame;
}
self.playerViewAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:0.5 dampingRatio:0.8 animations:^{
self.playerView.frame = targetFrame;
}];
}
- (void)panningChangedWithTranslation:(CGPoint)translation
{
if (self.playerViewAnimator.isRunning)
{
return;
}
CGFloat translatedY = self.parentView.center.y + translation.y;
CGFloat progress;
switch (self.playerState) {
case PlayerStateThumbnail:
progress = 1 - (translatedY / self.parentView.center.y);
break;
case PlayerStateFullscreen:
progress = (translatedY / self.parentView.center.y) - 1;
}
progress = MAX(0.001, MIN(0.999, progress));
self.playerViewAnimator.fractionComplete = progress;
}
- (void)panningEndedWithTranslation:(CGPoint)translation velocity:(CGPoint)velocity
{
self.panGestureRecognizer.enabled = NO;
CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
__weak NativeAnimator *weakSelf = self;
switch (self.playerState) {
case PlayerStateThumbnail:
if (translation.y <= -screenHeight / 3 || velocity.y <= -100)
{
self.playerViewAnimator.reversed = NO;
[self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
weakSelf.playerState = PlayerStateFullscreen;
weakSelf.panGestureRecognizer.enabled = YES;
}];
}
else
{
self.playerViewAnimator.reversed = YES;
[self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
weakSelf.playerState = PlayerStateThumbnail;
weakSelf.panGestureRecognizer.enabled = YES;
}];
}
break;
case PlayerStateFullscreen:
if (translation.y >= screenHeight / 3 || velocity.y >= 100)
{
self.playerViewAnimator.reversed = NO;
[self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
weakSelf.playerState = PlayerStateThumbnail;
weakSelf.panGestureRecognizer.enabled = YES;
}];
}
else
{
self.playerViewAnimator.reversed = YES;
[self.playerViewAnimator addCompletion:^(UIViewAnimatingPosition finalPosition) {
weakSelf.playerState = PlayerStateFullscreen;
weakSelf.panGestureRecognizer.enabled = YES;
}];
}
}
CGVector velocityVector = CGVectorMake(velocity.x / 100, velocity.y / 100);
UISpringTimingParameters *springParameters = [[UISpringTimingParameters alloc] initWithDampingRatio:0.8 initialVelocity:velocityVector];
[self.playerViewAnimator continueAnimationWithTimingParameters:springParameters durationFactor:1.0];
}
@end
唯一缺少的文件是modulemap
。这里是:
module.modulemap
module NativeAnimator {
header "NativeAnimator.h"
export *
}
如果运行tns prepare ios
,则NativeAnimator
文件将成为项目的一部分。做得好!
为了查看我们新创建的NativeAnimator
实际工作,我们需要创建将要使用的视图。
我认为您已经创建了一个香草 js Hello World 应用程序!
onNavigatingTo
函数中的一个好地方是函数,因为我们可能在UIViewController
那里找到本地人。
function onNavigatingTo(args) {
const page = args.object;
page.ios.playerView = UIView.alloc().initWithFrame(CGRectMake(100, 500, 100, 100));
page.ios.playerView.backgroundColor = UIColor.blackColor;
page.ios.view.addSubview(page.ios.playerView);
page.ios.animator = NativeAnimator.alloc().initWithViewAndParent(page.ios.playerView, page.ios.view);
page.ios.animator.setup();
...
}
结果如下:
使用本机代码拖动的示例
# 摘要
将 Objective-C 源代码直接添加到 NativeScript 应用程序的可能性消除了每次需要创建 Objective-C 类并从 JavaScript 访问它们时创建插件的麻烦。当然,当您要抽象一些逻辑并使其可重用时,创建插件仍然是最佳选择,因此请明智地选择。