专栏首页macOS 开发学习Mac开发跬步积累(三):被忽略的 NSTabViewController

Mac开发跬步积累(三):被忽略的 NSTabViewController

图片来自网络

从名字上看,NSTabViewController 很容易让熟悉iOS开发的人联想到UITableviewController,但是它在行为上更像是iOS中另外一个常用的控制器UITabBarController

0x00: NSTabViewController 简介

NSTabViewControllermacOS 10.10 之后推出的一个UI层级的控制器,可以通过使用多个Tab标签来管理多个子业务控制器,实现业务分离.

Apple 官方描述 NSTabViewController作为一个容器业务控制器,可以管理多个页面,并且一次仅显示一个页面

  1. 我们先看一个简单的示例效果:

NSTabViewController的 四种 Style

  • NSTabViewController 有四种显示样式,可以通过tabStyle属性进行设置,它是一个枚举类型,具体效果如上图;
extension NSTabViewController {
    @available(OSX 10.10, *)
    public enum TabStyle : Int {
        /// Uses an NSSegmentedControl to show the UI for the tabs. The control is on the top of the view.
        case segmentedControlOnTop
        /// Uses an NSSegmentedControl to show the UI for the tabs. The control is on the bottom of the view.
        case segmentedControlOnBottom
        /// Automatically pushes the tabs into the window's toolbar as toolbar items, if non-nil. This style will cause the TabViewController to set its containing window's toolbar to its own and become that toolbar's delegate. The toolbar items can be customized or supplemented by overriding the relevant NSToolbarDelegate methods.
        case toolbar
        /// NSTabViewController will not provide any of its own tab control UI. Separate UI, such as a NSSegmentedControl or NSPopupButton, can be easily bound to the TabViewController. Or \c tabView.tabViewType can be changed for the TabView itself to draw the UI.
        case unspecified
    }
}
  1. NSTabViewController提供了默认的切换子控制器的转场效果:Crossfade

子控制器的转场切换效果 Crossfade NSTabViewController提供了一个枚举属性transitionOptions可以设置切换转场效果

 open var transitionOptions: NSViewController.TransitionOptions

关于NSViewController.TransitionOptions详细效果可以参看Mac开发跬步积累(二):NSViewController 转场动画精耕细作

0x01: NSTabViewController设置更多Style

NSTabViewControllertabStyle属性仅提供了4种样式,但实际开发中可能会需要下图中的两种情况(居左/居右)

切换栏居左/居右

我们使用tabViewtabViewType代替NSTabViewController的样式设置,即可实现更多的样式设置效果.

  1. 使用Storyboard设置:

storyboard 设置 tabView 的 type

  1. 使用代码设置:
import Cocoa
class TabViewController: NSTabViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        1. 先设置NSTableView的样式为unspecified 
        tabStyle = .unspecified  
        2. 设置tabView的type样式 居左
        tabView.tabViewType = .leftTabsBezelBorder
    }
}

从代码设置中可以看出一个事实: NSTabViewController的最终样式是由NSTabViewController的tabStyle属性与tabViewtabViewType属性值共同作用的效果; 我们可以使用下面这段代码来验证这个事实:

import Cocoa
class TabViewController: NSTabViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        1. 设置显示在顶部
        tabStyle = .segmentedControlOnTop      
        2. 设置显示在左边
        tabView.tabViewType = .leftTabsBezelBorder  
    }
}

实现效果如图(同时显示顶部和左边):

同时显示顶部和左边

0x03: NSTabViewController的应用场景

无论在macOS系统中或者在其他应用中,NSTabViewController都有广泛的使用场景

NSTabViewController的应用场景

每个macOS App 几乎都有一个功能: 偏好设置,如果偏好设置中的选项比较少,一个页面就足够展示,这种情况使用一个NSViewController就可以实现效果了,但通常来讲,我们希望自己的App能提供给用户更多的选项设置,以便于用户可以更多的进行个性化选择功能,这时候就属于NSTabViewController用武之地

0x04: 用NSTabViewController实现偏好设置功能(敲黑板~划重点)

我们先看一下系统Finder的偏好设置,然后我们通过NSTabViewController来模仿类似的效果来强化对NSTabViewController的学习.

  1. 系统Finder 偏好设置的切换效果:

系统Finder 偏好设置

  • 需求点: 在NSTabViewController切换业务控制器时,需要动态的调整所在window尺寸
  1. 效果实现: 要在NSTabViewController切换选项时,动态的计算窗口size,并根据实际size设置window的尺寸,我们需要通过创建一个继承NSTabViewController的子类重写tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?)即可.
import Cocoa

class TabViewController: NSTabViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
        super.tabView(tabView, didSelect: tabViewItem)
        
        guard let itemView = tabViewItem?.view,
            let window = view.window
            else {return}
        let oldFrame = window.frame
        
        let newViewSize = itemView.fittingSize
        
        var newFrame = window.frameRect(forContentRect: NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, newViewSize.width, newViewSize.height))
        
        let newY = oldFrame.origin.y - ( newFrame.size.height - oldFrame.size.height)
        newFrame.origin = NSMakePoint(oldFrame.origin.x, newY)
        
        NSAnimationContext.runAnimationGroup({ (context) in
            window.animator().setFrame(newFrame, display: window.isVisible)
        }, completionHandler: nil)
    }
}

划重点 如果你实现的效果与预期的不同,那么一定是你在子业务控制器中少写了下面这行代码

   self.preferredContentSize = view.frame.size
  1. 最终实现效果:

实现效果

  1. Demo Github 地址: Day31- NSTabViewController

0x05: NSTabViewController 小结

  • NSTabViewController 支持的样式有4种;
  • 实现更多的样式,需要使用tabViewtabViewType枚举;
  • NSTabViewControllerviewNSView,它里面包含一个NSTabViewNSSegmentedControl(样式为segmentedTop/segmentedBottom时)
  • NSTabViewController的样式结果由NSTabViewControllertabStyle属性与tabViewtabViewType属性值共同作用的
  • 切换子业务控制器时,会触发方法tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?)

0x06: One more thing .....

NSTabViewController的非ToolBar样式时如果需要实现特殊的选项卡效果,需要自定义NSSegmentedControl. 关于NSViewNSViewController的相关基础,有兴趣的同学可以参考macOS 开发基础视频教程中的项目代码(地址在文章中有链接)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Mac开发跬步积累(六): 响应链NSResponder Chain

    响应链是一种消息处理机制,它是由一组有序的响应者对象组成的链条.当消息进入响应链条后,由响应者对象依次判断是否能够处理该消息,当一个响应者对象不能处理此条消息时...

    代码行者
  • Mac开发跬步积累(四):ImageIO解析Gif 图像数据

    这里可以看到Gif 是保存了多幅图像的一个图像文件,有了这个基础认识,我们就可以使用代码来解析Gif图像了.

    代码行者
  • Mac开发跬步积累(五): Dark Mode下适配你的UI界面

    在macOS 10.9+ 的时候,苹果就提供了NSAppearance这个类来协助AppKit管理App的UI控件. NSAppearance决定着AppKit...

    代码行者
  • Mac开发跬步积累(二):NSViewController 转场动画精耕细作

    在macOS 10.10之后,关于NSViewController,苹果公司专门在一个extension中提供了四个方法用来处理控制器之间的关系以及切换转场处理...

    代码行者
  • Mac开发跬步积累(一):Cocoa Drawing 之 NSImage imageNamed: 到底做了什么?

    首先,NSImage提供了支持多种格式图像数据进行管理的api, 但是NSImage对被其管理的实际图像数据几乎是一无所知的,这是因为NSImage并没有直接与...

    代码行者
  • 5.python中文编码

    python到目前为止,一共有两个版本,分别是2.x和3.x版本,根据官方正式通知2020年停止对python更新和维护,距离今天还有110天左右,所以正在学习...

    猿说编程[Python和C]
  • 5.python中文编码

    python到目前为止,一共有两个版本,分别是2.x和3.x版本,根据官方正式通知2020年停止对python更新和维护,距离今天还有110天左右,所以正在学习...

    猿说编程[Python和C]
  • 38.python print函数

    python中内置函数我们使用的最频繁的莫过于print函数,重helloword开始,我们就一直在接触print,虽然使用简单,不过你真的会玩print函数吗...

    猿说编程[Python和C]
  • python 文件读写操作

    以前的代码都是直接将数据输出到控制台,实际上我们也可以通过读/写文件的方式读取/输出到磁盘文件中,文件读写简称I/O操作。文件I/O操作一共分为四部分:打开(o...

    猿说编程[Python和C]
  • 24.python 文件读写操作

    以前的代码都是直接将数据输出到控制台,实际上我们也可以通过读/写文件的方式读取/输出到磁盘文件中,文件读写简称I/O操作。文件I/O操作一共分为四部分:打开(o...

    猿说编程[Python和C]
  • 关于“量变”和“质变”的圣贤语录与辩证思考

    夫风生于地,起于青苹之末。风起于青萍之末,浪成于微澜之间。千里之堤,毁于蚁穴。千万不要小瞧了微小细节,勿以恶小而为之,勿以善小而不为。

    一个会写诗的程序员
  • 3.Pycharm设置开发模板/字体大小/背景颜色

    选择 File –> setting –> Editor –> Font ,可以看到如上界面,可以根据自己的喜好随意调整字体大小,字体风格,文字行间距,设置之后...

    猿说编程[Python和C]
  • python 字符串

    在 python变量 文章中我们对python变量做了一个简单的了解,整数/浮点数/bool值相对来讲都比较简单,今天详细在讲解一下关于字符串的内容,字符串俗称...

    猿说编程[Python和C]
  • 12.python 字符串

    在 python变量 文章中我们对python变量做了一个简单的了解,整数/浮点数/bool值相对来讲都比较简单,今天详细在讲解一下关于字符串的内容,字符串俗称...

    猿说编程[Python和C]
  • 33.python basestring函数

    该函数是 str 和 unicode 的父类,是抽象类,因此不能被调用和实例化,但可以被用来判断一个对象是否为 str 或者 unicode 的实例,语法如下:

    猿说编程[Python和C]
  • 46.python GIL锁与互斥锁Lock的区别

    前面的文章分别介绍了python线程互斥锁Lock 和 python GIL锁,两个对 python线程threading 都会有影响,那么具体又有什么区别呢?

    猿说编程[Python和C]
  • 职业发展规划及个人能力要求

    软件工程职业发展路线:项目经理 --> 技术经理 --> 架构师 --> 首席技术官CTO

    凹谷
  • 29.python 线程互斥锁Lock

    在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是...

    猿说编程[Python和C]
  • python 线程互斥锁Lock

    在前一篇文章 python线程创建和传参 中我们介绍了关于python线程的一些简单函数使用和线程的参数传递,使用多线程可以同时执行多个任务,提高开发效率,但是...

    猿说编程[Python和C]

扫码关注云+社区

领取腾讯云代金券