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

macOS中ScrollView内LazyVGrid按钮点击异常问题求助

macOS SwiftUI ScrollView嵌套LazyVGrid的按钮点击与Popover问题解决

问题根源

在双方向滚动的ScrollView内使用LazyVGrid时,窗口尺寸调整会触发布局重绘,SwiftUI的视图复用机制和坐标计算可能出现不同步,导致按钮点击区域失效、Popover锚点偏移或不显示。

解决方案

1. 采用popover(item:)替代isPresented绑定

将Popover的显示状态直接绑定到选中的Cell实例,避免布尔状态可能出现的不同步问题,同时确保Popover始终关联正确的单元格。

2. 明确按钮点击区域

给按钮设置与GridItem匹配的固定尺寸,避免因文本大小导致点击区域不稳定。

修改后的完整代码

import SwiftUI

struct Cell: Identifiable {
  var id: UUID = UUID()
  var i: Int = 0
  
  init(i: Int) {
    self.i = i
  }
}

struct ContentView: View {
  var columns: [GridItem] = [GridItem](repeating: GridItem(.fixed(40), spacing: 5), count: 60)
  var cells: [Cell] = []
  @State private var selectedCell: Cell?

  init() {
    cells.removeAll()
    for i in 0..<180 {
      cells.append(Cell(i:i))
    }
  }
  
  var body: some View {
    ScrollView([.horizontal, .vertical]) {
      LazyVGrid(columns: columns, spacing: 5) {
        ForEach(cells) { cell in
          ButtonView(cell: cell, selectedCell: $selectedCell)
        }
      }
    }
  }
}

struct ButtonView: View {
  var cell: Cell
  @Binding var selectedCell: Cell?
  
  var body: some View {
    Button {
      selectedCell = cell
    } label: {
      Text("\(cell.i)")
        .frame(width: 40, height: 40)
        .background(Color.gray.opacity(0.1)) // 可选,便于可视化点击区域
    }
    .buttonStyle(.plain)
    .popover(item: $selectedCell) { selectedCell in
      Text("Information line of button \(selectedCell.i)").padding()
    }
  }
}

#Preview {
    ContentView()
}

额外优化(可选)

如果仍存在坐标偏移问题,可以监听窗口 resize 事件,手动更新视图坐标:
在ButtonView中添加GeometryReader获取按钮全局坐标,并指定Popover的锚点:

struct ButtonView: View {
  var cell: Cell
  @Binding var selectedCell: Cell?
  @State private var buttonFrame: CGRect = .zero
  
  var body: some View {
    Button {
      selectedCell = cell
    } label: {
      Text("\(cell.i)")
        .frame(width: 40, height: 40)
        .background(Color.gray.opacity(0.1))
    }
    .background(
      GeometryReader { geo in
        Color.clear
          .onAppear { buttonFrame = geo.frame(in: .global) }
          .onReceive(NotificationCenter.default.publisher(for: NSWindow.didResizeNotification)) { _ in
            buttonFrame = geo.frame(in: .global)
          }
      }
    )
    .buttonStyle(.plain)
    .popover(item: $selectedCell, attachmentAnchor: .rect(.bounds), source: buttonFrame) { selectedCell in
      Text("Information line of button \(selectedCell.i)").padding()
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 08:31:06