When scrolling in a Menu - Picker, releasing the tap mid-scroll causes it to keep jumping to the top

46 Views Asked by At
struct PlainTextPicker<T: Hashable>: View
{
    @Binding var selection: T?
    let defaultString: String?
    var options: [T]
    var toLabel: (T) -> String

    // body
    Menu
    { 
        Picker("Select", selection: $selection)
        {
            if let defaultString = defaultString
            {
                 Text(defaultString).tag(nil as T?)
            }
                
            ForEach(options, id:\.self)
            {   option in
                Text(toLabel(option)).tag(option as T?)
            }
        } label: 
        {
            HStack
            {
                Text(getSelectionText())
            }
        }
    }
}
PlainTextPicker(selection: $selectedStall, 
                options: ItemsInformation.shared.getItems(customer, category: category),
                defaultString: "Please Select Item",
                toLabel: {"\($0.code) \($0.name)"})

In SwiftUI / iOS 16.4+, I'm using a Picker as a label in a Menu as shown in the above code. In a specific view, when the total size of the Picker (the area where selections can be made) is larger than the screen and requires scrolling, releasing a tap while scrolling causes the picker to jump to the top.

What I suspect is that redraws are occurring due to changes in specific observable properties. Therefore, I checked for changes in variables that may trigger redraws, such as @State, @ObservedObject, and @StateObject. Additionally, I'm using a Timer, and even when I stop the Timer, the same issue persists. Thus, it seems that the view is being redrawn despite no changes in variables. I'm seeking advice on what situations to be suspicious of.

1

There are 1 best solutions below

0
SeongHyeon LIm On

First and foremost, I want to express my gratitude to those who offered advice.

I am using the following View to formalize the list format.

struct SampleList<T: Identifiable, Content: View>: View
{
    @Binding var datas: [T]
    let attributes: [ListTextAttribute]
    let row: (T) -> Content
    
    var width: CGFloat
    {
        return self.attributes.reduce(0){$0 + $1.width}
    }
    
    init(rows: Binding<[T]>,
         attributes: [ListTextAttribute],
         @ViewBuilder customRow: @escaping ((T) -> Content))
    {
        _datas = rows
        self.attributes = attributes
        self.row = customRow
    }
    
    
    var div: some View
    {
        RoundedRectangle(cornerRadius: 8)
            .frame(height: 2)
    }
    
    
    var body: some View
    {
        VStack
        {
            // header
            HStack(spacing: 0)
            {
                ForEach(self.attributes)
                {   attribute in
                    attribute.getTextRow()
                }
            }
            .frame(width: width, height: 40)
            
            // list
            ScrollView(showsIndicators: false)
            {
                LazyVStack(spacing: 0)
                {
                    ForEach(datas)
                    {   data in
                        
                        row(data)
                        div
                    }
                }
            }
            .frame(width: width)
        }
    }
}

In the given code, I'm unsure of the exact cause, but there seems to be continuous redraws occurring due to the values in the attributes within a specific view.

As a solution, I've refactored the view that utilizes this list into a new Struct: View and invoked it so that redraws only happen within the view corresponding to that list.

If there are any possible reasons that come to mind regarding this issue, I'd appreciate it if you could share them. Thank you

+ I apologize for the unclear question earlier.