NSWindow frame编程修改后NSPopover位置错误问题咨询
问题根因
- 你代码中声明的
MyDelegate实例是局部变量,而NSPopover的delegate属性为弱引用,出方法作用域后会被自动释放,导致包括定位回调在内的所有代理逻辑失效。 - 调用
window.setFrame属于代码主动修改窗口尺寸,系统不会自动触发behavior = .applicationDefined类型的NSPopover重定位逻辑。 - 你调用
show方法时传入的参照视图为hostingButton.superview,按钮本身的水平位置变化会直接影响定位精度。
修复步骤
- 第一步:调整Delegate的持有方式,将其改为当前类的成员属性,避免提前释放,同时优化popover唤起的参照参数:
// 类作用域内声明成员属性持有delegate private var popoverDelegate: MyDelegate? // 唤起popover的逻辑修改为: popover = NSPopover() popover?.behavior = .applicationDefined popover?.contentViewController = self self.popoverDelegate = MyDelegate() self.popoverDelegate?.controller = self popover?.delegate = self.popoverDelegate // 参照对象直接用hostingButton本身,锚点更稳定 popover?.show(relativeTo: hostingButton.bounds, of: hostingButton, preferredEdge: .minY)
- 第二步:在你调用
window.setFrame修改窗口尺寸的代码后,追加popover重定位逻辑:
window.setFrame(newFrame, display: true) // 新增重定位逻辑 if let popover = popover, popover.isShown { // 重复调用show方法不会触发额外弹窗动画,仅会更新定位 popover.show(relativeTo: hostingButton.bounds, of: hostingButton, preferredEdge: .minY) }
- 可选优化:如果需要频繁修改窗口尺寸,可以监听
NSWindow.didResizeNotification通知,窗口大小变化时自动触发popover重定位,无需每次手动调用。
手动调整定位方案
如果不想重复调用show方法,也可以直接修改popover所属窗口的位置:
if let popover = popover, popover.isShown { // 把按钮坐标转换为屏幕坐标 let buttonScreenRect = hostingButton.convert(hostingButton.bounds, to: nil) // 计算期望的popover左上角坐标(示例为按钮下方居中对齐,可根据需要自行调整) let targetOrigin = NSPoint( x: buttonScreenRect.midX - popover.contentSize.width / 2, y: buttonScreenRect.minY - popover.contentSize.height ) popover.contentViewController?.view.window?.setFrameOrigin(targetOrigin) }
内容的提问来源于stack exchange,提问作者Frank R.
相关产品推荐
相关产品推荐

