首页 > 其他分享 >15. NSView 视图与 NSViewController 视图控制器

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

时间:2024-11-01 12:47:39浏览次数:3  
标签:self NSView 视图 let func NSViewController view

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 模糊化效果

有两种方式可以实现:

  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 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

相关文章

  • flaks 钩子函数 | 中间件 | 内置对象 | Flask类视图和RESTfu
    什么是钩子(中间件Middleware)钩子或叫钩子函数,是指在执行函数和目标函数之间挂载的函数,框架开发者给调用方提供一个point-挂载点,是一种AOP切面编程思想,常用的钩子函数before_first_request:处理第一次请求之前执行,before_request:在每次请求之前执行,通常使用这个钩子函......
  • 响应式项目(RxJS+Vue.js+Spring)大决战(5):主页的实现(前端视图模块)
    书接上篇:响应式项目(RxJS+Vue.js+Spring)大决战(4):主页的实现(后端服务模块)5.2前端视图模块5.2.1整体结构的设计        前端模块app-view/home负责主页视图的建构,其结构如下图所示:        本篇所述方法,体现了极强的独特性、技巧性! 5.2.2主页home.html ......
  • 界面控件DevExpress WPF中文教程:Data Grid——卡片视图概述
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。无论是Office办公软件的衍伸产品,还是以数据为中心......
  • 选择使用通用视图(Generic Views)或视图集(ViewSets)
    在DjangoRESTFramework中,通用视图和视图集都可以用于快速构建RESTAPI。它们各自有不同的适用场景,下面将详细介绍如何在两者之间进行选择。通用视图(GenericViews)通用视图提供了一些常用的、独立的视图类,如ListCreateAPIView、RetrieveUpdateDestroyAPIView等,用于处理增删改......
  • Rollup 同步物化视图
    同步物化视图|StarRockshttps://docs.starrocks.io/zh/docs/using_starrocks/Materialized_view-single_table/同步物化视图本文介绍如何在StarRocks中创建、使用以及管理同步物化视图(Rollup)。同步物化视图下,所有对于基表的数据变更都会自动同步更新到物化视图中。您无需......
  • SQL Server创建用户只能访问指定数据库和视图
    我们在给数据库用户赋予权限时,有时候不想让该用户看到太多过程表和过程视图,这时就需要限定用户的访问权限第一步:创建用户创建数据库连接后,进入安全性——登录名,单击右键,新建登录名,并设置默认数据库第二步:设置用户映射点击用户映射,勾选指定要访问的数据库,数据库成员身份默认......
  • 数据库对视图的学习
    视图目录视图什么是视图视图的作用视图操作创建视图更新视图查看视图删除视图视图规则与限制什么是视图MySQL中的视图(View)是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含行和列,但视图本身不包含数据。视图中的数据是存储在基础表中的数据。视图的作用简化复杂查询:......
  • 10.29 视图
    数据库之视图(一)视图的介绍=============================一、什么是视图?视图是一个虚拟表,它是一个虚拟表,它不在数据库中以存储的形式保存(本身不包含数据),是在使用视图的时候动态生成。二、视图的优点?1、提高查询效率数据库中的数据查询非常复杂,可以简化sql语句2、安全有些......
  • 数据库之视图
    一、什么是视图?视图是一个虚拟表,它是一个虚拟表,它不在数据库中以存储的形式保存(本身不包含数据),是在使用视图的时候动态生成。二、视图的优点?1、提高查询效率数据库中的数据查询非常复杂,可以简化sql语句2、安全有些保密字段,可以通过创建视图限制用户对某些字段进行操作3、简......
  • 第12课—数据库之视图
    数据库之视图(一)视图的介绍=============================一、什么是视图?视图是一个虚拟表,它是一个虚拟表,它不在数据库中以存储的形式保存(本身不包含数据),是在使用视图的时候动态生成。二、视图的优点?1、提高查询效率数据库中的数据查询非常复杂,可以简化sql语句2、安全有些......