iosswiftiphoneswiftuiios18

SwiftUI context menu scroll broken when view changes


I have a view that changes with a timer and I want to be able to long press that view and show a context menu. However the scroll on the context menu seems to be broken?

I am on ios 18 and xcode 16


import SwiftUI

struct ContentView: View {
  @State var number = 0
  @State var timer = Timer.publish(every: 0.25, on: .main, in: .common).autoconnect()
  var body: some View {
    VStack {
      Text(number.formatted())
        .font(.largeTitle)
      Text("Long press me")
      Text("Notice context menu cannot scroll down")
    }
    .padding()
    .background {
      RoundedRectangle(cornerRadius: 10.0)
        .fill(Color(uiColor: .secondarySystemBackground))
    }
    .contextMenu {
      ForEach(0 ..< 100) { index in
        Button {
          print("option \(index)")
        } label: {
          Label("Option \(index)", systemImage: "globe")
        }
      }
    }
    .onReceive(timer) { t in
      number += 1
    }
  }
}

Is this a bug in swiftUI? In my real application I want the context menu to be able to switch the kind of data the user is viewing and all data is changing real time


Solution

  • Whenever State value changes the body of the view is called to update the views.

    Since your code is a single view the init of every view is called and compared.(All the buttons in context)

    You can move the buttons to separate view to improve performance

    .contextMenu {
        ContextListView()
    }
    
    struct ContextListView: View {
        var body: some View {
            ForEach(0 ..< 100) { index in
                Button {
                  print("option \(index)")
                } label: {
                  Label("Option \(index)", systemImage: "globe")
                }
            }
        }
    }
    

    Please check the answers for this question for more understanding SwiftUI Text, changing the text with @State property causing whole view to re-render