我的侧边导航栏图标没法显示,可不可以改为跟侧边栏按钮一样的调用方式?//
// EventCenterViewController.swift
// OmadaSurveillance
//
// Created by 代小青 on 2025/9/19.
//
import Cocoa
import TPBMDesignKit
enum PageType {
case events
case customAlerts
case deviceAlerts
case none
}
// MARK: - eventcenter主控制器
class TPGuardEventCenterController: TPHomeBaseViewController{
//侧边栏controller
private let eventCenterSidebarController = TPGuardSidebarController()
private let eventCenterSplitView: TPGuardEventCenterSpliteView = TPGuardEventCenterSpliteView()
private var isIniteventCenterPlayerSplitView: Bool = false
// 侧边栏是否已收起
private var isSidebarCollapsed :Bool = false
// 右侧三个子页面controller
private let eventController = TPGuardEventController()
private let customAlertController = TPGuardCustomAlertController()
private let deviceAlertController = TPGuardDeviceAlertController()
//侧边栏的导航栏
private let eventCenterSidebarScrollView : NSScrollView = NSScrollView()
private let eventCenterSidebarOutlineView : TPGuardOutlineView = TPGuardOutlineView()
//侧边栏收起按钮
private let sidebarButton : TPBButton = TPBButton()
//侧边栏的item
private var sidebarItems: [SidebarItem] = []
//第一次加载进内存时候
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
//默认子节点展开等
}
override func setupSubviews() {
super.setupSubviews()
// MARK: - 主界面splitView分割
/*split添加调用 addArrangedSubview 不仅添加了视图,还让 NSSplitView 知道:“这个视图是我要管理的一个面板”,然后根据 arrangedSubviews 的顺序来决定左右或上下结构*/
eventCenterSplitView.addArrangedSubview(eventCenterSidebarController.view)
eventCenterSplitView.addArrangedSubview(eventController.view)
//设置代理
eventCenterSplitView.delegate = self
//分割线设置+风格
eventCenterSplitView.isVertical = true
eventCenterSplitView.dividerStyle = .thin
// 异步设置初始宽度(确保布局已完成)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let initialWidth: CGFloat = self.isSidebarCollapsed ? 48 : 160
self.eventCenterSplitView.setPosition(initialWidth, ofDividerAt: 0)
// 根据目标宽度设置状态
self.isSidebarCollapsed = false
}
// 将 splitView 添加到主视图
view.addSubview(eventCenterSplitView)
// MARK: - 侧边栏层级+按钮设置
// 添加收起按钮、配置 TPBButton 样式
sidebarButton.iconImage = .eventCenterSidebarButton
sidebarButton.fillColor = .clear
//代理
sidebarButton.target = self
sidebarButton.action = #selector(hideSidebar)
//将侧边栏按钮添加到侧边栏视图
eventCenterSidebarController.view.addSubview(sidebarButton)
//将 eventCenterSidebarOutlineView 设置为滚动视图的“文档内容视图”(即可滚动区域的内容)
eventCenterSidebarScrollView.documentView = eventCenterSidebarOutlineView
//设置滚动视图背景颜色为深灰色(接近黑色)
eventCenterSidebarScrollView.backgroundColor = NSColor(hexString: "0x1A1A1A")
//关闭系统自动调整内容边距(content insets)
eventCenterSidebarScrollView.automaticallyAdjustsContentInsets = false
//启用 Auto Layout 布局方式、允许使用约束(constraints)来定位该视图
eventCenterSidebarScrollView.translatesAutoresizingMaskIntoConstraints = false
//documentView添加水平方向约束让documentView的左右边缘与scrollView 对齐
eventCenterSidebarScrollView.documentView?.snp.remakeConstraints({ make in
make.leading.trailing.equalTo(eventCenterSidebarScrollView)
})
eventCenterSidebarOutlineView.backgroundColor = NSColor(hexString: "0x1A1A1A")
//delegate 管“怎么展示”,dataSource 管“展示什么”。
eventCenterSidebarOutlineView.delegate = self
eventCenterSidebarOutlineView.dataSource = self
eventCenterSidebarOutlineView.tpGuardDelegate = self
//启用自动行高(Auto Sizing Rows)。
eventCenterSidebarOutlineView.usesAutomaticRowHeights = true
//禁用列的自动调整大小行为。默认情况下,AppKit 会尝试平均分配或按内容调整列宽。
eventCenterSidebarOutlineView.columnAutoresizingStyle = .noColumnAutoresizing
//关闭选中项的高亮样式。原生默认是蓝色或灰色背景高亮。
eventCenterSidebarOutlineView.selectionHighlightStyle = .none
//移除表头(Header View)。默认 NSTableView 顶部有一栏用于显示列标题。
eventCenterSidebarOutlineView.headerView = nil
//禁用“轮廓列”(Outline Column)的自动缩放。在 NSOutlineView 中,“outline column”是显示层级结构的那一列(带展开箭头的那列)。
eventCenterSidebarOutlineView.autoresizesOutlineColumn = false
//允许子视图(cell 内部的 subviews)随容器自动调整大小。这会影响单元格内部控件是否跟随表格宽度拉伸。
eventCenterSidebarOutlineView.autoresizesSubviews = true
//启用 Core Animation Layer(即开启图层支持)。
eventCenterSidebarOutlineView.wantsLayer = true
eventCenterSidebarOutlineView.layer?.borderColor = NSColor(hexString: "0xFFFFFF").withAlphaComponent(0.1).cgColor
eventCenterSidebarOutlineView.layer?.borderWidth = 1.0
//注册支持接收某种拖拽类型的数据。
eventCenterSidebarOutlineView.registerForDraggedTypes([.dragOutlineItem])
//设置当前视图为拖拽源(source)时允许的操作类型。
eventCenterSidebarOutlineView.setDraggingSourceOperationMask(NSDragOperation.every, forLocal: true)
//设置单元格之间的间距
eventCenterSidebarOutlineView.intercellSpacing = NSSize(width: view.bounds.width, height: 30.0)
//创建并添加一列到表格中。
let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column"))
eventCenterSidebarOutlineView.addTableColumn(column)
// eventCenterSidebarOutlineView.onDoubleClick = { [weak self] item in
// if let device = item as? TPGuardDeviceItem, let self {
// if device.deviceType == .IPC {
// self.selectDevice(for: device)
// }
// }
// }
//允许“轮廓列”(Outline Column)根据父视图的大小变化自动调整宽度。
eventCenterSidebarOutlineView.autoresizesOutlineColumn = true
//设置每一级子节点相对于父节点的缩进像素值(indentation),这里设为 0 像素。
eventCenterSidebarOutlineView.indentationPerLevel = 0
//将scrollView添加到侧边栏视图
eventCenterSidebarController.view.addSubview(eventCenterSidebarScrollView)
}
//添加约束
override func makeConstraints() {
super.makeConstraints()
// 让 eventCenterPlayerSplitView 的 上边、左边、下边、右边 都等于父视图 view 的对应边缘,也就是让这个 splitView 填满整个父视图的客户区(不包括窗口标题栏等系统区域)。
eventCenterSplitView.snp.makeConstraints { make in
make.top.leading.bottom.trailing.equalTo(view)
}
// 添加侧边栏按钮的约束
sidebarButton.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(15)
make.top.equalToSuperview().offset(15)
make.size.equalTo(CGSize(width: 18, height: 18))
}
//scrollview添加约束(与侧边栏按钮拉开了点距离)
eventCenterSidebarScrollView.snp.makeConstraints { make in
make.top.equalTo(sidebarButton.snp.bottom).offset(8)
make.leading.trailing.bottom.equalTo(view)
}
}
// MARK: - 初始化数据
override func setupInitialData() {
super.setupInitialData()
// 全部事件
let events = SidebarItem(title: "Event", iconSymbolName: "sidebarChooseButton", showsBadge: false)
// 自定义告警
let customAlerts = SidebarItem(
title: "Custom Alert",
iconSymbolName: "sidebarChooseButton",
children: [
SidebarItem(title: "alert name", iconSymbolName: "camera.fill", showsBadge: true, unreadCount: 2),
SidebarItem(title: "alert name", iconSymbolName: "camera.fill", showsBadge: true, unreadCount: 1),
SidebarItem(title: "alert name", iconSymbolName: "person.crop.circle.fill", showsBadge: true, unreadCount: 2)
],
showsBadge: true,
unreadCount: 5
)
// 设备告警
let deviceAlerts = SidebarItem(title: "Device Alert", iconSymbolName: "sidebarChooseButton", showsBadge: true, unreadCount: 3)
sidebarItems = [events, customAlerts, deviceAlerts]
eventCenterSidebarOutlineView.reloadData()
eventCenterSidebarOutlineView.expandItem(nil, expandChildren: true)
}
//绑定侧边栏的按钮事件(收起+拉伸)
override func bindActions() {
super.bindActions()
}
//侧边栏收起方法
@objc func hideSidebar() {
let sidebarIndex = 0
let expandWidth : CGFloat = 160
let collapsedWidth : CGFloat = 48
//当侧边栏收起时
if isSidebarCollapsed{
//收起---展开
NSAnimationContext.runAnimationGroup{ context in
context.duration = 0.3 // Set the duration of the animation
self.eventCenterSplitView.setPosition(expandWidth, ofDividerAt: sidebarIndex)
}
}else {
//展开--收起
NSAnimationContext.runAnimationGroup{ context in
context.duration = 0.3 // Set the duration of the animation
self.eventCenterSplitView.setPosition(collapsedWidth, ofDividerAt: sidebarIndex)
}
}
isSidebarCollapsed.toggle()
}
}
// MARK: - 侧边栏Controller class
class TPGuardSidebarController:TPBaseViewController{
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.tpBackground.cgColor;
}
}
// MARK: - 侧边栏Controller扩展
extension TPGuardEventCenterController: NSSplitViewDelegate {
//subview 这个子视图是否允许被折叠(即宽度/高度缩小到 0)?”
func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool {
return false
}
func splitView(_ splitView: NSSplitView, constrainMinCoordinate proposedMinimumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return 48
}
func splitView(_ splitView: NSSplitView, constrainMaxCoordinate proposedMaximumPosition: CGFloat, ofSubviewAt dividerIndex: Int) -> CGFloat {
return 160
}
//“当用户拖动分隔条时,询问代理:是否允许系统自动调整 view 这个子视图的大小?”
func splitView(_ splitView: NSSplitView, shouldAdjustSizeOfSubview view: NSView) -> Bool {
return false
}
// func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool {
// return deviceListHasHidden
// }
// func splitView(_ splitView: NSSplitView, effectiveRect proposedEffectiveRect: NSRect, forDrawnRect drawnRect: NSRect, ofDividerAt dividerIndex: Int) -> NSRect {
// return deviceListHasHidden ? NSRect.zero : proposedEffectiveRect
// }
// func splitViewWillResizeSubviews(_ notification: Notification) {
// if deviceListViewController.view.frame.size.width > 500 {
// deviceListViewController.view.frame.size = NSSize(width: 500, height: deviceListViewController.view.frame.size.height)
// }
// }
}
// MARK: - 事件中心split class
class TPGuardEventCenterSpliteView: NSSplitView {
override var dividerColor: NSColor {
return NSColor.tpSeparator
}
override var dividerThickness: CGFloat {
return 1
}
}
// MARK: - 事件中心controller扩展
//extension TPGuardEventCenterController : NSOutlineViewDelegate, NSOutlineViewDataSource, TPGuardOutlineViewDelegate {
// // MARK: - DataSource
// // 缩进
// func outlineView(_ outlineView: NSOutlineView, indentationForItem item: Any?) -> CGFloat {
// return 12
// }
//}
// MARK: - OutlineView Delegate + DataSource
extension TPGuardEventCenterController: NSOutlineViewDelegate, NSOutlineViewDataSource, TPGuardOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
guard let sidebarItem = item as? SidebarItem else {
return sidebarItems.count
}
return sidebarItem.children?.count ?? 0
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
guard let sidebarItem = item as? SidebarItem else { return false }
return sidebarItem.children?.isEmpty == false
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
guard let sidebarItem = item as? SidebarItem else {
return sidebarItems[index]
}
return sidebarItem.children![index]
}
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let sidebarItem = item as? SidebarItem else { return nil }
let identifier = NSUserInterfaceItemIdentifier("SidebarCell")
var cell = outlineView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView
if cell == nil {
cell = NSTableCellView()
cell?.identifier = identifier
// icon
let imageView = NSImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
cell?.addSubview(imageView)
imageView.tag = 100
imageView.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(8)
make.centerY.equalToSuperview()
make.width.height.equalTo(16)
}
// title
let textLabel = NSTextField(labelWithString: "")
textLabel.translatesAutoresizingMaskIntoConstraints = false
textLabel.textColor = .white
cell?.addSubview(textLabel)
textLabel.tag = 101
textLabel.snp.makeConstraints { make in
make.leading.equalTo(imageView.snp.trailing).offset(8)
make.centerY.equalToSuperview()
}
// unread count
let badgeLabel = NSTextField(labelWithString: "")
badgeLabel.translatesAutoresizingMaskIntoConstraints = false
badgeLabel.wantsLayer = true
badgeLabel.layer?.backgroundColor = NSColor.systemRed.cgColor
badgeLabel.layer?.cornerRadius = 8
badgeLabel.textColor = .white
badgeLabel.alignment = .center
badgeLabel.font = NSFont.systemFont(ofSize: 12)
cell?.addSubview(badgeLabel)
badgeLabel.tag = 102
badgeLabel.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-8)
make.centerY.equalToSuperview()
make.width.greaterThanOrEqualTo(16)
make.height.equalTo(16)
}
}
if let imageView = cell?.viewWithTag(100) as? NSImageView {
imageView.image = NSImage(systemSymbolName: sidebarItem.iconSymbolName ?? "", accessibilityDescription: nil)
}
if let textLabel = cell?.viewWithTag(101) as? NSTextField {
textLabel.stringValue = sidebarItem.title
}
if let badgeLabel = cell?.viewWithTag(102) as? NSTextField {
if sidebarItem.showsBadge, let count = sidebarItem.unreadCount, count > 0 {
badgeLabel.isHidden = false
badgeLabel.stringValue = "\(count)"
} else {
badgeLabel.isHidden = true
}
}
return cell
}
// 缩进
func outlineView(_ outlineView: NSOutlineView, indentationForItem item: Any?) -> CGFloat {
return 12
}
}