iOS 18下SwiftUI动态更新视图的Context Menu滚动失效问题
嘿,这个问题我在iOS 18的SwiftUI开发里也碰到过!核心原因是你的Timer每0.25秒就触发一次视图更新,导致承载Context Menu的整个VStack一直在频繁重渲染,直接打断了Context Menu的滚动状态——每次父视图刷新,Context Menu的滚动位置都会被重置,自然就没法正常滚动了。
给你两个实用的解决方案,都是围绕减少承载Context Menu的视图重渲染频率来解决的:
方案1:拆分动态视图,隔离重渲染范围
把需要动态更新的文本单独封装成一个遵循Equatable的子视图,这样只有这个子视图会随数字变化重绘,外层带Context Menu的容器视图不会被频繁刷新:
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 { // 把动态更新的部分拆成独立子视图 DynamicNumberView(number: number) 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) { _ in number += 1 } } } // 独立的动态视图,只有这个视图会随number变化重绘 struct DynamicNumberView: View, Equatable { let number: Int var body: some View { Text(number.formatted()) .font(.largeTitle) } static func == (lhs: DynamicNumberView, rhs: DynamicNumberView) -> Bool { lhs.number == rhs.number } }
方案2:用ObservableObject管理状态,精准控制重绘
把Timer和数字状态放到ObservableObject里管理,结合@StateObject让SwiftUI更精准地追踪状态变化,避免不必要的视图重绘:
import SwiftUI import Combine class TimerViewModel: ObservableObject { @Published var number = 0 private var cancellables = Set<AnyCancellable>() init() { Timer.publish(every: 0.25, on: .main, in: .common) .autoconnect() .sink { [weak self] _ in self?.number += 1 } .store(in: &cancellables) } } struct ContentView: View { @StateObject private var viewModel = TimerViewModel() var body: some View { VStack { Text(viewModel.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") } } } } }
这两个方案都能解决你遇到的滚动失效问题,本质都是让承载Context Menu的视图保持稳定,不会被Timer的频繁更新干扰。
备注:内容来源于stack exchange,提问作者Abhi
相关产品推荐
相关产品推荐

