欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 15. NSView 视图与 NSViewController 视图控制器

15. NSView 视图与 NSViewController 视图控制器

2025/2/24 20:21:38 来源:https://blog.csdn.net/liudonglovehemin/article/details/142720100  浏览:    关键词:15. NSView 视图与 NSViewController 视图控制器

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

    //创建一个xibfunc 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 模糊化效果

有两种方式可以实现:

  1. 在UI设置面板中把 Custom View 的实现类替换成 NSVisualEffectView;
  2. 拖动一个Visual Effect View 控件到设计视图中,然后在属性面板中修改配置属性;
    在这里插入图片描述

自定义实现

创建NSViewController 子类

  1. 先删除掉main.storyboard中的视图;
  2. 拖动一个view controller到设计面板中,并从window controller 拖动到view controller,绑定连接;
  3. 创建一个NSWindowController子类;
  4. 设置上面拖动的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 Cocoaclass 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! NSViewControllerlet vc2 = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "SecondVC")) as! NSViewControllerself.addChildViewController(vc1)self.addChildViewController(vc2)}

实现子视图切换

给combo添加事件

    @IBAction func switchViewAction(_ sender: NSComboBox) {let selectedIndex = sender.indexOfSelectedItemself.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? NSViewControllerself.presentViewControllerAsModalWindow(presentVC!)}@IBAction func presentAsSheetAction(_ sender: NSButton){let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewControllerself.presentViewControllerAsSheet(presentVC!)}@IBAction func presentAsPopoverAction(_ sender: NSButton){let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewControllerself.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? NSViewControllerlet animator = PresentCustomAnimator()self.presentViewController(presentVC!, animator: animator)}@IBAction func showAction(_ sender: NSButton){let presentVC = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "PresentVC")) as? NSViewControllerlet 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 = fromViewControllerlet topVC = viewControllertopVC.view.wantsLayer = truetopVC.view.alphaValue = 0bottomVC.view.addSubview(topVC.view)topVC.view.layer?.backgroundColor = NSColor.gray.cgColorNSAnimationContext.runAnimationGroup( {context incontext.duration = 0.5topVC.view.animator().alphaValue = 1}, completionHandler:nil)}func animateDismissal(of viewController: NSViewController, from fromViewController: NSViewController) {let topVC = viewControllerNSAnimationContext.runAnimationGroup( {context incontext.duration = 0.5topVC.view.animator().alphaValue = 0}, completionHandler: { topVC.view.removeFromSuperview() } )}
}

设计器实现视图切换效果

storyboard设计

就是用UI设计的方式实现,术语称为Segue Control,方法是拖动按钮到新视图上面,这样省去了绑定事件这一步,如下图所示:
在这里插入图片描述
设置完成后的效果如下图所示:
在这里插入图片描述

自定义 segue 效果

对于动画效果,需要自定义Segue,然后设置为自定义实现类。

import Cocoaclass CustomSegue: NSStoryboardSegue {override func perform(){let sourceViewController      = self.sourceController as! NSViewController;let destinationViewController = self.destinationController as! NSViewControllerlet 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 viewNSAppearance.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.systemRedNSColor.systemBlueNSColor.systemGrayNSColor.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 = selfself.view.window?.makeKeyAndOrderFront(self)}

实现手势识别协议

实现 NSGestureRecognizerDelegate 代理协议的magnify方法

extension ViewController: NSGestureRecognizerDelegate {@objc func magnify(_ sender: NSMagnificationGestureRecognizer) {switch  sender.state {case .began:print("ClickGesture began")breakcase .changed:print("ClickGesture changed")breakcase .ended:print("ClickGesture ended")breakcase .cancelled:print("ClickGesture cancelled")breakdefault : break}}
}

这样在视图上进行上述触摸板操作,就可以识别动作了。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词