You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 18下SwiftUI动态更新视图的Context Menu滚动失效问题

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 15:34:27