In my NativeScript (Angular) App i use a RadListView to create a list and each element has many different informations to display. 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. On Android is the performance by using the list much better as on iOS. With an iPad Pro (2020) the rendering of the list at scrolling is not smooth. 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.
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. 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
<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
<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). 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? 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.
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. But it looks like, that the <Placeholder>
not official supported in the Angular flavor - see github issue #283
To move on, i looked at the samples for NativeScript plugins and build a working solution. If anybody interested the full sample source code are in this repository: 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.
// 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
// 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:
// 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.
/// 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
// object-list-item.directives.ts
@Directive({
selector: 'ObjectListItem',
})
export class ObjectListItemDirective {
}
export const ObjectListItemDirectives = [ObjectListItemDirective];
At least register the component in an Angular module.
// 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. 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. Hope this sample will be useful for someone.