macOS开发基础- UI体系

2,068 阅读6分钟

UI控件是应用开发的基础,无论是iOS还是macOS,任何一个界面都是由一个个UI控件堆积而成,控件是应用与用户交互的核心。iOS的UI控件隶属于UIKit,而macOS的UI控件都属于AppKit框架。学习和掌握cocoa框架系统中AppKit提供的常用控件的属性设置、对事件的响应和处理都是macOS开发的基础。

1、macOS下的控件分类

  • 文本类

主要用来展示文字信息或获取用户输入。 常见的有文本标签(Label)、文本输入(TextField)、多行文本输入框(TextView)、组合框(Combo Box)等。

  • 按钮类

主要用来响应用户的一个动作,或者对用户的点击选择做出响应,包括基本的按钮(Button)、单选按钮(Radio)、复选框(Check Box)、弹出按钮(Pop Up Button)等。

  • 图像类

用来展示图片的图像视图(NSImageView)。

  • 菜单/工具栏/dock

包括菜单(Menu)和工具栏(ToolBar)。多个分类功能集中展示,接受用户的鼠标点击动作,做出事件响应。

  • 容器类

包括基本视图(View)、滚动条视图(Scroll View)、分组视图(Box)、选项卡视图(Tab View)、分栏视图(Split View)。其中选项卡视图和分栏视图可以将界面控件分类展示在不同的区域。滚动条视图可以滚动展示视图的不同区域的内容。

  • 窗口类

弹出式窗口,包括窗口(Window)和各种面板(Panel)。

  • 多数据展示类

以数据源代理模式统一管理多行/列数据信息,包括表视图(Table View)、大纲视图(Outline View)、组合框(Combo Box)和集合视图(Collection View)。

2、AppKit的UI族谱

image.png

  • 所有控件的根类都继承自NSObject。
  • NSMenu、NSToolBar、NSCell类看似像一个UI控件,但是它是直接继承自NSObject,不可以直接 当UI控件使用。
    • 例如NSMenu,需要配合NSMenuItem使用,NSMenu相当于一个容器,里面装了好多NSMenuItem。
  • 绝大多数控件继承自NSView,NSView继承自NSResponser。
  • 部分控件继承自NSResponser。
  • Cell相关类继承自NSCell。

2.1、NSResponser

NSResponser针对各类型的消息。定义了键盘、鼠标、触控板等事件响应的抽象方法,设定了事件响应的“响应者链”处理机制。 NSApplication、NSWindow、NSWindowController、NSView、NSViewController构成了基本的响应者处理对象。响应者链再不同的场景下根据既定的处理顺序来传递和处理,对每一类事件,系统都会根据当前的对象来构造可能接受事件的队列(响应者链),当事件发生时,从队头依次遍历查找,如果找到了能实现事件响应方法的对象则终止后续查找,将事件发送到此,结束此事件的相应处理。 响应链消息传递的原则是上层对象优先得到事件响应(倒序便利)。比如子视图和父视图同时定义了鼠标事件响应mouseDown的处理函数,则子视图的mouseDown优先得到执行。

  • NSResponser主要提供了响应事件的基础能力:

image.png

对标iOS中的UIResponser:

image.png

  • 前者是mouse鼠标交互,后者是touch触摸交互。

2.2、NSView

类似iOS中的UIView,NSView是macOS中最重要的UI基础组件类,与CALayer配合一起负责控件的图形化界面绘制。

2.3、NSControl

NSControl类处理消息事件中的用户动作类消息,如文本输入、按钮点击、菜单工具栏事件等。

image.png

  • NSControl间接继承自NSResponser,既有事件的处理能力,又有消息处理能力。

2.4、NSCell

NSCell可以理解为对NSControl更细粒度的控制。对NSCell的定制可以实现对类继承自NSControl的控件界面的改变和定制,大多数NSView子类对应的控件并不是由NSView来完成界面绘制和事件响应处理的,而是由内部的Cell类完成的。

当NSControl有一组界面对象时,使用NSCell统一管理性能更高。例如,在Radio控件中,可以使用NSCell统一完成界面绘制处理,而不需要对每个Radio单独绘制给一个Label作为标题,以及绘制一个Button作为单选按钮。

控件通常与一个或多个单元格相关联,这些单元格式抽象类NSCell的子类的实例。控件的一个或者多个单元格通常恰好位于控件的边界之内。NSCell是可以绘制自身并响应事件的对象,但是他们只能根据其控件的指示而间接的这样做,这是一种协调的背景。

控件管理其Cell的行为。通过从NSView继承,控件可以响应用户的操作并呈现其在屏幕上的表示。当用户单击控件时,它会通过发送trackMouse:inRect:ofView:untilMouseUp:到所单击的Cell部分响应。收到此消息后,cell会跟踪鼠标,并使控件将cell的操作消息发送到其目标(鼠标上移或者连续发送,取决于单元的属性)。当控件收到显示请求时,它们又向其一个或者多个cell发送一条drawWithFrame:inView:消息,以使这些cell进行绘制。

控件与cell之间的关系:控件可以用不同的cell管理不同的target和action。单个控件可以管理多个cell。大多数AppKit控件(如NSButton和NSTextFields)仅管理单个cell。但是某些控件(尤其是NSMatrix和NSForm)可以管理多个cell(通常具有相同的大小和属性,并以规则的方式排列)。由于cell比控件轻,因此在继承数据和行为方面,使用多单元控件比使用多个控件更有效。

NSControl的许多方法(尤其是设置或取值和属性的方法)在NSCell中都有相应的方法。将消息发送到控件会导致将其转发到控件的单元格(如果是多单元格控件,则转发到其选定的单元格)。但是,许多NSControl方法仅在具有单个单元格的控件中有效(这些方法说明中已经提到)。

NSControl子类不必使用NSCell子类来实现自身。NSScroller和NSColorWell是不需要NSControls的示例。但是,此类子类必须注意NSCell否则会处理细节,具体来说,它们必须重写设计用于单元的方法,而且,缺少单元格意味着您无法利用NSMatrix功能来管理多单元格阵列,例如单选按钮。

一个简单的例子帮助理解:用NSButtonCell初始化NSButton
  • 初始化NSButtonCell
NSButtonCell *cell = [[NSButtonCell alloc] init];
cell.title = @"这是一个title";
cell.backgroundColor = [NSColor redColor];
  • 初始化NSButton
NSButton *button1 = [[NSButton alloc] init];
NSButton *button2 = [[NSButton alloc] init];
NSButton *button3 = [[NSButton alloc] init];
NSButton *button4 = [[NSButton alloc] init];
  • 将NSButtonCell赋值给NSButton的cell属性
button1.cell = cell;
button2.cell = cell;
button3.cell = cell;
button4.cell = cell;
  • 运行后的效果

image.png

  • NSButtonCell相当于一个模版,批量生成来4个NSButton。
  • NSButtonCell模版要比直接初始化4个NSButton效率高,因为NSButtonCell继承自NSObject,而NSButton继承自NSView,不需要重复的去操作一个NSView的属性。
  • 类似NSCell的子类可以分别用于不同的控件,运用得当会有非常美妙的效果。

3、总结

本篇文章主要介绍了macOS的UI体系,旨在帮助你对macOS开发有个入门且相对完整的认知。 此系列文章是本人学习macOS开发的参考。原地址没有链接,无法引用。欢迎各位大佬指点。


参考: