为什么一开始进入到默认event.view页面就会打印viewWillAppear false日志,然后切换到device alert就会viewWillDisappear false,而在custom和device切换没有日志,event页面是不是切换有逻辑问题?且我并没有打印出这样的日志,系统还会自动打印吗?//
// EventCenterViewController.swift
import TPBMDesignKit
// 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 eventCenterRightView = NSView()
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(eventCenterRightView)
//三个右侧页面添加到view
[eventController.view, customAlertController.view, deviceAlertController.view].forEach{
eventCenterRightView.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints=false
$0.snp.makeConstraints{make in make.edges.equalToSuperview()}
}
// //默认显示event页面
print("默认显示event页面")
switchRightView(to: 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)
eventCenterSidebarOutlineView.target = self
eventCenterSidebarOutlineView.action = #selector(handleSidebarClick)
}
//添加约束
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.equalToSuperview()
}
}
// MARK: - 初始化数据(后续需要介入拉取未读数)
override func setupInitialData() {
super.setupInitialData()
// 全部事件
let events = SidebarItem(title: "Events", iconImage: .sidebarChooseButton, showsBadge: false)
// 自定义告警
let customAlerts = SidebarItem(
title: "Custom Alerts",
iconImage: .sidebarChooseButton,
children: [
SidebarItem(
title: "My Alerts",
iconImage: .sidebarChooseButton,
children: [
SidebarItem(title: "Alert 1", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 2),
SidebarItem(title: "Alert 2", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 1)
],
showsBadge: false
),
SidebarItem(
title: "Shared Alerts",
iconImage: .sidebarChooseButton,
children: [
SidebarItem(title: "Alert 3", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 3)
],
showsBadge: false
)
],
showsBadge: true,
unreadCount: 99
)
// 设备告警
let deviceAlerts = SidebarItem(title: "Device Alerts", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 3)
sidebarItems = [events, customAlerts, deviceAlerts]
}
//绑定侧边栏的按钮事件(收起+拉伸)
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: outlineview数据与界面
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 }
//唯一标记一种类型的表格单元格(cell)实现 NSOutlineView 或 NSTableView 的 cell 复用机制
let identifier = NSUserInterfaceItemIdentifier("SidebarCell")
//尝试从复用池中获取一个已经存在的、可重用的单元格
var sidebarCell = outlineView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView
if sidebarCell == nil {
sidebarCell = NSTableCellView()
sidebarCell?.identifier = identifier
// icon
let sidebarImageView = NSImageView()
sidebarImageView.translatesAutoresizingMaskIntoConstraints = false
sidebarCell?.addSubview(sidebarImageView)
sidebarImageView.tag = 100
sidebarImageView.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(0)
make.centerY.equalToSuperview()
make.width.height.equalTo(18)
}
// title
let sidebarTextLabel = NSTextField(labelWithString: "")
sidebarTextLabel.translatesAutoresizingMaskIntoConstraints = false
sidebarTextLabel.textColor = .white
// sidebarTextLabel.font = NSFont.systemFont(ofSize: 12)
sidebarCell?.addSubview(sidebarTextLabel)
sidebarTextLabel.tag = 101
sidebarTextLabel.snp.makeConstraints { make in
make.leading.equalTo(sidebarImageView.snp.trailing).offset(8)
make.centerY.equalToSuperview()
}
// unread count
// let sidebarBadgeWidth: CGFloat = 20;
// let sidebarBadgeHeigth: CGFloat = 14;
// let sidebarBadgeLabel = TPBLabel();
// sidebarBadgeLabel.textColor = .white;
// sidebarBadgeLabel.wantsLayer = true;
// sidebarBadgeLabel.layer?.backgroundColor = NSColor.red.cgColor;
// sidebarBadgeLabel.textAlignment = .center;
// sidebarBadgeLabel.layer?.cornerRadius = (sidebarBadgeHeigth - 2) / 2;
// sidebarBadgeLabel.layer?.masksToBounds = true;
// sidebarBadgeLabel.layer?.borderWidth = 1.0;
// sidebarBadgeLabel.layer?.borderColor = NSColor.red.cgColor;
// unread count
let sidebarBadgeLabel = NSTextField(labelWithString: "")
sidebarBadgeLabel.translatesAutoresizingMaskIntoConstraints = false
sidebarBadgeLabel.wantsLayer = true
sidebarBadgeLabel.layer?.backgroundColor = NSColor.systemRed.cgColor
sidebarBadgeLabel.layer?.cornerRadius = 6
sidebarBadgeLabel.textColor = .white
sidebarBadgeLabel.alignment = .center
sidebarBadgeLabel.font = NSFont.systemFont(ofSize: 10)
sidebarCell?.addSubview(sidebarBadgeLabel)
sidebarBadgeLabel.tag = 102
sidebarBadgeLabel.snp.makeConstraints { make in
make.leading.equalTo(sidebarTextLabel.snp.trailing).offset(8)
make.centerY.equalTo(sidebarTextLabel.snp.centerY)//对齐文字
make.width.greaterThanOrEqualTo(16)
make.height.equalTo(16)
}
}
if let sidebarImageView = sidebarCell?.viewWithTag(100) as? NSImageView {
sidebarImageView.image = sidebarItem.iconImage
}
if let sidebarTextLabel = sidebarCell?.viewWithTag(101) as? NSTextField {
sidebarTextLabel.stringValue = sidebarItem.title
}
if let sidebarBadgeLabel = sidebarCell?.viewWithTag(102) as? NSTextField {
if sidebarItem.showsBadge, let unreadCount = sidebarItem.unreadCount, unreadCount > 0 {
sidebarBadgeLabel.isHidden = false
if unreadCount >= 99 {
sidebarBadgeLabel.stringValue = "\(unreadCount)+"
}else{
sidebarBadgeLabel.stringValue = "\(unreadCount)"
}
} else {
sidebarBadgeLabel.isHidden = true
}
}
return sidebarCell
}
// 缩进
func outlineView(_ outlineView: NSOutlineView, indentationForItem item: Any?) -> CGFloat {
return 0
}
func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
guard let sidebarItem = item as? SidebarItem else { return true }
// My Alerts / Shared Alerts 这类分组节点不可选中
if let parent = outlineView.parent(forItem: sidebarItem) as? SidebarItem,
parent.title == "Custom Alerts",
sidebarItem.children?.isEmpty == false {
return false
}
// Custom Alerts 主节点和 alert name 子节点都可以选中
return true
}
func outlineViewSelectionDidChange(_ notification: Notification) {
let selectedRow = eventCenterSidebarOutlineView.selectedRow
guard selectedRow >= 0,
let selectedItem = eventCenterSidebarOutlineView.item(atRow: selectedRow) as? SidebarItem else { return }
for i in 0..<eventCenterSidebarOutlineView.numberOfRows {
guard let cell = eventCenterSidebarOutlineView.view(atColumn: 0, row: i, makeIfNecessary: false) as? NSTableCellView,
let sidebarItem = eventCenterSidebarOutlineView.item(atRow: i) as? SidebarItem,
let titleLabel = cell.viewWithTag(101) as? NSTextField,
let iconView = cell.viewWithTag(100) as? NSImageView else { continue }
var shouldHighlight = false
// Events / Device Alerts 高亮
if sidebarItem == selectedItem && (sidebarItem.title == "Events" || sidebarItem.title == "Device Alerts") {
shouldHighlight = true
}
// Custom Alerts 主节点 + 子节点高亮
if sidebarItem.title == "Custom Alerts" {
if selectedItem.title == "Custom Alerts" {
shouldHighlight = true
} else if let parent = eventCenterSidebarOutlineView.parent(forItem: selectedItem) as? SidebarItem,
parent.title == "Custom Alerts",
(sidebarItem == parent || sidebarItem == selectedItem) {
shouldHighlight = true
}
}
// 更新视觉效果
if shouldHighlight {
cell.wantsLayer = true
cell.layer?.backgroundColor = NSColor.tpTextHover.cgColor
titleLabel.textColor = NSColor.tpStatusGreen
iconView.image = .sidebarChoosedButton
} else {
cell.layer?.backgroundColor = NSColor.clear.cgColor
titleLabel.textColor = NSColor.tpTextWhite
iconView.image = sidebarItem.iconImage
}
}
}
// 切换右侧视图
private func switchRightView(to newView: NSView) {
[eventController.view, customAlertController.view, deviceAlertController.view].forEach {
$0.isHidden = $0 != newView
}
}
@objc func handleSidebarClick() {
let row = eventCenterSidebarOutlineView.clickedRow
guard row >= 0,
let item = eventCenterSidebarOutlineView.item(atRow: row) as? SidebarItem else { return }
switch item.title {
case "Events":
switchRightView(to: eventController.view)
collapseCustomAlertsIfNeeded()
case "Device Alerts":
switchRightView(to: deviceAlertController.view)
collapseCustomAlertsIfNeeded()
case "Custom Alerts":
// 点击主节点才折叠/展开
if eventCenterSidebarOutlineView.isItemExpanded(item) {
eventCenterSidebarOutlineView.collapseItem(item, collapseChildren: true)
} else {
eventCenterSidebarOutlineView.expandItem(item, expandChildren: true)
}
switchRightView(to: customAlertController.view)
default:
// 点击 alert 子节点
if let parent = eventCenterSidebarOutlineView.parent(forItem: item) as? SidebarItem,
parent.title == "Custom Alerts" {
//switchRightView(to: customAlertController.view)
// 不折叠父节点
}
}
// 统一通过 selectRowIndexes 更新选中
if outlineView(eventCenterSidebarOutlineView, shouldSelectItem: item) {
eventCenterSidebarOutlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
// 触发高亮刷新
outlineViewSelectionDidChange(Notification(name: NSOutlineView.selectionDidChangeNotification))
}
}
private func collapseCustomAlertsIfNeeded() {
if let customAlertsItem = sidebarItems.first(where: { $0.title == "Custom Alerts" }) {
eventCenterSidebarOutlineView.collapseItem(customAlertsItem, collapseChildren: true)
}
}
}