[英]How to use a native SwiftUI View in NativeScript 7
在我的 NativeScript (Angular) 应用程序中,我使用 RadListView 创建一个列表,每个元素都有许多不同的信息要显示。 看起来像这样
由于 Stackoverflow 和其他来源的许多提示,我尽可能减少了嵌套布局(StackLayout、GridLayout、...)的数量,以使 RadListView 更快。 在 Android 上,使用列表的性能比在 iOS 上要好得多。 使用 iPad Pro (2020) 时,列表在滚动时的呈现不流畅。 如果用户更改设备的方向,屏幕会冻结,并且会在侧面或底部出现片刻黑条。 冻结的时间取决于每行中显示的元素数量。 ListView 中的相同行布局要快得多,但与原生 (SwiftUI) 不同,并且缺少滑动和拉动刷新等功能。
对不起歌词,但我认为有一点背景可以解释为什么我尝试下一步。
为了改善用户体验,我使用 SwiftUI 和几乎相同的行布局制作了一个小型原生测试应用程序。 感觉好多了,首次加载速度快,滚动流畅,没有方向变化的延迟。 我的下一个想法是在 SwiftUI 中创建一个本机组件,以尽可能显示/渲染 RadListView 的每一行
<RadListView [items]="items">
<ListViewLinearLayout tkListViewLayout></ListViewLinearLayout>
<ng-template tkListItemTemplate let-item="item" let-i="index" let-odd="odd">
<MyNativeSwiftUIComponentElement data="item.rowData"></MyNativeSwiftUIComponentElement>
</ng-template>
</RadListView>
或使用 SwiftUI 中的列表来显示/渲染整个列表
<ActionBar title="Objects"></ActionBar>
<MyNativeSwiftUIListComponent data="items"></MyNativeSwiftUIListComponent>
寻找文档和示例很困难。 I found this very short advise Adding Objective-C/Swift code and the linked tutorial there for Objective-C ( Adding Objective-C Code to a NativeScript App ) and some questions on Stackoverflow but there all about classes and not SwiftUI (with struct and views). One question was about SwiftUI: Is it possible to display a View written with SwiftUI with NativeScript the answer was unfortunately not helpful for me (btw. thank you @Manoj for your great support for NativeScript at Stackoverflow.).
如何在我的 {N} 应用程序中使用 SwiftUI View 作为本机组件? 有没有人提示、教程链接或应用程序/插件的公共存储库链接? 欢迎每一个小提示。
您也许可以使用 Nativescript 的placeholder
组件(更多信息请点击此处
所以你会在你的模板上有Placeholder
标签,并使用creatingView
事件来添加本机 UI
<Placeholder creatingView="creatingView"/>
import { CreateViewEventData } from "@nativescript/core";
export function creatingView(args: CreateViewEventData) {
let nativeView = new UILabel(); // where this would be your native UI
nativeView.text = "Native";
args.view = nativeView;
}
过了一会儿,我放弃了在项目({N}+Angular)中直接使用 SwiftUI 的尝试,而是尝试了@William-Juan 建议的<Placeholder>
组件。 但看起来,Angular 风格中未官方支持<Placeholder>
- 请参阅github 问题 #283
继续前进,我查看了 NativeScript 插件的示例并构建了一个可行的解决方案。 如果有人对完整示例源代码感兴趣,请在此存储库中: https://github.com/teha-at/sample-nativescript-native-ui-component
首先,创建一个 class 扩展@nativescript/core/View
class 并有一个项目来获取将要显示的数据。
// object-list-item.d.ts
// [...]
export class ObjectListItem extends View {
item: ObjectModel;
}
export const itemProperty: Property<ObjectListItem, string>;
然后创建一个抽象基础 class ,它还扩展了@nativescript/core/View
class ,这为 Android 和 Z1BZDF155991920CDB1DZ 创建了基础
// object-list-item.common.ts
// [...]
export const itemProperty = new Property<ObjectListItemBase, string>({
name: 'item',
defaultValue: null,
affectsLayout: isIOS,
});
export abstract class ObjectListItemBase extends View {
item: PortalObjectModel;
}
// defines 'item' property on the ObjectListItemBase class
itemProperty.register(ObjectListItemBase);
ObjectListItemBase.prototype.recycleNativeView = 'auto';
因为我只是在寻找 iOS 的组件,所以object-list-item.android.ts
非常简单:
// object-list-item.android.ts
import { ObjectListItemBase } from './object-list-item.common';
export class ObjectListItem extends ObjectListItemBase {}
对于 iOS 有更多的行,完整的文件内容请查看github repo 。
/// object-list-item.ios.ts
// [...]
export class ObjectListItem extends ObjectListItemBase {
// added for TypeScript intellisense.
nativeView: UIView;
// [...]
/**
* Creates new native button.
*/
public createNativeView(): Object {
const mainUiStackView = UIStackView.new();
// [...]
}
/**
* Initializes properties/listeners of the native view.
*/
initNativeView(): void {
// Attach the owner to nativeView.
// When nativeView is tapped we get the owning JS object through this field.
(<any>this.nativeView).owner = this;
super.initNativeView();
}
/**
* Clean up references to the native view and resets nativeView to its original state.
* If you have changed nativeView in some other way except through setNative callbacks
* you have a chance here to revert it back to its original state
* so that it could be reused later.
*/
disposeNativeView(): void {
// Remove reference from native listener to this instance.
(<any>this.nativeView).owner = null;
// If you want to recycle nativeView and have modified the nativeView
// without using Property or CssProperty (e.g. outside our property system - 'setNative' callbacks)
// you have to reset it to its initial state here.
super.disposeNativeView();
}
[itemProperty.setNative](item: ObjectModel) {
this.item = item;
// [...]
}
}
添加 Angular 指令
// object-list-item.directives.ts
@Directive({
selector: 'ObjectListItem',
})
export class ObjectListItemDirective {
}
export const ObjectListItemDirectives = [ObjectListItemDirective];
至少在 Angular 模块中注册该组件。
// object-list-item.module.ts
// [...]
@NgModule({
imports: [],
declarations: [
ObjectListItemDirectives,
],
schemas: [NO_ERRORS_SCHEMA],
exports: [
ObjectListItemDirectives,
],
entryComponents: [],
})
export class ObjectListItemModule {
}
registerElement('ObjectListItem', () => ObjectListItem);
在所有这些步骤之后调用模板中的新组件
<!-- [...] -->
<RadListView #myListView [items]="items$ | async">
<ng-template tkListItemTemplate let-item="item">
<StackLayout margin="0" padding="0" class="-separator m-y-5" height="90">
<android>
<!-- [...] -->
</android>
<ios>
<ObjectListItem [item]="item"></ObjectListItem>
</ios>
</StackLayout>
</ng-template>
</RadListView>
<!-- [...] -->
所有这些工作都花得很好。 UI 更快,感觉更像是一个原生应用程序。 同时,我在 Swift 和 SwiftUI 中构建了一个原生 iOS 应用程序原型,当然这个纯原生应用程序更流畅一点,但目前我使用的是我的原生组件。 希望这个样本对某人有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.