NativeScript icon indicating copy to clipboard operation
NativeScript copied to clipboard

NS 8.2.0+ iOS Base64 images disappearing after scrolling ListView/RadListView

Open felixkrautschuk opened this issue 4 years ago • 6 comments

Issue Description

When upgrading @nativescript/core from 8.1.5 to latest 8.2.1 (or 8.2.0), I notice that images inside ListView/RadListView are disappearing after scrolling when using base64 ImageSources on iOS. They appear again when removing the items from ObservableArray and add them again.

<ListView items="{{ items }}">
            <ListView.itemTemplate>
                <GridLayout columns="auto,20,*>
                    <Image col="0" src="{{ imageSrc }}" className="image-list"/>
                    <Label col="2" text="{{ title }}" verticalAlignment="center"/>
                </GridLayout>
            </ListView.itemTemplate>
</ListView>
export const vm = fromObject({
  items: new ObservableArray([
      {title: "Item 1", imageSrc: ImageSource.fromBase64Sync("/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZg...")},
      {title: "Item 2", imageSrc: ImageSource.fromBase64Sync("/9j/4AAQSkZJRgABAQEBLAEsAAD/7QJoUGhvdG...")},
      {title: "Item 3", imageSrc: null},
      //...
  ])
});

The output: https://user-images.githubusercontent.com/6443021/157667068-8effbe14-f17d-4360-ac4b-f8d1f2157712.mov

With nativescript/core 8.1.5, it was working as expected:

https://user-images.githubusercontent.com/6443021/157667249-c57d8be4-3b16-4e3f-9238-6efe862a557c.mov

Reproduction

Sample app to reproduce: ns-8-2-ios-image.zip

  • run the app on ios
  • tap the first button to navigate to the page with RadListView and base64 images
  • scroll the list down and up again -> images disappearing

Relevant log output (if applicable)

-

Environment

OS: macOS 12.2.1
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Shell: /bin/zsh
node: 14.18.1
npm: 6.14.15
nativescript: 8.1.5

# android
java: 11.0.11
ndk: Not Found
apis: Not Found
build_tools: Not Found
system_images: Not Found

# ios
xcode: 13.2.1/13C100
cocoapods: 1.11.2
python: 2.7.18
python3: 3.9.7
ruby: 2.7.4
platforms: 
  - DriverKit 21.2
  - iOS 15.2
  - macOS 12.1
  - tvOS 15.2
  - watchOS 8.3

Dependencies

"dependencies": {
  "@nativescript/core": "^8.2.1",
  "nativescript-ui-listview": "^10.0.2"
},
"devDependencies": {
  "@nativescript/android": "^8.2.2",
  "@nativescript/ios": "^8.2.1",
  "@nativescript/types": "~8.2.0",
  "@nativescript/webpack": "~5.0.6",
  "typescript": "~4.5.5"
}

Please accept these terms

felixkrautschuk avatar Mar 10 '22 13:03 felixkrautschuk

I just noticed that it may happen on Android as well, but not in the same dimenson like iOS.

https://user-images.githubusercontent.com/6443021/157669994-86a5e0dc-623d-4142-96d2-41f10c8ce910.mov

But I noticed that on ANdroid it may happen with nativescript/core 8.1.5 as well

felixkrautschuk avatar Mar 10 '22 13:03 felixkrautschuk

@felixkrautschuk I think there had been some work in 8.2 regarding iOS UIImage memory leaks and clean so perhaps it could be related to this? As a temporary solution, I think you could use callbacks that return image sources. For example:

export const vm = fromObject({
  items: new ObservableArray([
      {title: "Item 1", getImageSrc: () => ImageSource.fromBase64Sync("/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZg...")},
      {title: "Item 2", getImageSrc: () => ImageSource.fromBase64Sync("/9j/4AAQSkZJRgABAQEBLAEsAAD/7QJoUGhvdG...")},
      {title: "Item 3", getImageSrc: () => null},
      //...
  ])
});
<ListView items="{{ items }}">
            <ListView.itemTemplate>
                <GridLayout columns="auto,20,*>
                    <Image col="0" src="{{ getImageSrc() }}" className="image-list"/>
                    <Label col="2" text="{{ title }}" verticalAlignment="center"/>
                </GridLayout>
            </ListView.itemTemplate>
</ListView>

Hope it works.

CatchABus avatar Mar 15 '22 18:03 CatchABus

@DimitrisRK Thanks for your feedback. I checked that out in the sample app and it seems to work for ListView.

But there are some issues when using RadListView (which is actually our main use-case).

On iOS, the images do not disappear anymore when scrolling, but I see some binding errors:

CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(58)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(61)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(64)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(67)@rlv-page.xml:12:21; ...

On Android, the app is crashing

System.err: Error: The method setNativeSource() expects android.graphics.Bitmap instance. System.err: System.err: StackTrace: System.err: setNativeSource(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/image-source/index.android.js:225:0) System.err: at ImageSource(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/image-source/index.android.js:31:0) System.err: at _createImageSourceFromSrc(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/image/image-common.js:113:0) System.err: at _createImageSourceFromSrc(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/image/index.android.js:110:0) System.err: at [src:setNative](file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/image/index.android.js:166:0) System.err: at applyAllNativeSetters(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/properties/index.js:1136:0) System.err: at initNativeView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/properties/index.js:1079:0) System.err: at onResumeNativeUpdates(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:740:22) System.err: at _resumeNativeUpdates(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:280:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:235:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/view-common.js:107:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/index.android.js:309:0) System.err: at (file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at callFunctionWithSuper(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:312:0) System.err: at callLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at loadView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:458:0) System.err: at (file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:237:0) System.err: at eachChildView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/layouts/layout-base-common.js:101:0) System.err: at eachChild(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/view-common.js:772:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:236:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/view-common.js:107:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/index.android.js:309:0) System.err: at (file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at callFunctionWithSuper(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:312:0) System.err: at callLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at loadView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:458:0) System.err: at (file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:237:0) System.err: at eachChildView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/layouts/layout-base-common.js:101:0) System.err: at eachChild(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/view-common.js:772:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:236:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/view-common.js:107:0) System.err: at onLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view/index.android.js:309:0) System.err: at (file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at callFunctionWithSuper(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:312:0) System.err: at callLoaded(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:318:0) System.err: at loadView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:458:0) System.err: at _addViewCore(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:453:0) System.err: at _addView(file: app/webpack:/ns-8-2-ios-image/node_modules/@nativescript/core/ui/core/view-base/index.js:439:0) System.err: at ListViewAdapter.onCreateViewHolder(file: app/webpack:/ns-8-2-ios-image/node_modules/nativescript-ui-listview/index.android.js:120:0) System.err: at com.tns.Runtime.callJSMethodNative(Native Method) System.err: at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1302) System.err: at com.tns.Runtime.callJSMethodImpl(Runtime.java:1188) System.err: at com.tns.Runtime.callJSMethod(Runtime.java:1175) System.err: at com.tns.Runtime.callJSMethod(Runtime.java:1153) System.err: at com.tns.Runtime.callJSMethod(Runtime.java:1149) System.err: at com.tns.gen.com.telerik.widget.list.ListViewAdapter_vendor_54258_28_ListViewAdapter.onCreateViewHolder(ListViewAdapter_vendor_54258_28_ListViewAdapter.java:41) System.err: at com.telerik.widget.list.ListViewWrapperAdapter.onCreateViewHolder(ListViewWrapperAdapter.java:468) System.err: at com.telerik.widget.list.ListViewWrapperAdapter.onCreateViewHolder(ListViewWrapperAdapter.java:20) System.err: at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7078) System.err: at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235) System.err: at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118) System.err: at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114) System.err: at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303) System.err: at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627) System.err: at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587) System.err: at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665) System.err: at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134) System.err: at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851) System.err: at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404) System.err: at com.telerik.widget.list.RadListView.onLayout(RadListView.java:600) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) System.err: at android.widget.FrameLayout.onLayout(FrameLayout.java:270) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at org.nativescript.widgets.CommonLayoutParams.layoutChild(CommonLayoutParams.java:222) System.err: at org.nativescript.widgets.GridLayout.onLayout(GridLayout.java:351) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at org.nativescript.widgets.CommonLayoutParams.layoutChild(CommonLayoutParams.java:222) System.err: at org.nativescript.widgets.GridLayout.onLayout(GridLayout.java:351) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at org.nativescript.widgets.CommonLayoutParams.layoutChild(CommonLayoutParams.java:222) System.err: at org.nativescript.widgets.ContentLayout.onLayout(ContentLayout.java:73) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) System.err: at android.widget.FrameLayout.onLayout(FrameLayout.java:270) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829) System.err: at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673) System.err: at android.widget.LinearLayout.onLayout(LinearLayout.java:1582) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) System.err: at android.widget.FrameLayout.onLayout(FrameLayout.java:270) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829) System.err: at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673) System.err: at android.widget.LinearLayout.onLayout(LinearLayout.java:1582) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332) System.err: at android.widget.FrameLayout.onLayout(FrameLayout.java:270) System.err: at com.android.internal.policy.DecorView.onLayout(DecorView.java:779) System.err: at android.view.View.layout(View.java:21912) System.err: at android.view.ViewGroup.layout(ViewGroup.java:6260) System.err: at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080) System.err: at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590) System.err: at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721) System.err: at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598) System.err: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966) System.err: at android.view.Choreographer.doCallbacks(Choreographer.java:790) System.err: at android.view.Choreographer.doFrame(Choreographer.java:725) System.err: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951) System.err: at android.os.Handler.handleCallback(Handler.java:883) System.err: at android.os.Handler.dispatchMessage(Handler.java:100) System.err: at android.os.Looper.loop(Looper.java:214) System.err: at android.app.ActivityThread.main(ActivityThread.java:7356) System.err: at java.lang.reflect.Method.invoke(Native Method) System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

felixkrautschuk avatar Mar 23 '22 16:03 felixkrautschuk

@DimitrisRK Thanks for your feedback. I checked that out in the sample app and it seems to work for ListView.

But there are some issues when using RadListView (which is actually our main use-case).

On iOS, the images do not disappear anymore when scrolling, but I see some binding errors:

CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(58)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(61)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(64)@rlv-page.xml:12:21; CONSOLE ERROR: Binding: Error: Cannot perform a call using a null or undefined property at Image(67)@rlv-page.xml:12:21; ...

On Android, the app is crashing

Yes, it seems RadListView takes a little bit to load context for items and call expressions are quite strict in XML. You can make your call null-safe using this syntax:

{{ getImageSrc?.() }}

This will make call return undefined but android may still not like it.

CatchABus avatar Mar 23 '22 17:03 CatchABus

Thanks, that seems to do the trick!

felixkrautschuk avatar Mar 23 '22 17:03 felixkrautschuk

watching

sinhapallav avatar Mar 28 '22 12:03 sinhapallav