Apple - Control and Cell Programming Topics

本文翻译整理自:Control and Cell Programming Topics(Updated: 2008-10-15
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ControlCell/ControlCell.html#//apple_ref/doc/uid/10000015-SW1



一、Cocoa的控制和单元编程主题简介

控件和单元格实现用户界面对象,如按钮、文本字段和滑块。

本主题包含以下子主题:

按钮编程主题用户交互界面对象,单击时向目标发送操作消息。
图像视图编程主题一种用户交互界面对象,在框架中显示单个图像,并可选择允许用户将图像拖到其中。
滑块编程主题一种用户交互界面对象,显示一系列值,并具有指示当前设置的指示器或旋钮。
文本字段显示用户可以选择或编辑的文本的用户交互界面对象。
框编程主题一个用户交互界面对象,它可以在自己周围画一个边框并为自己命名。
进度指标编程主题显示正在进行的冗长任务的用户交互界面对象。
状态栏编程主题用户交互界面对象,显示与用户交互或向用户提供反馈的项目集合。
浏览器编程主题提供用户交互界面,用于显示和选择数据列表或分层组织的数据列表(如目录路径)中的项目。
矩阵编程指南用于创建以各种方式协同工作的单元格组的用户交互界面对象。
表单编程主题一组相关的文本字段。
组合框编程主题一种用户交互界面对象,为用户提供两种输入值的方式:直接在文本字段中输入,或从预先选择的值的弹出列表中选择。
适用于Mac的表格视图编程指南一个用户交互界面对象,显示一组相关记录的数据,行表示单个记录,列表示这些记录的属性。
选项卡视图编程主题用户交互界面对象提供了在多个页面中提供信息的便捷方式。
大纲视图编程主题一种表,允许用户展开或折叠包含分层数据的行。
文本视图文本视图是Cocoa文本系统的主要用户交互界面对象。
步进编程主题由两个小箭头组成的用户交互界面对象,可以增加和减少出现在它旁边的值,例如日期或时间。
搜索字段用户交互界面对象,为搜索提供标准用户交互界面。
分段控制编程指南一种用户交互界面对象,其外观和行为类似于分成多个段的水平按钮。

本文件的组织

控件和单元格实现用户界面对象,如按钮、文本字段和滑块。
控件负责

  • 显示控件自己
  • 拦截用户事件(例如单击按钮或移动滑块)
  • 向其他对象发送操作,通常是为了响应用户事件(例如在滑块移动时更改变量的值或在按下按钮时执行命令。)

控件通常将前两个职责委托给单元格。
拆分这些职责可以更容易地创建具有许多相同元素(如电子表格)或几个不同元素(如下拉列表,允许您在文本字段或从预先选择的字符串菜单中输入字符串)的控件。

以下是概念:

  • 关于单元格和控件提供了有关NSCell和NSControl类的基本信息。
  • 控件和单元格如何交互提供了有关控件和单元格如何交互以及它们如何操作的更多信息。
  • 单元状态 描述了控件可以具有的三种状态:开、关或混合。
    虽然主要由NSButton使用,但状态是在NSCell中定义的,因此未来的子类可以使用它们。
  • 表示对象说明如何将单元格与其表示的对象相关联。

以下是任务:


二、关于Cell和控制

本主题提供有关NSCell和NSControl的基本信息。


1、关于NSCell

NSCell类提供了一种在NSView中显示文本或图像的机制,而不需要完整的NSView子类的开销。
特别是,它通过提供对应用程序中所有NSCell实例使用的共享NSText对象的访问来提供NSText类的大部分功能。
NSCell对于在NSView的自定义子类中的不同位置放置文本或图像也非常有用。

大多数NSControl类都大量使用NSCell来实现其内部工作。
例如,NSSlider使用NSSliderCell,NSTextField使用NSTextFieldCell,NSBrowser使用NSBrowserCell。
向NSControl发送消息通常比直接处理相应的NSCell更简单。
例如,NSControlls通常在更改单元格属性后调用updateCell:(导致单元格显示);而如果您直接调用NSCell的相应方法,NSCell可能不会自动再次显示自己。

NSControl的一些子类(特别是NSMatrix)将NSCell以某种协作方式组合在一起。
因此,使用NSMatrix,您可以实现一组大小一致的单选按钮,而无需为每个按钮设置NSView(也无需NSText对象作为每个按钮上文本的字段编辑器)。

NSCell类提供了用于显示文本或图像、编辑文本、设置和获取对象值、维护状态、突出显示和跟踪鼠标的原语。
NSCell的trackMouse:inRect:ofView:untilMouseUp:方法 实现了向目标对象发送动作消息的机制。
然而,NSCell抽象地实现了目标/动作特性,将实现的细节推迟到NSActionCell及其子类。


2、关于NSControl

NSControl是一个抽象超类,它为实现用户界面设备提供了三个基本特性。
首先,作为NSView的子类,NSControl绘制或协调设备的屏幕表示。
其次,它通过覆盖NSResponse der的mouseDown:方法并提供响应器链中的位置来接收和响应其边界内的用户生成事件。
第三,它实现了sendAction:to:方法来向NSControl的目标对象发送操作消息。
Application Kit中定义的NSControl子类是NSBrowser、NSButton(及其子类NSPopUpButton)、NSColorWell、NSImageView、NSMatrix(及其子类NSForm)、NSScroller、NSSlider、NSTableView和NSTextField。
具体NSControl子类的实例通常简称为控件。


三、Controls和Cells如何交互

控件通常与一个或多个单元格相关联——抽象类NSCell的子类的实例。
控件的单元格(或多个单元格)通常正好适合控件的边界。
单元格是可以绘制自己并响应事件的对象,但它们只能根据控件的指示间接这样做,控件充当一种协调背景。

控件管理其单元格的行为。
通过从NSView继承,控件派生出响应用户操作并呈现其屏幕表示的能力。
当用户单击控件时,它会通过发送trackMouse:inRect:ofView:untilMouseUp:到被单击的单元格来响应。
收到此消息后,单元格跟踪鼠标,并可能让控件将单元格的操作消息发送到其目标(在鼠标向上时或连续发送,具体取决于单元格的属性)。
当控件收到显示请求时,它们会反过来向单元格发送一个或多个单元格的drawWithFrame:inView:消息,让单元格自己绘制。

控件和单元格的这种关系使两件事成为可能:一个控件可以管理不同类型、具有不同目标和操作的单元格(见下文),一个控件可以管理多个单元格。
大多数Application Kit控件,如NSButton和NSTextFields,只管理一个单元格。
但是一些控件,特别是NSMatrix和NSForm,管理多个单元格(通常具有相同的大小和属性,并以规则的模式排列)。
因为单元格比控件更轻,就继承的数据和行为而言,使用多单元格控件比使用多个控件更有效。

NSControl的许多方法——特别是设置或获取值和属性的方法——在NSCell中有相应的方法。
向控件发送消息会导致它被转发到控件的单元或(如果是多单元控件)其选定的单元。
然而,许多NSControl方法仅在具有单个单元的控件中有效(这些在方法描述中注明)。

NSControl子类不需要使用NSCell子类来实现自己——NSScroller和NSColorWell就是NSControl不需要的例子。
然而,这些子类必须处理NSCell否则会处理的细节。
具体来说,它们必须覆盖设计用于处理单元格的方法。
此外,缺少单元格意味着您无法利用NSMatrix功能来管理多单元阵列,例如单选按钮。


四、Represented Objects

Represented Objects 是NSCell所代表的对象。
(不要将它们与NSCell的对象值混淆,后者是单元格的值。)
通过为NSCell设置表示对象(使用setRepresentedObject:),您可以在NSCell和该对象之间建立关联。
例如,您可以有一个弹出列表,其中每个单元格都列出一种颜色作为其标题;当用户选择单元格时,表示的NSColor对象将以颜色显示。
此功能完全是为了开发人员的方便。
单元格本身不使用表示对象,除了归档和恢复它。


五、Cell States

对于NSCell的某些子类,例如NSButtonCell,对象的值就是它的状态。
它可以有两种状态——NSOnStateNSOffState——或者三种状态——NSOnStateNSOffStateNSMixedState
混合状态对于复选框或单选按钮很有用,这些复选框或单选按钮反映了仅适用于应用程序或当前选择中某些项目的功能状态。
例如,假设一个复选框将所选文本设为粗体。
如果所有选定的文本都为粗体,则打开。
如果没有一个选定的文本是粗体,则关闭。
如果文本有粗体和纯文本的组合,则混合。
现在假设您单击复选框。
如果您打开它,所有文本都将变为粗体。
如果您关闭它,所有文本都将变为纯文本。
如果选择混合状态,文本将保持原样。

默认情况下,NSCell有两种状态。
您可以使用方法setAllowsMixedState:setAllowsMixedState:要直接设置按钮的状态,请使用setState:
要循环遍历所有可用状态,请使用setNextState


六、操作Cell和控制

本文包含操作单元格和控件的各种提示和示例。


1、设置单元格或控件的大小

要将单元格(以及任何封闭的单元格控件)的大小设置为符合人机界面指南的最佳大小,请执行以下操作:

  1. 如果单元格包含文本,请将文本的字体设置为与三种标准大小之一一致:常规、小和迷你。
    为此,请使用NSFont类方法systemFontSizeForControlSize:
    此方法的参数是NSControl类声明的NSControlSize常量。
float fontSize = [NSFont systemFontSizeForControlSize:NSMiniControlSize];
NSCell *theCell = [theControl cell];
NSFont *theFont = [NSFont fontWithName:[[theCell font] fontName] size:fontSize];
[theCell setFont:theFont];

  1. 使用相同的常量将控件大小设置为与字体大小相同的大小。
    使用NSControlsetControlSize:方法。
[theCell setControlSize:NSMiniControlSize];

  1. 最后,将sizeToFit发送到控件以修剪额外的宽度。
[theControl sizeToFit];

2、在单元格边界内绘制焦点环

NSSetFocus usRingStyleNSSetFocusRingStyle``NSFocusRingPlacement类型的常量来告诉应用程序工具包在图像上、文本下或形状周围绘制焦点环(当文本或图像都不是考虑因素时)。
您可以使用此函数和常量NSFocusRingOnly在单元格边界内绘制焦点环。

例1显示了如何绘制这样一个焦点环。
它要求您重写NSCelldrawWithFrame:inView:
在这个方法中,如果单元格应该绘制第一响应者状态的证据,设置焦点环的矩形,调用NSSetFocusRingStyle,参数为NSFocusRingOnly,然后创建并填充一个定义该矩形的贝塞尔路径。
在这种情况下,填充只是绘制环。

**注意:**此技术要求您对单元格类进行子类化。
有关信息,请参阅“子类化NSCell”。


例1在单元格边界内绘制焦点环

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    // other stuff might happen here
    if ([self showsFirstResponder]) {
         // showsFirstResponder is set for us by the NSControl that is drawing  us.
        NSRect focusRingFrame = cellFrame;
        focusRingFrame.size.height -= 2.0;
        [NSGraphicsContext saveGraphicsState];
        NSSetFocusRingStyle(NSFocusRingOnly);
        [[NSBezierPath bezierPathWithRect: NSInsetRect(focusRingFrame,4,4)] fill];
        [NSGraphicsContext restoreGraphicsState];
    }
     // other stuff might happen here
}


3、在单元格中定位图像

如果单元格要显示图像而不是文本(或除了文本之外),您可以通过覆盖NSCell和NSMenuItemCell类声明的imageRectForBounds:方法来影响图像在单元格中的位置。
此方法返回绘制单元格图像的矩形,该矩形通常是与单元格边界略有偏移的矩形。
例2给出了一个示例。

**注意:**此技术要求您对单元格类进行子类化。
有关信息,请参阅“子类化NSCell”。


例2在单元格中居中图像

- (NSRect) imageRectForBounds:(NSRect)theBounds {
    NSRect r = theBounds;
    // get size. If no image, result of method returning NSSize is undefined so assume NSZeroSize
    NSSize imageSize = [self image] != nil ? [[self image] size] : NSZeroSize;
    r.origin.x = floor((r.size.width/2)  - (imageSize.width/2)  + 0.5);
    r.origin.y = floor((r.size.height/2) - (imageSize.height/2) + 0.5);
    r.size     = imageSize;
    return r;
}

在此示例中,单元格将图像居中。
请注意,它将返回值四舍五入到最近的像素,以避免使用部分像素偏移绘制可能导致的模糊。
代码还将返回矩形的大小字段设置为图像的大小,以便在矩形中正确绘制(假设NSImage对象使用drawInRect:fromRect:operation:fraction:用于绘制而不是compositeToPoint:operation:)。


七、验证控制条目

NSControl提供了委托方法control:isValidObject:用于验证嵌入在控件中的单元格的内容(特别是NSTextField和NSMatrix的实例)。
在验证时,您可以检查作为对象允许但在给定上下文中不受欢迎的值,例如日期永远不应该在未来的日期字段,或者对特定状态有效的邮政编码。

方法control:isValidObject:在插入点离开单元格(即,关联控件放弃第一响应者状态)但在显示单元格对象的字符串值之前调用。
返回YES以允许显示字符串,返回NO以拒绝显示并将光标返回单元格。
以下Objective-C示例评估对象(NSDate)并在日期在未来时拒绝它:

- (BOOL)control:(NSControl *)control isValidObject:(id)obj
{
    if (control == contactsForm) {
        if (![obj isKindOfClass:[NSDate class]]) return NO;
        if ([[obj laterDate:[NSDate date]] isEqual:obj]) {
            NSRunAlertPanel(@"Date not valid",
                @"Reason: date in future", NULL, NULL, NULL);
            return NO;
        }
    }
    return YES;
}

NSControl为其允许文本编辑的子类提供了几个委托方法,例如NSTextField和NSMatrix。
当控件单元格的格式化程序无法格式化字符串(control:didFailToFormatString:errorDescription:)或拒绝部分字符串条目(control:didFailToValidatePartialString:errorDescription:)时,会调用一些委托方法。
NSControl还提供control:textView:doCommandBySelector:,它允许委托有机会检测和响应键绑定,例如complete:(名称完成)。
请注意,虽然NSControl定义了委托方法,但它本身并没有委托。
任何使用委托方法的子类都必须包含委托以及获取和设置它的方法。


八、显示Cell 值

每个显示文本的NSCell都有一个与之关联的值。
NSCell对象将该值存储为潜在的任何类型的对象,将其显示为NSString对象,并根据请求(intValuefloatValuestringValue等)将其作为主值或字符串对象返回。
格式是与NSCell对象关联的对象(通过setFormatter:),将单元格的对象值转换为其文本表示,并将用户键入的内容转换为底层对象。
NSCell对象具有内置的格式化程序来处理常见的字符串和数字(intfloatdouble)转换。
此外,您可以实现自己的格式化程序来提供专门的对象转换;请参阅*数据格式指南*。

一个NSCell对象显示和存储的文本可以是一个属性字符串。
有几种方法可以帮助设置和获取属性字符串值,包括setAttributedStringValue:setImportsGraphics:


九、更改控件的Cell

因为NSControl使用从NSCell类派生的对象来实现其大部分功能,所以您通常可以通过创建NSCell而不是NSControl的子类来实现独特的用户交互界面设备。
例如,假设您希望应用程序的所有NSSlider都具有通用NSSliderCell以外的单元类型。
首先,您创建NSCell、NSActionCell或NSSliderCell的子类。
(我们称之为MyCellSubclass。)
然后,您可以简单地调用NSSlider的setCellClass类方法:

[NSSlider setCellClass:[MyCellSubclass class]];

此后创建的所有NSSlider都将使用MyCellSubclass,直到您再次调用setCellClass:

如果您想在与使用MyCellSubclass的自定义NSSlider相同的应用程序中创建通用NSSlider(使用NSSliderCell的NSSlider),有两种可能的方法。
一种是调用setCellClass:如上所述,每当您要创建自定义NSSlider时,然后将单元类重置为NSSliderCell。
另一种方法是创建自动使用MyCellSubclass的NSSlider自定义子类,如NSControl类参考中所述。


十、使用连续控件

当用户按住鼠标按钮时,连续控件定期发送其操作消息。
例如,当用户移动旋钮时,连续滑块重复发送其操作消息,当用户按下按钮时,连续按钮重复发送其操作消息。
如果控件不是连续的,它只在用户松开鼠标后才发送其操作消息。

要找出或更改控件是否连续:发送isContinuoussetContinuous:到它的单元格。
默认情况下,滑块是连续的,而其他控件不是。

要了解连续控件发送其操作消息的频率,请使用方法getPeriodicDelay:interval:
,它返回以下内容:

  • 周期性延迟是连续控件在开始定期向目标对象发送操作消息之前将暂停的时间量(以秒为单位)。
    它取自用户的默认值。
    如果用户尚未设置,则为0.4秒。
  • 间隔是消息之间的时间量(以秒为单位)。
    默认情况下,它取自用户的默认值。
    如果用户尚未设置,则为0.075秒。

(在Java中,使用periodicDelayinterval这两种方法。)

如果您使用的是按钮,请使用setPeriodicDelay:interval:以编程方式更改这些值。
如果您使用的是另一种类型的控件,则必须子类化控件的单元类以编程方式更改它们。


十一、子类化NSCell

initImageCell: 方法是显示图像的NSCell的指定初始化程序。
initTextCell: 方法是显示文本的NSCell的指定初始化程序。
如果您实现了执行其自身初始化的NSCell子类,请覆盖这些方法中的一个或两个。
如果您需要使用目标和操作行为,您可能更喜欢子类NSActionCell或其子类之一,它们提供了此行为的默认实现。

如果您想实现自己的鼠标跟踪或鼠标向上行为,请考虑覆盖startTrackingAt:inView:continueTracking:at:inView:stopTracking:at:inView:mouseIsUp:
如果您想实现自己的绘图,请覆盖drawWithFrame:inView:drawInteriorWithFrame:inView:

如果子类包含包含指向对象的指针的实例变量,请考虑重写copyWithZone:以复制对象。
默认版本仅复制指向对象的指针。


十二、子类化NSControl

如果您要创建一个自定义的NSControl类来执行它自己的初始化,您应该覆盖指定的初始化程序(initWithFrame:)。
但是,请注意,当Application Kit控件类的子类的实例从笔尖取消归档时,不会调用此方法。

如果您创建与自定义单元格子类配对的自定义控件子类——例如,NSSlider的自定义子类和NSSliderCell的自定义子类——您有两种方法可以将该自定义单元格的实例与自定义控件的实例相关联。
第一种方法要求您拥有Interface Builder版本3。
在Interface Builder中,当您将控件放置在窗口上时,控件及其单元格将被实例化,并且当您保存用户交互界面时,这些对象(以及其他放置的对象)将被编码并序列化为nib文件。
Interface Builder还可以帮助您定义自定义子类,包括框架控件类的子类,例如NSButtonNSSlider
Interface Builder还允许您将放置的控件对象的类更改为自定义控件类,但该应用程序的早期版本无法对与控件对象关联的自定义单元格执行相同操作。

但是Interface Builder 3.0版允许您设置控件单元格的类。
为此,请单击控件选择它并右键单击鼠标(在单键鼠标上单击控件)。
然后在出现的弹出列表的右上角再次单击。
子菜单列出了鼠标指针下的对象——包括控件的单元格。
对于NSSliderCell对象,如图1所示,子菜单包括一个“滑块单元格”项。
选择单元格项。

图1在Interface Builder中选择控件的单元格
在这里插入图片描述


接下来打开选定单元格对象的检查器窗口。
找到“对象检查器”部分,在自定义类组合框中选择(或输入)自定义单元格类的名称。

图2设置控件的自定义单元格类

在这里插入图片描述


如果Interface Builder 3.0版不可用,您仍然可以使用编程方法将自定义单元格的实例分配给自定义控件,如例1所示。
在自定义控件子类中,当所有对象都从nib文件中取消归档时,创建自定义单元格的实例并为其分配当前单元格的所有相关属性。
然后使用NSControlsetCell:方法将自定义单元格设置为控件的单元格。


例1为自定义控件创建和设置自定义单元格

- (void)awakeFromNib {
    MySliderCell *newCell = [[MySliderCell alloc] init];
    id oldCell = [self cell];
    [newCell setImage:[oldCell image]];
    [newCell setMinValue:[oldCell minValue]];
    [newCell setMaxValue:[oldCell maxValue]];
    [newCell setSliderType:[oldCell sliderType]];
    // ....  set other slider cell attributes
    [self setCell:newCell];
    [newCell release];
}

每当控件需要为自己创建一个新单元格时,您都可以覆盖cellClass方法——例如,如果它使用initWithFrame:实例化。
initWithFrame:方法使用cellClass的返回值来分配和初始化正确类型的NSCell对象。


例2 覆盖cellClass

+ (Class) cellClass
{
    return [MySliderCell class];
}

请注意,重写NSControlcellClass类方法不会更改从nib文件中未归档的单元格对象的类。


十三、使用系统控制色调

macOS允许用户使用系统首选项中的外观窗格设置窗口、菜单和控件显示中使用的颜色。
这种颜色称为控制色调。
应用程序套件提供的用户界面元素会根据当前控制色调自动修改其外观。

自定义控件和应用程序用户交互界面的其他方面也可以使用控件色调,如图1所示。

图1控制色调感知自定义控制单元的示例

在这里插入图片描述


1、获取系统控制色调和颜色

您可以使用NSColor类方法currentControlTint获取当前系统范围的控制色调。
此方法返回一个表示当前选择的外观颜色的NSControlTint
当前NSBlueControlTintNSGraphiteControlTint分别是表示Aqua和Graphite外观的可能返回值。

确定了当前的NSControlTint值后,您现在可以使用NSColor类方法colForControlTint:colorForControlTint:

应用程序可以通过注册为NSControlTintDidChangeNotification通知的观察者来检测用户何时改变系统范围的外观设置。


2、与视图和图像的交互

您的应用程序可能想要修改NSView子类内容的外观,或更改NSControl子类使用的图像类型以响应系统范围的外观更改。
为此,您的应用程序必须注册NSControlTintDidChangeNotification、更新适当的元素并重新绘制视图。

在例1中的示例演示了如何确定当前的控制色调并更改NSButton和NSImageView中显示的图像。
实现此方法的对象先前已注册为NSControlTintDidChangeNotification通知的观察者,此方法作为选择器。


例1示例,它使用当前的ControlTint设置NSButton和NSImageView的内容

- (void)systemTintChangedNotification:(NSNotification *)notification;
{
    NSString *tintImageName;
 
    // compare the result of [NSColor currentControlTint]
    // with the supported tint values, defaulting to Aqua
    if ([NSColor currentControlTint] == NSGraphiteControlTint)
        tintImageName=@"GraphiteImage";
    else
        tintImageName=@"AquaImage";
 
    [exampleButton setImage:[NSImage imageNamed:tintImageName]];
    [exampleImageView setImage:[NSImage imageNamed:tintImageName]];
 
}


3、与NSCell子类的交互

当系统范围的外观发生变化时,任何NSCell对象都会自动重新绘制。
NSCell子类有责任确定当前的控制色调,作为其实现drawWithFrame:inView:的一部分。

在例2中的示例演示了如何实现一个色彩感知的drawWithFrame:inView:方法。
它确定单元格是否使用系统范围的外观色调,或者是否已显式设置其色调,然后选择适当的图像进行显示。
还确定了用于绘制时钟指针的适当控制色调颜色。


例2一个色彩感知NSCell的示例

- (void)drawWithFrame:(NSRect)cellFrame
               inView:(NSView *)controlView
{
    // if we're not the front window, we'll resort to using the
    // special NSClearControlTint value
    NSControlTint currentTint;
    if ([[controlView window] isKeyWindow])
        currentTint = [self controlTint];
    else
        currentTint= NSClearControlTint;
 
    // If the NSCell's control tint has been overridden
    // using the setControlTint: method we should use
    // the value returned by [self controlTint] as this
    // controls authoritative tint. If the tint is
    // NSDefaultControlTint then this cell should use the
    // system-wise appearance value, and we use the value
    // returned by the NSColor +currentControlTint method.
    if ([self controlTint] == NSDefaultControlTint)
        currentTint=[NSColor currentControlTint];
    else
        currentTint=[self controlTint];
 
 
 
    // and change the image used in drawing according to
    // the currentTint
    // Use the Aqua image as the default image
    NSImage *clockFaceImage;
    switch (currentTint) {
        case NSGraphiteControlTint:
            clockFaceImage = [NSImage imageNamed: @"ClockFace-Graphite"];
            break;
        case NSClearControlTint:
            clockFaceImage = [NSImage imageNamed: @"ClockFace-Clear"];
            break;
        case NSBlueControlTint:
        default:
            clockFaceImage = [NSImage imageNamed: @"ClockFace-Aqua"];
            break;
    }
 
    float clockRadius = MIN(NSHeight(cellFrame), NSWidth(cellFrame));
 
    // Draw the clock face (draw it flipped
    // if we are in a flipped view, like NSMatrix).
    [clockFaceImage setFlipped:[controlView isFlipped]];
    [clockFaceImage drawInRect:NSMakeRect(NSMinX(cellFrame),
                                          NSMinY(cellFrame),
                                          clockRadius,clockRadius)
                      fromRect:NSMakeRect(0,0,
                                          [clockFaceImage size].width,
                                          [clockFaceImage size].height)
                     operation:NSCompositeSourceOver
                      fraction:1.0];
 
    // get the color for the currentTint and use it for
    // drawing the hands on the clock face
    NSColor *tintColor=[NSColor colorForControlTint:currentTint];
 
    // Draw the clock hour and minute hands.
    [self drawClockHandsForTime:time
                      withFrame:cellFrame
                         inView:controlView
                     usingColor:tintColor];
}

2024-06-16(日)

  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI工程化

请我喝杯伯爵奶茶~!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值