最近属实是睡不着系列,SwiftUI 精通之路的分享目前在搁置,因为目前自己在尝试开发自己的第一款软件,是壁纸类的 MacOS 应用程序,所以自己也在学习一些 AppKit框架,今天分享下这个 NSPopover.behavior 这个弹出层类的属性,它主要可以做什么!
在 macos 开发中,NSPopover 是一个类,允许你在应用程序的用户界面中显示浮动的弹出窗口。
.behavior 属性主要是用于控制弹出窗口的行为,特别是用户与其进行交互时的响应。
它主要有这几变量:.transient, .semitransient, .applicationDefined, .modal
1 .transient
-
描述: 这种行为的弹出窗口将保持在用户的屏幕上,直到用户点击弹出窗口外的区域。
-
使用场景: 当你希望弹出窗口只在用户需要时出现,而不干扰用户的工作流时,可以使用这种行为。比如,工具提示或辅助信息窗口。
注意: 这种类型的弹出窗口通常在用户与应用程序的其他部分进行交互时消失。
2 .semitransient
-
描述: 这种行为的弹出窗口在用户点击窗口之外时不会立即消失。用户可以在窗口内和外部之间进行交互。
-
使用场景: 如果你希望用户在与弹出窗口交互时能够不被打断,但仍然希望他们能够关闭它。
3 .applicationDefined:
- 描述: 这种行为表示弹出窗口的行为由应用程序自定义。
- 使用场景: 当你想根据应用的特定需求来定义弹出窗口的显示和消失行为时,可以使用这个选项。
4 .modal:
-
描述: 这种行为的弹出窗口会阻止用户与应用的其他部分进行交互,直到弹出窗口被关闭。
-
使用场景: 当你希望强制用户解决某个问题或提供某个输入时,可以使用这个选项。
下面示例案例展示如何创建一个
NSPopover并且其行为设置为.transient
import AppKit
class AppDelegate: NSObject, NSApplicationDelegate {
var popover: NSPopover!
func applicationDidFinishLanuching(_ notification: Notification) {
popover = NSPopover()
popover.contentSize = NSSize(width: 300, height: 200)
popover.behavior = .transient
popover.contentViewController = NSViewController() // 设置内容视图控制器
}
}
NSPopover.show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge)
NSPopover.show(relativeTo:of:preferredEdge:) 是 NSPopover 用来显示弹出窗口的方法。通过这个方法,你可以将 NSPopover 相对于某个视图显示出来,并决定箭头的位置。它的具体参数如下:
方法定义
func show(relativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge)
参数介绍
relativeTo positioningRect: NSRect
-
定义了弹出层的位置矩形(一个相对定位矩形),通常是相对于一个按钮或者视图的 bounds 来决定弹出层应该出现在屏幕上的哪个位置。通常传递的是状态栏按钮或其他视图的
bounds。 -
例如:
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
of positioningView: NSView
-
指定用于定位 NSPopover 的视图。NSPopover 会相对于该视图进行弹出,箭头会根据该视图的位置进行定位。
-
例如传入一个 NSButton 或状态栏按钮,NSPopover 就会相对于该按钮显示。
preferredEdge: NSRectEdge
-
指定弹出层的箭头应该出现在视图的哪一边。这是 NSRectEdge 类型的枚举值,可选的边包括:
-
.minX:箭头出现在左边。
-
.maxX:箭头出现在右边。
-
.minY:箭头出现在视图的顶部(通常用于状态栏按钮弹出时)。
-
.maxY:箭头出现在视图的底部。
假设你有一个状态栏按钮,你希望弹出层从按钮上方弹出,带有指向按钮的箭头。你可以使用如下代码:
if let button = statusBarItem.button {
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
}
在这个例子中:
relativeTo: button.bounds 表示弹出层的位置相对于按钮的边界进行定位。
of: button 表示弹出层会在这个按钮附近弹出。
preferredEdge: .minY 表示弹出层的箭头应该指向按钮的顶部,弹出层会从按钮的上方出现。
以下是一个完整的示例,展示如何在状态栏按钮上方显示带有箭头的 NSPopover:
class AppDelegate: NSObject, NSApplicationDelegate {
var statusBarItem: NSStatusItem!
var popoverWindow: NSPopover!
func applicationDidFinishLaunching(_ notification: Notification) {
createStatusBarSymbol()
createPopoverWindow()
}
func createStatusBarSymbol() {
statusBarItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.variableLength)
let image = NSImage(systemSymbolName: "photo.stack.fill",accessibilityDescription: "")
image?.size = NSSize(width: 25, height: 25)
image?.isTemplate = true
statusBarItem.button?.image = image
statusBarItem.button?.action = #selector(handleStatusButton)
statusBarItem.button?.target = self
}
func createPopoverWindow() {
popoverWindow = NSPopover()
popoverWindow.contentSize = NSSize(width: 300, height: 400)
popoverWindow.behavior = .transient
popoverWindow.contentViewController = NSHostingController(rootView: ContentView())
}
@objc
func handleStatusButton() {
if let button = statusBarItem.button {
// 弹出 Popover
if popoverWindow.isShown {
popoverWindow.performClose(nil)
} else {
popoverWindow.show(relativeTo: button.bounds, of: button,preferredEdge: .minY)
}
}
}
}
在这个例子中,NSPopover 会从状态栏按钮的上方弹出,并带有指向该按钮的箭头。
对于 relativeTo 这个属性的通俗的解释:
relativeTo 参数指定的是弹出层相对于某个视图(比如按钮)的位置矩形,它帮助你定义 NSPopover 弹出的位置。简单来说,relativeTo 是告诉系统,弹出层应该“靠近”哪部分显示。
假设你有一个按钮(或其他视图),当用户点击这个按钮时,弹出层会显示出来。relativeTo 就是指 这个弹出层应该以按钮的哪个部分为参考位置。通常,这个矩形是按钮的大小和位置。你可以理解为:
-
relativeTo 相当于告诉系统:“我想让弹出层显示在这个按钮的旁边”。
-
这个矩形(NSRect)定义了参考的区域,也就是 弹出层会靠近并与这个区域对齐。
通常,设置为 button.bounds, 表示弹出层相对于整个按钮区域弹出。
举个形象的例子:
想象有一张纸(button.bounds 就是纸的大小),你要在这张纸的旁边(或上面、下面)放一个盒子(就是 NSPopover)。relativeTo 就是这张纸的位置和大小,告诉系统你想让盒子放在纸的哪个方向。
-
如果你说
relativeTo: button.bounds,系统会让弹出层出现在按钮附近,具体靠哪一边由preferredEdge决定,比如上方或下方。 -
preferredEdge是决定弹出层箭头在哪边的参数,比如箭头指向按钮的顶部、左边、右边等。
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
这段代码做的事情就是:
-
relativeTo: button.bounds:弹出层相对于按钮的整个区域进行定位(通常是弹出层出现在这个区域的上方、下方、左边或右边)。 -
preferredEdge: .minY:弹出层的箭头会在按钮的顶部,弹出层会从按钮的上方出现。
如果你换成 .maxY,弹出层就会从按钮的下方出现。