如何实现NSPopover带动画的强制调整大小?
NSPopover带动画调整尺寸的优雅实现方案
核心思路
利用系统提供的NSPopover.anchoredEdge只读属性直接获取popover实际附着的锚定边缘,无需手动对比屏幕位置计算,再结合NSAnimationContext实现尺寸调整的平滑动画。
具体实现(Objective-C)
1. 封装帧计算方法
先把根据锚定边缘计算新窗口帧的逻辑封装成工具方法,便于复用:
- (NSRect)calculateNewPopoverFrameWithCurrentFrame:(NSRect)currentFrame anchoredEdge:(NSRectEdge)edge sizeDelta:(NSSize)delta { NSRect newFrame = currentFrame; switch (edge) { case NSMinXEdge: // 锚定左边缘,向右扩展 newFrame.size.width += delta.width; newFrame.size.height += delta.height; break; case NSMaxXEdge: // 锚定右边缘,向左扩展 newFrame.origin.x -= delta.width; newFrame.size.width += delta.width; newFrame.size.height += delta.height; break; case NSMinYEdge: // 锚定下边缘,向上扩展 newFrame.size.height += delta.height; newFrame.size.width += delta.width; break; case NSMaxYEdge: // 锚定上边缘,向下扩展 newFrame.origin.y -= delta.height; newFrame.size.height += delta.height; newFrame.size.width += delta.width; break; default: break; } return newFrame; }
2. 执行动画调整尺寸
使用NSAnimationContext的block动画(更简洁的现代方式):
- (void)animatePopoverToNewContentSize:(NSSize)newSize { NSPopover *popover = self.myPopover; NSWindow *popoverWindow = popover.window; if (!popoverWindow || ![popover isShown]) return; // 确保popover已显示 // 计算内容尺寸变化对应的窗口尺寸增量 NSSize sizeDelta = NSMakeSize(newSize.width - popover.contentSize.width, newSize.height - popover.contentSize.height); NSRect currentFrame = popoverWindow.frame; NSRect newFrame = [self calculateNewPopoverFrameWithCurrentFrame:currentFrame anchoredEdge:popover.anchoredEdge sizeDelta:sizeDelta]; // 执行动画 [NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) { context.duration = 0.3; // 自定义动画时长 popoverWindow.animator.frame = newFrame; } completionHandler:^{ // 动画结束后同步更新contentSize,避免后续布局异常 popover.contentSize = newSize; }]; }
3. 调用示例
// 调整popover内容尺寸为300x400,带动画 [self animatePopoverToNewContentSize:NSMakeSize(300, 400)];
关键注意事项
anchoredEdge仅在popover显示状态下返回有效值,动画触发前需确保popover.isShown为YES。- 动画结束后必须更新
popover.contentSize,否则后续调用setContentSize:会出现尺寸不一致问题。 - 若需支持macOS 10.9及以下版本,可替换为
NSViewAnimation实现,逻辑与上述一致,仅动画初始化方式不同。
内容的提问来源于stack exchange,提问作者jeanlain
相关产品推荐
相关产品推荐

