简体   繁体   English

如何使用原生 SwiftUI 查看 NativeScript 7

[英]How to use a native SwiftUI View in NativeScript 7

In my NativeScript (Angular) App i use a RadListView to create a list and each element has many different informations to display.在我的 NativeScript (Angular) 应用程序中,我使用 RadListView 创建一个列表,每个元素都有许多不同的信息要显示。 It looks like that看起来像这样

样本对象列表

Because of many hints at Stackoverflow and other sources i reduced the amount of nested layouts (StackLayout, GridLayout, ...) as much as possible to make the RadListView faster.由于 Stackoverflow 和其他来源的许多提示,我尽可能减少了嵌套布局(StackLayout、GridLayout、...)的数量,以使 RadListView 更快。 On Android is the performance by using the list much better as on iOS.在 Android 上,使用列表的性能比在 iOS 上要好得多。 With an iPad Pro (2020) the rendering of the list at scrolling is not smooth.使用 iPad Pro (2020) 时,列表在滚动时的呈现不流畅。 If the user change the orientation of the device the screen is freezing and have black bars at the side or bottom for a moment.如果用户更改设备的方向,屏幕会冻结,并且会在侧面或底部出现片刻黑条。 The time of the freezing depends on the amount of elements to display in each row.冻结的时间取决于每行中显示的元素数量。 The same row layout in a ListView is much faster but not the same as native (SwiftUI) and with missing features like swipe and pull to refresh. ListView 中的相同行布局要快得多,但与原生 (SwiftUI) 不同,并且缺少滑动和拉动刷新等功能。

Sorry for the lyric but i think a little background explains why i try the next step.对不起歌词,但我认为有一点背景可以解释为什么我尝试下一步。

To improve the user experience i make a tiny native test app with SwiftUI and nearly the same row layout.为了改善用户体验,我使用 SwiftUI 和几乎相同的行布局制作了一个小型原生测试应用程序。 The feeling is much better, fast first loading, smooth scrolling and no delay by orientation changes.感觉好多了,首次加载速度快,滚动流畅,没有方向变化的延迟。 My next idea is to create a native component in SwiftUI to show/render each row of the RadListView if possible我的下一个想法是在 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>

or use the List from SwiftUI to show/render the whole list或使用 SwiftUI 中的列表来显示/渲染整个列表

<ActionBar title="Objects"></ActionBar>
<MyNativeSwiftUIListComponent data="items"></MyNativeSwiftUIListComponent>

Looking for docs and examples was difficult.寻找文档和示例很困难。 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). 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.). 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.).

How can i use a SwiftUI View as native component in my {N}app?如何在我的 {N} 应用程序中使用 SwiftUI View 作为本机组件? Have anyone a hint, a link to a tutorial or a link to a public repository for a app/plugin?有没有人提示、教程链接或应用程序/插件的公共存储库链接? Every tiny tip is welcome.欢迎每一个小提示。

You might be able to use Nativescript's placeholder component (more info on that here您也许可以使用 Nativescript 的placeholder组件(更多信息请点击此处

So you would have the Placeholder tag on your template, and use the creatingView event to add the native UIs所以你会在你的模板上有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;
}

After a while i give up with my attempts to use directly SwiftUI in the project ({N}+Angular) and instead i try the <Placeholder> component which @William-Juan suggested.过了一会儿,我放弃了在项目({N}+Angular)中直接使用 SwiftUI 的尝试,而是尝试了@William-Juan 建议的<Placeholder>组件。 But it looks like, that the <Placeholder> not official supported in the Angular flavor - see github issue #283但看起来,Angular 风格中未官方支持<Placeholder> - 请参阅github 问题 #283

To move on, i looked at the samples for NativeScript plugins and build a working solution.继续前进,我查看了 NativeScript 插件的示例并构建了一个可行的解决方案。 If anybody interested the full sample source code are in this repository: https://github.com/teha-at/sample-nativescript-native-ui-component如果有人对完整示例源代码感兴趣,请在此存储库中: https://github.com/teha-at/sample-nativescript-native-ui-component

First, create a class which extends the @nativescript/core/View class and has an item to get the data which will be to display.首先,创建一个 class 扩展@nativescript/core/View class 并有一个项目来获取将要显示的数据。

// object-list-item.d.ts
// [...]
export class ObjectListItem extends View {
    item: ObjectModel;
}

export const itemProperty: Property<ObjectListItem, string>;

Than create a abstract base class which also extends the @nativescript/core/View class and this creates the base for Android and iOS然后创建一个抽象基础 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';

Because i was only looking for a component for iOS the object-list-item.android.ts are very simple:因为我只是在寻找 iOS 的组件,所以object-list-item.android.ts非常简单:

// object-list-item.android.ts
import { ObjectListItemBase } from './object-list-item.common';
export class ObjectListItem extends ObjectListItemBase {}

For iOS there are much more lines, for the complete file content look at the github repo please.对于 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;
        // [...]
    }

}

Add an Angular directive添加 Angular 指令

// object-list-item.directives.ts
@Directive({
    selector: 'ObjectListItem',
})
export class ObjectListItemDirective {
}

export const ObjectListItemDirectives = [ObjectListItemDirective];

At least register the component in an Angular module.至少在 Angular 模块中注册该组件。

// object-list-item.module.ts
// [...]

@NgModule({
    imports: [],
    declarations: [
        ObjectListItemDirectives,
    ],
    schemas: [NO_ERRORS_SCHEMA],
    exports: [
        ObjectListItemDirectives,
    ],
    entryComponents: [],
})
export class ObjectListItemModule {
}

registerElement('ObjectListItem', () => ObjectListItem);

After all this steps call the new component in the template在所有这些步骤之后调用模板中的新组件

<!-- [...] -->
<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>
<!-- [...] -->

All this work is well spent.所有这些工作都花得很好。 The UI is much faster and it feels more like a native app. UI 更快,感觉更像是一个原生应用程序。 At the mean time i build a prototype as a native iOS App in Swift and SwiftUI, of course this pure native app are a little bit more smoother, but at the moment i work with my {N}-App and the native component.同时,我在 Swift 和 SwiftUI 中构建了一个原生 iOS 应用程序原型,当然这个纯原生应用程序更流畅一点,但目前我使用的是我的原生组件。 Hope this sample will be useful for someone.希望这个样本对某人有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM