NSView视图对象
视图与窗口
- NSViewController视图控制器负责管理NSView的生命周期,同时也会管理所有子视图控制器,以便实现不同视图的切换,同时需要区分下NSWindow和NSView的区别,视图这东西比较复杂。
- NSViewController不能独立显示,必须做为NSWindow(view)的子视图或NSWindowController(contentViewController)的属性才能显示,同时它本身又可以管理多个子视图实现显示的切换,如下:
视图生命周期
视图生命周期如下:
load阶段
主要是加载xib或storyboard文件
- loadView (1)
- viewDidLoad(2)
appear阶段
- viewWillAppear(3)
- viewDidAppear(4)
layout阶段
比如窗口伸缩等,都会触发下面的事件
- updateViewConstraints(7)
- viewWillLayout(5)
- viewDidLayout(6)
disappler阶段
- viewWillDisappear(8)
- viewDidDisapper(9)
正常显示执行顺序:1、2、3、4、5、6
添加了constraints后显示执行顺序:1,2,3,7,5,6,5,6,最后多执行的5和6是由于RunLoop界面重绘触发的
关闭执行顺序:8、9
NSView与NSViewController
采用storyboard创建项目时,默认的工程结构如下图所示,这里的View Controller是可以修改为自定义子类实现的:比如点击删除,然后拖动一个ViewController到设计面板中,再绑定Window和View。
修改NSWindowController类实现
此时可以把Main.storyboard文件中的ViewController Scene删除掉了(见上图),我们后面用程序动态修改。注意需要绑定Main.storyboard的class到自定义的类上。
class CustomWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
//self.createByViewController()
//self.createByStoryboard()
//self.createByCode()
}
}
接下来尝试用不同的方法实现ViewController的创建;
通过XIB创建
创建一个Cocoa类,语言选择swift再勾选上创建xib文件。
class ViewByController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}
实例化 ViewByController
//创建一个xib
func createByViewController(){
//默认会加载与Controller同名的xib文件
let viewController = ViewByController(nibName: NSNib.Name(rawValue: "ViewByController"), bundle: nil)
self.contentViewController = viewController
}
通过storyboard创建
只需要创建一个.storyboard文件即可,不需要swift,后续可创建一个NSViewController子类,然后替换下列代码实现:
//创建一个storyboard文件
func createByStoryboard() {
let sbName = NSStoryboard.Name(rawValue: "ViewByStoryboard")
let storyboard = NSStoryboard(name:sbName, bundle: nil)
let myViewController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "ViewByStoryboard"))
self.contentViewController = myViewController as? NSViewController
}
通过code编程创建
编码实现,创建一个.swift文件,不需要xib或storyboard。
class ViewByCode: NSViewController {
override func loadView() {
// super.loadView()
let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
let view = NSView(frame: frame)
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
实例化 ViewByCode
//手工创建
func createByCode() {
let viewController = ViewByCode()
self.contentViewController = viewController
}
NSViewController内置了一个名为 representedObject 的属性对象,作用是用来存储 NSViewController 的模型对象。
NSView 模糊化效果
有两种方式可以实现:
- 在UI设置面板中把 Custom View 的实现类替换成 NSVisualEffectView;
- 拖动一个Visual Effect View 控件到设计视图中,然后在属性面板中修改配置属性;
自定义实现
创建NSViewController 子类
- 先删除掉main.storyboard中的视图;
- 拖动一个view controller到设计面板中,并从window controller 拖动到view controller,绑定连接;
- 创建一个NSWindowController子类;
- 设置上面拖动的view controller的类实现为自定义的子类;
class CustomViewController: NSViewController {
// 添加的视图控件,做为内容显示区
@IBOutlet weak var mainView: NSVisualEffectView!
//当前子视图
var currentController: NSViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
}
创建 NSView 子类
虽然NSViewController默认包含了一个名为view
的NSView对象,但有时也可以通过扩展自定义视图以实现更多功能,比如框架提供的:
- NSView:基类
- NSScrollView:带滚动条的视图
- ImageView:图像视图
import Cocoa
class CustomView: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
Swift.print("CustomView draw")
}
override func updateConstraints(){
Swift.print("CustomView updateConstraints")
super.updateConstraints()
}
override func display() {
Swift.print("CustomView display")
super.display()
}
override func layout() {
Swift.print("CustomView layout")
super.layout()
}
}
管理 NSViewController 视图控制器
NSViewController除了做为UI控件的管理器,还可以做为门面用来管理子视图控制,相关的接口/属性定义如下:
- parentViewController:父视图控制器指针;
- childViewController:子视图控制器们指针;
- (void)addChildViewController:增加子视图控制器
- (void)removeFromParentViewController:从父视图控制器中删除自己;
在UI设计面板中添加几个view controller,并定义storyboard ID,如下:
接下来实现通过下拉列表切换视图显示:
加载子视图控制器
在自定义的 CustomViewController.swift 类中按名称加载子视图控制器;
func initiateControllers() {
let vc1 = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "FirstVC")) as! NSViewController
let vc2 = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "SecondVC")) as! NSViewController
self.addChildViewController(vc1)
self.addChildViewController(vc2)
}
实现子视图切换
给combo添加事件
@IBAction func switchViewAction(_ sender: NSComboBox) {
let selectedIndex = sender.indexOfSelectedItem
self.changeViewController(selectedIndex)
}
定义 changeViewController()方法
func changeViewController(_ index: NSInteger) {
if currentController != nil {
currentController?.view.removeFromSuperview()
}
//数组越界保护
guard index >= 0 && index <= self.childViewControllers.count-1 else {
return
}
currentController = self.childViewControllers[index]
self.mainView.addSubview((currentController?.view)!)
//autoLayout约束
let topAnchor = currentController?.view.topAnchor.constraint(equalTo: self.mainView.topAnchor, constant: 0)
let bottomAnchor = currentController?.view.bottomAnchor.constraint(equalTo: self.mainView.bottomAnchor, constant: 0)
let leftAnchor = currentController?.view.leftAnchor.constraint(equalTo: self.mainView.leftAnchor, constant: 0)
let rightAnchor = currentController?.view.rightAnchor.constraint(equalTo: self.mainView.rightAnchor, constant: 0)
NSLayoutConstraint.activate([topAnchor!, bottomAnchor!, leftAnchor!, rightAnchor!])
}
编码实现视图切换效果
这里主要说明下视图切换时的效果,比如:
- modal:以模态窗口方式出现;
- sheel:从顶部滑出;
- popover:以弹出层方式出现;
- animator:动画效果,遮盖父窗口,需要实现动画类;
- show:视图切换;
自定义子视图
这个示例中,我们只定义一个视图就可以,上面添加一个按钮用于关闭本视图;
class ChildViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func closeView(_ sender: Any) {
if (self.presenting != nil) {
self.dismiss(self)
} else {
self.view.window?.close();
}
}
}
添加切换显示样式
即给上面5个按钮添加不同的事件
@IBAction func presentAsModalAction(_ sender: NSButton){
let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewController
self.presentViewControllerAsModalWindow(presentVC!)
}
@IBAction func presentAsSheetAction(_ sender: NSButton){
let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewController
self.presentViewControllerAsSheet(presentVC!)
}
@IBAction func presentAsPopoverAction(_ sender: NSButton){
let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewController
self.presentViewController(presentVC!, asPopoverRelativeTo: sender.frame, of: self.view, preferredEdge: .minY, behavior:.transient )
}
@IBAction func presentAsAnimatorAction(_ sender: NSButton){
let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewController
let animator = PresentCustomAnimator()
self.presentViewController(presentVC!, animator: animator)
}
@IBAction func showAction(_ sender: NSButton){
let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewController
let toVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "toVC")) as? NSViewController
//增加 2个子视图控制器
self.addChildViewController(presentVC!)
self.addChildViewController(toVC!)
//显示 presentVC 视图
self.view.addSubview((presentVC?.view)!)
// 从 presentVC 视图 切换到另外一个 toVC 视图
self.transition(from: presentVC!, to: toVC!, options: NSViewController.TransitionOptions.crossfade , completionHandler: nil)
}
自定义视图切换动画效果
自定义一个动画效果
//渐变动画
class PresentCustomAnimator: NSObject,NSViewControllerPresentationAnimator {
func animatePresentation(of viewController: NSViewController, from fromViewController: NSViewController) {
let bottomVC = fromViewController
let topVC = viewController
topVC.view.wantsLayer = true
topVC.view.alphaValue = 0
bottomVC.view.addSubview(topVC.view)
topVC.view.layer?.backgroundColor = NSColor.gray.cgColor
NSAnimationContext.runAnimationGroup( {context in
context.duration = 0.5
topVC.view.animator().alphaValue = 1
}, completionHandler:nil)
}
func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) {
let topVC = viewController
NSAnimationContext.runAnimationGroup( {context in
context.duration = 0.5
topVC.view.animator().alphaValue = 0
}, completionHandler: { topVC.view.removeFromSuperview() } )
}
}
设计器实现视图切换效果
storyboard设计
就是用UI设计的方式实现,术语称为Segue Control,方法是拖动按钮到新视图上面,这样省去了绑定事件这一步,如下图所示:
设置完成后的效果如下图所示:
自定义 segue 效果
对于动画效果,需要自定义Segue,然后设置为自定义实现类。
import Cocoa
class CustomSegue: NSStoryboardSegue {
override func perform(){
let sourceViewController = self.sourceController as! NSViewController;
let destinationViewController = self.destinationController as! NSViewController
let animator = PresentCustomAnimator()
sourceViewController.presentViewController(destinationViewController , animator: animator)
}
}
通过上述设置,其效果同编码效果一样。
Dark模式设计
使用 NSAppearance 对象设计
override func viewDidLoad() {
super.viewDidLoad()
/** 设置view的appearance 效果 */
/**
NSAppearance.Name.Aqua : Light 默认设置
NSAppearance.Name.darkAqua : Dark 模式
NSAppearance.Name.vibrantDark : 仅可用于 Visual effect view
NSAppearance.Name.accessibilityHighContrastDarkAqua : 高对比的Dark 模式 (通常用于image)
NSAppearance.Name.accessibilityHighContrastVibrantLight : 高对比的毛玻璃 效果 ,用于visual effec view;
*/
/** 此次对view 的appearance进行赋值是无效的,因为 window的生命周期方法尚未执行 (具体可参考基础课程视频或项目代码)*/
// view.appearance? = NSAppearance.init(named: NSAppearance.Name.aqua)!
// print("\(view.effectiveAppearance.name.rawValue)")
/** 1. 颜色硬编码设置视图背景色 : 这种情况下,无论是light 或者dark 模式,颜色都是固定的值,不会根据主题进行适配 */
// adaptedView.layer?.backgroundColor = NSColor.red.cgColor
/** 2. 使用Asset 中的color 进行light /dark 之间的颜色适配: 切换light和dark时,需要重新开启应用 */
adaptedView.layer?.backgroundColor = NSColor(named: "Color")?.cgColor
/** 3. 使用带有语意的NSColor */
// adaptedView.layer?.backgroundColor = NSColor.labelColor.cgColor;
// adaptedView.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor;
myLabel.textColor = NSColor.labelColor
/** 可适配的系统颜色
NSColor.systemRed
NSColor.systemBlue
NSColor.systemGray
NSColor.systemPink
*/
_ = NSImage(size: NSMakeSize(0, 0), flipped: true) { (rect) -> Bool in
/** 返回值表示图片是否创建成功*/
return true
}
}
视图手势识别
NSGestureRecognize r定义了手势识别的基本接口,即触摸板功能。可以识别以下手势:
- NSclickGestureRecognizer:点击
- NSPanGestureRecognizer:滑动
- NSPressGestureRecognizer:按住
- NSMagnificationGestureRecognizer :缩放
- NSRotationGestureRecognizer:旋转
添加手动识别功能
注意:手动识别是增加到NSView上面的,在NSViewController中包含了一个NSView对象,所以代码可以写在NSViewController中:
override func viewDidLoad() {
super.viewDidLoad()
//创建手势类
let gr = NSMagnificationGestureRecognizer(target: self, action: #selector(ViewController.magnify(_:)))
//视图增加手势识别
self.view.addGestureRecognizer(gr)
gr.delegate = self
self.view.window?.makeKeyAndOrderFront(self)
}
实现手势识别协议
实现 NSGestureRecognizerDelegate 代理协议的magnify方法
extension ViewController: NSGestureRecognizerDelegate {
@objc func magnify(_ sender: NSMagnificationGestureRecognizer) {
switch sender.state {
case .began:
print("ClickGesture began")
break
case .changed:
print("ClickGesture changed")
break
case .ended:
print("ClickGesture ended")
break
case .cancelled:
print("ClickGesture cancelled")
break
default : break
}
}
}
这样在视图上进行上述触摸板操作,就可以识别动作了。
标签:self,NSView,视图,let,func,NSViewController,view From: https://blog.csdn.net/liudonglovehemin/article/details/142720100