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

如何移除数组元素直至WrappingHStack高度不超可用屏幕高度?

问题与解决方案

问题场景

使用WrappingHStack展示自适应宽高的按钮,要求所有元素不溢出屏幕且无滚动,需自动移除足够元素避免垂直溢出。通过GeometryReader获取可用高度,用onChange检测容器高度是否超过可用高度,若超过则随机删除数组元素;同时监听数组元素数量变化再次校验,但Xcode报错:onChange(of: Int) action tried to update multiple times per frame。

报错原因

  1. 状态更新循环:删除元素后,buttonContent.count变化触发第二个onChange,该回调又可能再次删除元素,导致同一帧内多次修改状态变量,触发SwiftUI的帧内多次更新检测机制报错。
  2. 状态变量声明错误:原代码中buttonContent是普通var而非@State,修改它无法触发视图刷新,逻辑完全失效。

可行解决方案

核心修改点

  • 将buttonContent改为@State变量,确保修改能触发视图重新渲染。
  • 避免帧内多次触发状态修改,把删除操作放到下一帧执行,打断循环更新。
  • 简化布局结构,优化可用高度的获取方式。

修改后的代码

import SwiftUI
import WrappingHStack

struct ButtonFitView: View {
    @State private var availableHeight: CGFloat = 0.0
    @State private var buttonContent = ["alpha", "beta", "gamma", "delta", "epsilon", "zeta", "eta"]
    
    var body: some View {
        GeometryReader { fullScreenProxy in
            VStack(spacing: 0) {
                WrappingHStack(buttonContent, id: \.self, alignment: .center) { text in
                    Button(action: {}) {
                        Text(text)
                            .padding(.horizontal, 12)
                            .padding(.vertical, 8)
                            .background(RoundedRectangle(cornerRadius: 6).fill(.yellow))
                    }
                }
                .background(
                    GeometryReader { contentProxy in
                        Color.clear
                            .onChange(of: contentProxy.size.height) { newHeight in
                                guard newHeight > availableHeight else { return }
                                // 异步执行删除,避免帧内多次更新
                                DispatchQueue.main.async {
                                    removeRandomElement()
                                }
                            }
                    }
                )
                Spacer()
            }
            .onAppear {
                availableHeight = fullScreenProxy.size.height
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
    
    private func removeRandomElement() {
        guard !buttonContent.isEmpty else { return }
        buttonContent.remove(at: buttonContent.indices.randomElement()!)
    }
}

关键优化说明

  1. 状态变量修正:buttonContent改为@State,确保修改后视图能重新渲染,让高度检测逻辑生效。
  2. 异步执行删除:用DispatchQueue.main.async把删除操作放到下一帧,避免同一帧内多次触发onChange回调,打断循环更新。
  3. 简化布局:直接用外层GeometryReader获取屏幕可用高度,去掉冗余的ZStack布局,代码更简洁高效。
  4. 自动循环校验:删除元素后视图重新渲染,WrappingHStack的高度会再次被检测,若仍溢出则继续删除,直到内容高度符合要求。

内容的提问来源于stack exchange,提问作者Joseph

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:23:09