ReordableViews icon indicating copy to clipboard operation
ReordableViews copied to clipboard

Lag when loading images

Open ltns35 opened this issue 2 years ago • 5 comments

Displaying more than 10 images in a grid and start dragging it produces lag and animation issues.

ltns35 avatar Jun 26 '23 13:06 ltns35

@ltns35 Can you put here some minimal code that reproduces the issue?

gadirom avatar Jun 26 '23 14:06 gadirom

The complete source code is a little bit complex, but what I'm doing is loading images from photosPicker and display them after being processed as UIImage.

ReordableVGrid(items: $vm.selectedItems,
                       activeItem: $mediaReorder,
                       maxItems: maxSelectionCount,
                       columns: gridColumns,
                       alignment: .center,
                       spacing: spacing,
                       moveAnimation: moveAnimation,
                       selectAnimation: selectAnimation,
                       reorderDelay: 0.3,
                       id: \.self,
                       addButton: EmptyView()) {
           ForEach {
                // Display images
           }
}
.photosPicker(isPresented: $pickerShown,
                      selection: $vm.selectedItems,
                      maxSelectionCount: maxSelectionCount,
                      matching: .images,
                      photoLibrary: .shared())

ltns35 avatar Jun 26 '23 14:06 ltns35

From what I could guess from your code, it seems that the refreshing of the views in the grid that happen during the dragging triggers some expensive image loading operations. It would be easier for me to try to look into that if I had some minimal working code example that reproduces the issue. As a test try for example displaying already loaded images with Image() and see if the lagging goes.

gadirom avatar Jun 26 '23 15:06 gadirom

I think I found the issue, I've made the following test and I perceived the problem is related to the ScrollView, when I add it it's when the lag appears, if no ScrollView is present the dragging is smooth.

import SwiftUI
import PhotosUI
import ReordableViews

struct DemoView: View {

    private let moveAnimation: Animation = .spring(response: 0.5, dampingFraction: 0.7, blendDuration: 0)
    private let selectAnimation: Animation = .spring(response: 0.1, dampingFraction: 0.7, blendDuration: 0)

    @State
    private var images: [UIImage] = []

    @State
    private var selectedItems: [PhotosPickerItem] = []

    @State
    private var currentDraggedItem: UIImage? = nil

    @State
    private var pickerVisible = false

    var body: some View {
        ScrollView {
            VStack {
                Button("Select images") {
                    pickerVisible = true
                }

                ReordableVGrid(items: $images,
                               activeItem: $currentDraggedItem,
                               maxItems: 20,
                               columns: [.flexible(), .flexible(), .flexible()],
                               alignment: .center,
                               spacing: 20,
                               moveAnimation: moveAnimation,
                               selectAnimation: selectAnimation,
                               reorderDelay: 0.3,
                               id: \.self,
                               addButton: EmptyView()) { item,_,_,_,_,_,_ in
                    Image(uiImage: item.wrappedValue)
                        .resizable()
                        .width(.infinity)
                        .aspectRatio(1, contentMode: .fit)
                } orderChanged: { _,_ in
                }
            }
            .photosPicker(isPresented: $pickerVisible,
                          selection: $selectedItems,
                          maxSelectionCount: 20,
                          matching: .images,
                          photoLibrary: .shared())
            .onChange(of: selectedItems) { items in
                images.removeAll()

                items.forEach { item in
                    Task {
                        if let data = try? await item.loadTransferable(type: Data.self) {
                            if let image = UIImage(data: data) {
                                images.append(image)
                            }
                        }
                    }
                }
            }
        }
    }
}

ltns35 avatar Jun 26 '23 16:06 ltns35

Ha! I never thought to test it with a ScrollView, just because I didn’t know how to handle the scrolling. Turns out it somehow messes with the algorithm. My guess is it’s forcing GeometryReader to run too frequently.

Any way, I will try to look into this issue, but I’m not too optimistic.

For now my advice is to avoid ScrollView.

When I thought of using this package in a real application I had an idea to make several pages, but I never solved the problem of drugging an element outside the grid to place it to another page. So I opted for a UIKit solution.

gadirom avatar Jun 26 '23 19:06 gadirom