图片来自网络
从名字上看,
NSTabViewController很容易让熟悉iOS开发的人联想到UITableviewController,但是它在行为上更像是iOS中另外一个常用的控制器UITabBarController
NSTabViewController是macOS 10.10之后推出的一个UI层级的控制器,可以通过使用多个Tab标签来管理多个子业务控制器,实现业务分离.
Apple 官方描述
NSTabViewController作为一个容器业务控制器,可以管理多个页面,并且一次仅显示一个页面
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
}
}NSTabViewController提供了默认的切换子控制器的转场效果:Crossfade子控制器的转场切换效果 Crossfade
NSTabViewController提供了一个枚举属性transitionOptions可以设置切换转场效果
open var transitionOptions: NSViewController.TransitionOptions关于NSViewController.TransitionOptions详细效果可以参看Mac开发跬步积累(二):NSViewController 转场动画精耕细作
NSTabViewController的tabStyle属性仅提供了4种样式,但实际开发中可能会需要下图中的两种情况(居左/居右)
切换栏居左/居右
我们使用tabView的tabViewType代替NSTabViewController的样式设置,即可实现更多的样式设置效果.
storyboard 设置 tabView 的 type
import Cocoa
class TabViewController: NSTabViewController {
override func viewDidLoad() {
super.viewDidLoad()
1. 先设置NSTableView的样式为unspecified
tabStyle = .unspecified
2. 设置tabView的type样式 居左
tabView.tabViewType = .leftTabsBezelBorder
}
}从代码设置中可以看出一个事实: NSTabViewController的最终样式是由NSTabViewController的tabStyle属性与tabView的tabViewType属性值共同作用的效果;
我们可以使用下面这段代码来验证这个事实:
import Cocoa
class TabViewController: NSTabViewController {
override func viewDidLoad() {
super.viewDidLoad()
1. 设置显示在顶部
tabStyle = .segmentedControlOnTop
2. 设置显示在左边
tabView.tabViewType = .leftTabsBezelBorder
}
}实现效果如图(同时显示顶部和左边):
同时显示顶部和左边
无论在macOS系统中或者在其他应用中,NSTabViewController都有广泛的使用场景
NSTabViewController的应用场景
每个macOS App 几乎都有一个功能: 偏好设置,如果偏好设置中的选项比较少,一个页面就足够展示,这种情况使用一个NSViewController就可以实现效果了,但通常来讲,我们希望自己的App能提供给用户更多的选项设置,以便于用户可以更多的进行个性化选择功能,这时候就属于NSTabViewController的用武之地了
我们先看一下
系统Finder的偏好设置,然后我们通过NSTabViewController来模仿类似的效果来强化对NSTabViewController的学习.
系统Finder 偏好设置
NSTabViewController切换业务控制器时,需要动态的调整所在window尺寸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实现效果
NSTabViewController 支持的样式有4种;tabView的tabViewType枚举;NSTabViewController的view是NSView,它里面包含一个NSTabView和NSSegmentedControl(样式为segmentedTop/segmentedBottom时)NSTabViewController的样式结果由NSTabViewController的tabStyle属性与tabView的tabViewType属性值共同作用的tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) 在NSTabViewController的非ToolBar样式时如果需要实现特殊的选项卡效果,需要自定义NSSegmentedControl.
关于NSView与NSViewController的相关基础,有兴趣的同学可以参考macOS 开发基础视频教程中的项目代码(地址在文章中有链接)