MacOS应用TipNSPopover关闭失败及NSCollectionView悬停显示求助
问题解决方案
一、解决TipNSPopover关闭图标无法关闭弹窗的问题
你当前通过shouldDisplayUpdates监听显示状态的方式,无法响应手动点击关闭图标的操作——因为点击关闭图标不会自动触发shouldDisplay状态变更,需要手动标记Tip为已展示,同时修正popover的创建逻辑避免重复实例化:
设置TipNSPopover代理,处理关闭事件
给popover添加NSPopoverDelegate,在弹窗关闭时手动更新Tip的状态,触发shouldDisplayUpdates的流:class YourViewController: NSViewController, NSPopoverDelegate { var tipPopover: TipNSPopover? var tipObservationTask: Task<Void, Never>? let sampleTip = YourTip() // 你的Tip实例 override func viewDidLoad() { super.viewDidLoad() startTipObservation() } func startTipObservation() { tipObservationTask = Task { @MainActor in for await shouldDisplay in sampleTip.shouldDisplayUpdates { if shouldDisplay { guard tipPopover == nil else { return } // 避免重复创建 tipPopover = TipNSPopover(sampleTip) tipPopover?.delegate = self // 设置代理 tipPopover?.show(relativeTo: btnTip.bounds, of: btnTip, preferredEdge: .minY) } else { tipPopover?.close() tipPopover = nil } } } } // 代理方法:弹窗关闭时标记Tip为已展示 func popoverDidClose(_ notification: Notification) { sampleTip.markAsDisplayed() tipPopover = nil } }核心原因说明
点击TipNSPopover的关闭图标仅会关闭弹窗视图,但不会自动更新TipKit内部的展示状态。调用markAsDisplayed()会让Tip的shouldDisplay变为false,从而触发shouldDisplayUpdates的流,执行关闭和置空逻辑。
二、实现NSCollectionView单元格鼠标悬停显示TipNSPopover
需要给单元格添加鼠标追踪区域,监听进入/退出事件,在事件回调中控制Tip弹窗的显示和关闭:
1. 自定义NSCollectionViewCell
class CustomCollectionViewCell: NSCollectionViewCell { var tipPopover: TipNSPopover? var cellTip: YourTip? // 每个单元格对应的Tip实例,可根据单元格数据生成 override func awakeFromNib() { super.awakeFromNib() setupTrackingArea() } // 添加鼠标追踪区域 private func setupTrackingArea() { let trackingArea = NSTrackingArea( rect: bounds, options: [.mouseEnteredAndExited, .activeInKeyWindow], owner: self, userInfo: nil ) addTrackingArea(trackingArea) } // 鼠标进入单元格时显示Tip override func mouseEntered(with event: NSEvent) { super.mouseEntered(with: event) guard let cellTip = cellTip, tipPopover == nil else { return } tipPopover = TipNSPopover(cellTip) tipPopover?.show(relativeTo: bounds, of: self, preferredEdge: .maxY) } // 鼠标退出单元格时关闭Tip override func mouseExited(with event: NSEvent) { super.mouseExited(with: event) tipPopover?.close() tipPopover = nil } // 单元格复用前清理资源 override func prepareForReuse() { super.prepareForReuse() tipPopover?.close() tipPopover = nil cellTip = nil } }
2. 在NSCollectionView数据源中配置单元格的Tip
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { let cell = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier("CustomCell"), for: indexPath) as! CustomCollectionViewCell // 根据单元格数据生成对应的Tip实例 cell.cellTip = YourTip(for: yourDataArray[indexPath.item]) return cell }
注意事项
- 确保单元格的
wantsLayer设为true(默认可能已开启),否则追踪区域可能不生效。 - 如果需要延迟显示Tip(比如悬停0.5秒后再显示),可以在
mouseEntered中添加定时器控制。
内容的提问来源于stack exchange,提问作者Jeevan
相关产品推荐
相关产品推荐

