viewwithtag

这个函数有个bug,点击alertname,并没有alert name和custom alert一起高亮,而是都不高亮,这是为什么: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 } } }
09-30
我的侧边导航栏图标没法显示,可不可以改为跟侧边栏按钮一样的调用方式?// // 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 } }
09-29
里面哪里写了图标大小?为什么我的button和侧边栏的图标不一样大// 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", iconImage: .sidebarChooseButton, showsBadge: false) // 自定义告警 let customAlerts = SidebarItem( title: "Custom Alert", iconImage: .sidebarChooseButton, children: [ SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 2), SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 1), SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 2) ], showsBadge: true, unreadCount: 99 ) // 设备告警 let deviceAlerts = SidebarItem(title: "Device Alert", iconImage: .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: 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(16) } // 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 } }
09-29
侧边栏的button和cell的第一个图片icon离leading的距离为什么不同?不都是offset8吗 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", iconImage: .sidebarChooseButton, showsBadge: false) // 自定义告警 let customAlerts = SidebarItem( title: "Custom Alert", iconImage: .sidebarChooseButton, children: [ SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 2), SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 1), SidebarItem(title: "alert name", iconImage: .sidebarChooseButton, showsBadge: true, unreadCount: 2) ], showsBadge: true, unreadCount: 99 ) // 设备告警 let deviceAlerts = SidebarItem(title: "Device Alert", iconImage: .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: 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(8) make.centerY.equalToSuperview() make.width.height.equalTo(16) } // 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 12 } }
09-29
import SnapKit final class TPMediaPlayerTooView : UIView, TPMediaViewRefreshDelegate { private(set) var clickAction : TPMediaPlayerClickAction?; private var viewBtns = [UIButton](); private var btnItems = [TPMediaPlayToolItem](); private let maxCnt : Int = 5; private lazy var contentView : UIScrollView = { let view = UIScrollView(); view.showsHorizontalScrollIndicator = false; view.showsVerticalScrollIndicator = false; view.backgroundColor = UIColor.clear; view.delegate = self; return view }() var isVertical : Bool = false { didSet { self.relaySubviews(views: self.viewBtns, isVertical: isVertical); } } var isInScreen: Bool = false { didSet { if (!isInScreen && !self.indicator.isHidden) { self.contentView.contentOffset = .zero; self.indicator.offset = 0; } } } var darkStyle : Bool = false; var enableIndicator : Bool = false; let indicatorWidth : CGFloat = 25; private(set) lazy var indicator : TPProgressIndicatorView = { let view = TPProgressIndicatorView(); view.clipsToBounds = true; view.backgroundColor = UIColor.tpbGrey//.tpbProgressBackground; view.isHidden = true; return view; }() init(items:[TPMediaPlayToolItem], downloadCenterViewModel: TPDownloadCenterViewModel? = nil, clickAction:@escaping TPMediaPlayerClickAction) { super.init(frame: CGRectZero); self.addSubview(self.contentView); self.contentView.snp.remakeConstraints { make in make.top.bottom.leading.trailing.equalToSuperview() }; self.clickAction = clickAction; self.downloadCenterViewModel = downloadCenterViewModel; self.downloadCenterViewModel?.reloadDownloadTaskNumberViewInToolViewAction = { [weak self](downloadTaskNumber: Int, failedTaskNumber: Int) in if let self = self { for (index, item) in items.enumerated() { if index >= 0 && index < self.viewBtns.count && item.type == .PlayBackDownload { reloadDownloadNumberSubScript(downloadTaskNumber: downloadTaskNumber, errorTaskNumber: failedTaskNumber); } } } } self.setItems(items: items); } var downloadCenterViewModel: TPDownloadCenterViewModel?; lazy var downloadingTaskNumberLabel: UILabel = { let downloadingTaskNumberLabel = UILabel(frame: .zero); downloadingTaskNumberLabel.textColor = UIColor.tpbTextWhite; downloadingTaskNumberLabel.font = .projectFont(ofSize: 12); downloadingTaskNumberLabel.textAlignment = .center; downloadingTaskNumberLabel.backgroundColor = .tpbRed; return downloadingTaskNumberLabel; }() lazy var downloadErrorImageView: UIImageView = { let imageView = UIImageView(frame: .zero) imageView.backgroundColor = .clear; imageView.image = UIImage(named: "playback_download_error"); return imageView; }() public func setItems(items:[TPMediaPlayToolItem]) { if self.btnItems.elementsEqual(items, by: { item1, item2 in return item1.type == item2.type }) { refresh(self.darkStyle); return } self.viewBtns.forEach({$0.removeFromSuperview()}); self.viewBtns.removeAll() self.btnItems.removeAll(); self.btnItems.append(contentsOf: items); for item in items { let btn = UIButton.init(type: .custom); btn.setImage(TPImageLiteral(item.normalImgUri), for: .normal); btn.imgUri = item.normalImgUri; btn.secondUri = item.secondUri; var img = TPImageLiteral(item.highImgUri) btn.setImage(img, for: .highlighted) img = TPImageLiteral(item.disableImgUri) btn.setImage(img, for: .disabled) btn.uriList = item.uriList; btn.tag = TPMediaPreviewUtils.kMediaPlayerSubviewBaseTag + item.type.rawValue; btn.addTarget(self, action: #selector(clickViewAction), for: .touchUpInside); // 如果有图片变化的需要在变化的时候再次修改 btn.accessibilityLabel = item.accessibilityLabel self.contentView.addSubview(btn); self.viewBtns.append(btn); } relaySubviews(views: self.viewBtns, isVertical: isVertical) refresh(self.darkStyle); reloadDownloadNumberSubScript(downloadTaskNumber: self.downloadCenterViewModel?.downloadingTaskNumber ?? 0, errorTaskNumber: self.downloadCenterViewModel?.failedTaskNumber ?? 0) } func reloadDownloadNumberSubScript(downloadTaskNumber: Int, errorTaskNumber: Int){ relayoutDownloadingSubviews() if downloadTaskNumber > errorTaskNumber { // 存在正在下载的任务,优先显示正在下载的任务数 downloadingTaskNumberLabel.isHidden = false; downloadErrorImageView.isHidden = true; downloadingTaskNumberLabel.text = String(format: "%d", downloadTaskNumber - errorTaskNumber); } else if downloadTaskNumber == errorTaskNumber && errorTaskNumber > 0 { // 没有正在下载任务,但是有下载失败任务,显示error图标 downloadErrorImageView.isHidden = false; downloadingTaskNumberLabel.isHidden = true; } else { downloadingTaskNumberLabel.isHidden = true; downloadErrorImageView.isHidden = true; } } private func relayoutDownloadingSubviews() { guard let downBtn = self.viewWithType(type: .PlayBackDownload) else { return } let blk = { (view:UIView, clip:Bool) in if (view.superview != downBtn) { view.removeFromSuperview() downBtn.addSubview(view) let downloadingTaskNumberLabelHW = 16.0; view.snp.remakeConstraints { make in make.leading.equalToSuperview().offset(30); make.top.equalToSuperview().offset(-6); make.width.height.equalTo(downloadingTaskNumberLabelHW); } if (clip) { view.layer.cornerRadius = downloadingTaskNumberLabelHW / 2.0; view.clipsToBounds = true; } } } blk(self.downloadErrorImageView, false); blk(self.downloadingTaskNumberLabel, true); } public func viewWithType(type:TPMediaPlayerFuncType) -> UIButton? { let tag = type.rawValue + TPMediaPreviewUtils.kMediaPlayerSubviewBaseTag; return self.viewWithTag(tag) as? UIButton; } public func view(types:[TPMediaPlayerFuncType]) -> [UIButton]? { var items : [UIButton] = [UIButton](); for type in types { guard let btn = self.viewWithType(type: type) else { continue } items.append(btn); } return items; } public func setEnable(enable:Bool) { if enable { self.viewBtns.forEach({$0.isEnabled = true}) } else { self.viewBtns.forEach({$0.isEnabled = false}) } } public func setEnable(type:TPMediaPlayerFuncType, enable:Bool) { self.viewWithType(type: type)?.isEnabled = enable; } public func hitInBtn(point:CGPoint, parent:UIView) -> Bool { if (self.isHidden) { return false; } for btn in self.viewBtns { let btnFrame = btn.convert(btn.bounds, to: parent); if (btnFrame.contains(point)) { self.clickViewAction(btn); return true } } return false; } public func itemTypes() -> [TPMediaPlayerFuncType] { var types : [TPMediaPlayerFuncType] = []; self.btnItems.forEach({ types.append($0.type) }); return types; } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() relaySubviews(views: self.viewBtns, isVertical: isVertical) } override func didMoveToSuperview() { super.didMoveToSuperview() if !self.isInScreen, self.indicator.superview == nil, let superView = self.superview { superView.addSubview(self.indicator) self.indicator.snp.remakeConstraints { make in make.centerX.equalTo(self); make.size.equalTo(CGSize(width: indicatorWidth, height: 2)); make.top.equalTo(self.snp.bottom).offset(5); } self.indicator.layer.cornerRadius = 1; } } override func removeFromSuperview() { super.removeFromSuperview() self.indicator.removeFromSuperview() } //MARK: - TPMediaViewRefreshDelegate func refresh(_ darkStyle: Bool) { self.darkStyle = darkStyle; self.viewBtns.forEach({ $0.refresh(darkStyle)}) self.indicator.isHidden = self.isHidden || !enableIndicator || darkStyle || self.viewBtns.count <= maxCnt; if (!self.indicator.isHidden) { self.handleScroll(self.contentView); } } //MARK: - Private private func relaySubviews(views:[UIButton], isVertical:Bool) { if (views.count <= 0) { return } var lastBtn : UIButton? = nil; var lrMargin : CGFloat = 0; let btnWH : CGFloat = 56.0; let btnH : CGFloat = 24.0; var itemMargin : CGFloat = 24.0; var vContH : CGFloat = 0; var minVMargin : CGFloat = isVertical && views.count > 3 ? 10 : 24; if (!isVertical) { if isInScreen { let itemCnt = views.count// > 5 ? 5 : views.count lrMargin = views.count <= 2 ? (CGRectGetWidth(self.frame) - btnWH * CGFloat(itemCnt)) / CGFloat(itemCnt + 1) : lrMargin let totalMargin = CGRectGetWidth(self.frame) - 2.0 * lrMargin - btnWH * CGFloat(itemCnt) itemMargin = totalMargin / CGFloat(max(1, (itemCnt - 1))) } else { if (CGFloat((views.count - 1) * 24) + CGFloat(views.count) * btnWH) > CGRectGetWidth(UIScreen.main.bounds) || views.count > 4 { let itemCnt : CGFloat = views.count > maxCnt ? (CGFloat(maxCnt) + 0.5) : CGFloat(views.count) lrMargin = views.count <= 2 ? (CGRectGetWidth(self.frame) - btnWH * CGFloat(itemCnt)) / CGFloat(itemCnt + 1) : lrMargin + 45 let totalMargin = CGRectGetWidth(self.frame) - 2.0 * lrMargin - btnWH * CGFloat(itemCnt) itemMargin = abs(totalMargin / CGFloat(max(1, itemCnt - (CGFloat(views.count) > itemCnt ? 0.5 : 1)))) } else { lrMargin = (CGRectGetWidth(self.frame) - (CGFloat((views.count - 1) * 24) + CGFloat(views.count) * btnWH)) / 2 } } } else { //横屏特殊处理 minVMargin = isVertical && views.count > 3 ? 0 : 24; let itemCnt = CGFloat(views.count); let vMargin : CGFloat = 32; let contH = max(0, itemCnt - 1) * itemMargin + (btnH + vMargin) * itemCnt; if contH > CGRectGetHeight(self.frame) { let totalMargin = CGRectGetHeight(self.frame) - 2.0 * lrMargin - btnH * CGFloat(itemCnt) itemMargin = totalMargin / CGFloat(max(1, (itemCnt - 1))) vContH = max(0, itemCnt - 1) * itemMargin + btnH * itemCnt lrMargin = max(minVMargin, (CGRectGetHeight(self.frame) - vContH) / 2) } else { itemMargin = itemMargin + vMargin vContH = max(0, itemCnt - 1) * itemMargin + (btnH + vMargin) * itemCnt; lrMargin = max(minVMargin, (CGRectGetHeight(self.frame) - vContH) / 2) } } for i in 0..<views.count { let btn = views[i]; guard let lastView = lastBtn else { btn.snp.remakeConstraints { make in if (isVertical) { make.top.equalToSuperview().offset(lrMargin / 2); make.centerX.equalToSuperview(); } else { make.leading.equalToSuperview().offset(lrMargin); make.centerY.equalToSuperview(); } make.width.equalTo(btnWH); make.height.lessThanOrEqualTo(btnWH); make.height.greaterThanOrEqualTo(btnH) }; lastBtn = btn; continue } btn.snp.remakeConstraints { make in make.width.height.equalTo(lastView); if (isVertical) { make.centerX.equalTo(lastView); make.top.equalTo(lastView.snp_bottomMargin).offset(itemMargin); if (i == views.count - 1) { make.bottom.equalToSuperview().offset(0 - itemMargin); } } else { make.centerY.equalTo(lastView); make.leading.equalTo(lastView.snp.trailing).offset(itemMargin); if (i == views.count - 1) { if (isVertical) { make.bottom.equalToSuperview(); } else { make.trailing.equalToSuperview().offset(0 - lrMargin); } } } } lastBtn = btn; } let itemCnt : CGFloat = CGFloat(self.viewBtns.count); if (isVertical) { self.contentView.contentSize = CGSizeMake(CGRectGetWidth(self.frame), max(CGRectGetHeight(self.frame) - 2 * minVMargin, vContH)) } else { let gap = enableIndicator && self.viewBtns.count > self.maxCnt ? 5.0 : 0.0; self.contentView.contentSize = CGSize(width: max(CGRectGetWidth(self.frame), lrMargin * 2 + max(0, itemCnt - 1) * itemMargin + itemCnt * btnWH) + gap, height: CGRectGetHeight(self.frame)); } } @objc private func clickViewAction(_ sender:UIButton) { guard let type = TPMediaPlayerFuncType(rawValue: sender.tag - TPMediaPreviewUtils.kMediaPlayerSubviewBaseTag) else { return } for item in self.btnItems { if (type == item.type) { guard let callback = item.clickAction else { self.clickAction?(type, false, nil); break } callback(type, false, nil); break; } } } } extension UIButton { @objc var textLable : UILabel { get { if let view = objc_getAssociatedObject(self, unsafeBitCast(#selector(setter: self.textLable), to: UnsafeRawPointer.self)) as? UILabel { return view; } let view = UILabel.init(); view.font = .projectFont(ofSize: 12) view.textColor = UIColor.tpbTextWhite; view.textAlignment = .center; view.backgroundColor = .clear; self.addSubview(view); view.snp.remakeConstraints { make in make.center.equalTo(self); }; self.textLable = view; return view; } set { objc_setAssociatedObject(self, unsafeBitCast(#selector(setter: textLable), to: UnsafeRawPointer.self), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } @objc var subTextLabel: UILabel { get { if let view = objc_getAssociatedObject(self, unsafeBitCast(#selector(setter: self.subTextLabel), to: UnsafeRawPointer.self)) as? UILabel { return view } let view = UILabel.init() view.font = .projectFont(ofSize: 12) view.textColor = .tpbRed view.textAlignment = .center view.backgroundColor = .clear self.addSubview(view) view.snp.remakeConstraints { make in make.centerX.equalTo(self) make.top.equalTo(self.snp.bottom) make.height.equalTo(15) }; self.subTextLabel = view return view; } set { objc_setAssociatedObject(self, unsafeBitCast(#selector(setter: subTextLabel), to: UnsafeRawPointer.self), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } } extension TPMediaPlayerTooView : UIScrollViewDelegate { func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { handleScroll(scrollView) } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { handleScroll(scrollView, decelerate: decelerate) } func scrollViewDidScroll(_ scrollView: UIScrollView) { handleScroll(scrollView) } private func handleScroll(_ scrollView:UIScrollView, decelerate: Bool = false) { if (decelerate || self.indicator.isHidden) { return } let len = scrollView.contentSize.width - CGRectGetWidth(self.frame); let offset = scrollView.contentOffset.x; if (len >= 1) { self.indicator.offset = offset / len * (indicatorWidth - self.indicator.width); } } } final class TPProgressIndicatorView : UIView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var width : CGFloat = 11 { didSet { width != oldValue ? self.setNeedsDisplay() : nil; } } var color : UIColor = .tpbGreen { didSet { color != oldValue ? self.setNeedsDisplay() : nil } } var offset : CGFloat = 0 { didSet { if (offset != oldValue) { self.setNeedsDisplay() } } } init() { super.init(frame: .zero) } override func draw(_ rect: CGRect) { super.draw(rect) let pathH = CGRectGetHeight(rect); let path = UIBezierPath(rect: CGRect(x: offset, y: 0, width: width , height: pathH)) path.lineWidth = pathH; color.setStroke() path.stroke() } } 帮我修改整个代码,用方案2实现
最新发布
10-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值