Cocoa 如何绘制自己的NSTabView选项卡?
我想为Cocoa 如何绘制自己的NSTabView选项卡?,cocoa,user-interface,nstabview,Cocoa,User Interface,Nstabview,我想为NSTabViewItems绘制我自己的选项卡。我的标签应该看起来不同,从左上角开始,而不是居中 如何才能做到这一点?NSTabView不是Cocoa中最可自定义的类,但可以对其进行子类化并绘制自己的图形。除了维护选项卡视图项的集合外,您不会使用超类中的很多功能,最终将实现许多NSView和NSResponder方法,以使绘图和事件处理正常工作 最好先看看免费或开源的tab bar控件,我以前使用过,这比实现我自己的tab view子类(它正在替换)容易得多。我最近做了一些我正在做的事情
NSTabViewItem
s绘制我自己的选项卡。我的标签应该看起来不同,从左上角开始,而不是居中
如何才能做到这一点?NSTabView不是Cocoa中最可自定义的类,但可以对其进行子类化并绘制自己的图形。除了维护选项卡视图项的集合外,您不会使用超类中的很多功能,最终将实现许多NSView和NSResponder方法,以使绘图和事件处理正常工作
最好先看看免费或开源的tab bar控件,我以前使用过,这比实现我自己的tab view子类(它正在替换)容易得多。我最近做了一些我正在做的事情 最后我使用了一个tables选项卡视图,然后自己在另一个视图中绘制选项卡。我希望我的标签成为窗口底部状态栏的一部分
显然,你需要支持鼠标点击,这相当容易,但你应该确保你的键盘支持也能工作,这有点棘手:你需要运行计时器,在半秒钟后没有键盘访问后切换选项卡(看看OSX的操作方式)。可访问性是您应该考虑的另一件事,但您可能会发现它很有效—我还没有在代码中检查它。我对此非常感兴趣—并发布了—因为PSMTabBarControl现在已经过时,也发布了可以将NSTabView的样式设置为TABLES,然后使用一个NSSECTIONEDCONTROL来控制它子类NSSegmentedCell以覆盖样式和行为。要了解如何做到这一点,请查看这个模拟Xcode 4样式选项卡的项目:。使用单独的
NSSegmentedCell
来控制NSTabView
中的选项卡选择非常容易。您只需要一个实例变量,它们都可以绑定到该文件的所有者或nib文件中出现的任何其他控制器类中。只需在类接口声明中放入如下内容:
@property NSInteger selectedTabIndex;
然后,在IB绑定检查器中,将NSTabView
和NSSegmentedCell
的选定索引绑定到相同的selectedTabIndex
属性
这就是你需要做的!您不需要初始化属性,除非您希望默认的选定选项卡索引不是零。您可以保留这些选项卡,也可以使
NSTabView
表可用。无论哪个控件更改选择,控件都将保持同步。绘制选项卡的可能方法之一是使用NSCollectionView。以下是Swift 4示例:
类TabViewStackController
包含TabViewStackController
预先配置的样式。未指定和自定义TabBarView
class TabViewStackController: ViewController {
private lazy var tabBarView = TabBarView().autolayoutView()
private lazy var containerView = View().autolayoutView()
private lazy var tabViewController = TabViewController()
private let tabs: [String] = (0 ..< 14).map { "TabItem # \($0)" }
override func setupUI() {
view.addSubviews(tabBarView, containerView)
embedChildViewController(tabViewController, container: containerView)
}
override func setupLayout() {
LayoutConstraint.withFormat("|-[*]-|", forEveryViewIn: containerView, tabBarView).activate()
LayoutConstraint.withFormat("V:|-[*]-[*]-|", tabBarView, containerView).activate()
}
override func setupHandlers() {
tabBarView.eventHandler = { [weak self] in
switch $0 {
case .select(let item):
self?.tabViewController.process(item: item)
}
}
}
override func setupDefaults() {
tabBarView.tabs = tabs
if let item = tabs.first {
tabBarView.select(item: item)
tabViewController.process(item: item)
}
}
}
类TabViewController
预先配置了样式。未指定
class TabViewController: GenericTabViewController<String> {
override func viewDidLoad() {
super.viewDidLoad()
transitionOptions = []
tabStyle = .unspecified
}
func process(item: String) {
if index(of: item) != nil {
select(itemIdentifier: item)
} else {
let vc = TabContentController(content: item)
let tabItem = GenericTabViewItem(identifier: item, viewController: vc)
addTabViewItem(tabItem)
select(itemIdentifier: item)
}
}
}
下面是它的样子:
class TabViewController: GenericTabViewController<String> {
override func viewDidLoad() {
super.viewDidLoad()
transitionOptions = []
tabStyle = .unspecified
}
func process(item: String) {
if index(of: item) != nil {
select(itemIdentifier: item)
} else {
let vc = TabContentController(content: item)
let tabItem = GenericTabViewItem(identifier: item, viewController: vc)
addTabViewItem(tabItem)
select(itemIdentifier: item)
}
}
}
class TabBarCollectionView: CollectionView {
override func setupUI() {
isSelectable = true
allowsMultipleSelection = false
allowsEmptySelection = false
backgroundView = View(backgroundColor: .magenta)
backgroundColors = [.clear]
}
}
class TabBarScrollView: ScrollView {
override func setupUI() {
borderType = .noBorder
backgroundColor = .clear
drawsBackground = false
horizontalScrollElasticity = .none
verticalScrollElasticity = .none
automaticallyAdjustsContentInsets = false
horizontalScroller = InvisibleScroller()
}
}
// Disabling scroll view indicators.
// See: https://stackoverflow.com/questions/9364953/hide-scrollers-while-leaving-scrolling-itself-enabled-in-nsscrollview
private class InvisibleScroller: Scroller {
override class var isCompatibleWithOverlayScrollers: Bool {
return true
}
override class func scrollerWidth(for controlSize: NSControl.ControlSize, scrollerStyle: NSScroller.Style) -> CGFloat {
return CGFloat.leastNormalMagnitude // Dimension of scroller is equal to `FLT_MIN`
}
override func setupUI() {
// Below assignments not really needed, but why not.
scrollerStyle = .overlay
alphaValue = 0
}
}
class TabBarTabViewItem: CollectionViewItem {
private lazy var titleLabel = Label().autolayoutView()
override var isSelected: Bool {
didSet {
if isSelected {
titleLabel.font = Font.semibold(size: 10)
contentView.backgroundColor = .red
} else {
titleLabel.font = Font.regular(size: 10.2)
contentView.backgroundColor = .blue
}
}
}
override func setupUI() {
view.addSubviews(titleLabel)
view.wantsLayer = true
titleLabel.maximumNumberOfLines = 1
}
override func setupDefaults() {
isSelected = false
}
func configure(title: String) {
titleLabel.text = title
titleLabel.textColor = .white
titleLabel.alignment = .center
}
override func setupLayout() {
LayoutConstraint.withFormat("|-[*]-|", titleLabel).activate()
LayoutConstraint.withFormat("V:|-(>=4)-[*]", titleLabel).activate()
LayoutConstraint.centerY(titleLabel).activate()
}
}
class TabContentController: ViewController {
let content: String
private lazy var titleLabel = Label().autolayoutView()
init(content: String) {
self.content = content
super.init()
}
required init?(coder: NSCoder) {
fatalError()
}
override func setupUI() {
contentView.addSubview(titleLabel)
titleLabel.text = content
contentView.backgroundColor = .green
}
override func setupLayout() {
LayoutConstraint.centerXY(titleLabel).activate()
}
}