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
  2. 使用代码设置:

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. 最终实现效果:


    实现效果
  2. 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 开发基础视频教程中的项目代码(地址在文章中有链接)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_x阅读 15,968评论 3 119
  • 老爸晚上打了贝贝一顿。 贝贝是我家养的一条松狮。事情的经过这样的:晚上吃过晚饭,我看天这么热,就把贝贝从笼子里放出...
    又见一刀阅读 197评论 0 1
  • 2016年6月1日 溽暑中的深圳这一天格外地热。 坐在轮椅里的郑铁,着格子长袖衫,头发灰白稀疏但能看出仍然经过精心...
    淨淨阅读 313评论 0 0