NSCollectionView 中的选择突出显示

发布于 2024-08-26 20:50:11 字数 3880 浏览 9 评论 0原文

我有一个正在运行的 NSCollectionView,但有一个小但重要的异常。获取并突出显示集合中选定的项目。

在 Snow Leopard 之前,我已经完成了所有这些工作,但有些东西似乎发生了变化,我无法完全确定它,所以我将我的 NSCollectionView 直接带回到基本测试并遵循Apple 用于创建 NSCollectionView 的文档位于:

http:// /developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/CollectionViews/Introduction/Introduction.html

按照快速入门指南,集合视图可以正常工作。但是,本指南除了“有一些功能,例如合并图像视图、将对象设置为可选或不可选以及在选择它们时更改颜色”之外,不讨论选择。

以此为例,我进入下一步,使用控制器键 selectionIndexes 将数组控制器绑定到 NSCollectionView,认为这会绑定我在NSCollectionView 和数组控制器,从而触发 KVO 通知。我还将 NSCollectionView 设置为在 IB 中可选择。

NSCollectionView 似乎没有选择委托,并且与大多数 Cocoa UI 视图不同,似乎没有默认的选定突出显示。

所以我的问题实际上归结为一个相关的问题,但却是两个不同的问题。

  1. 如何捕获项目的选择?
  2. 如何突出显示某个项目?

NSCollectionView 的编程指南似乎很少,而且大多数通过 Google 的搜索似乎都会提取 Snow Leopard 之前的实现,或者使用单独的 XIB 文件中的视图。

对于后者(视图的单独 XIB 文件),我不明白为什么这应该是一个先决条件,否则我会怀疑 Apple 不会将视图包含在与集合视图项相同的包中。

我知道这将是一个“只见树木,不见森林”的问题——所以我已经做好了“呸!”的准备。片刻。

像往常一样,非常感谢任何和所有的帮助。

更新 1

好的,所以我想找到所选的项目,但还没有想出突出显示。对于对计算所选项目感兴趣的人(假设您遵循 Apple 指南):

在控制器(在我的测试用例中的应用程序代理)中,我添加了以下内容:

In awakeFromNib

[personArrayController addObserver:self
       forKeyPath:@"selectionIndexes" 
       options:NSKeyValueObservingOptionNew
       context:nil];

新方法< /em>

-(void)observeValueForKeyPath:(NSString *)keyPath 
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
{
    if([keyPath isEqualTo:@"selectionIndexes"])
    {
        if([[personArrayController selectedObjects] count] > 0)
        {
            if ([[personArrayController selectedObjects] count] == 1)
            {
                personModel * pm = (PersonModel *) 
                       [[personArrayController selectedObjects] objectAtIndex:0];
                NSLog(@"Only 1 selected: %@", [pm name]);
            }
            else
            {
                // More than one selected - iterate if need be
            }
        }
    }

不要忘记对非 GC 进行释放

-(void)dealloc
{
    [personArrayController removeObserver:self 
                               forKeyPath:@"selectionIndexes"];
    [super dealloc];
}

仍在寻找高亮分辨率...

更新 2

采纳了 Macatomy 的建议,但仍然遇到问题。发布相关的类方法来看看我哪里出错了。

MyView.h

#import <Cocoa/Cocoa.h>

@interface MyView : NSView {
    BOOL selected;
}

@property (readwrite) BOOL selected;

@end

MyView.m

#import "MyView.h"

@implementation MyView

@synthesize selected;

-(id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }
    return self;
}

-(void)drawRect:(NSRect)dirtyRect
{
    NSRect outerFrame = NSMakeRect(0, 0, 143, 104);
    NSRect selectedFrame = NSInsetRect(outerFrame, 2, 2);

    if (selected)
        [[NSColor yellowColor] set];
    else
        [[NSColor redColor] set];

    [NSBezierPath strokeRect:selectedFrame];
}

@end

MyCollectionViewItem.h

#import <Cocoa/Cocoa.h>
@class MyView;

@interface MyCollectionViewItem : NSCollectionViewItem {

}

@end

"MyCollectionViewItem.m*

#import "MyCollectionViewItem.h"
#import "MyView.h"

@implementation MyCollectionViewItem

-(void)setSelected:(BOOL)flag
{

    [(MyView *)[self view] setSelected:flag];
    [(MyView *)[self view] setNeedsDisplay:YES];
}

@end

I have a working NSCollectionView with one minor, but critical, exception. Getting and highlighting the selected item within the collection.

I've had all this working prior to Snow Leopard, but something appears to have changed and I can't quite place my finger on it, so I took my NSCollectionView right back to a basic test and followed Apple's documentation for creating an NSCollectionView here:

http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/CollectionViews/Introduction/Introduction.html

The collection view works fine following the quick start guide. However, this guide doesn't discuss selection other than "There are such features as incorporating image views, setting objects as selectable or not selectable and changing colors if they are selected".

Using this as an example I went to the next step of binding the Array Controller to the NSCollectionView with the controller key selectionIndexes, thinking that this would bind any selection I make between the NSCollectionView and the array controller and thus firing off a KVO notification. I also set the NSCollectionView to be selectable in IB.

There appears to be no selection delegate for NSCollectionView and unlike most Cocoa UI views, there appears to be no default selected highlight.

So my problem really comes down to a related issue, but two distinct questions.

  1. How do I capture a selection of an item?
  2. How do I show a highlight of an item?

NSCollectionView's programming guides seem to be few and far between and most searches via Google appear to pull up pre-Snow Leopard implementations, or use the view in a separate XIB file.

For the latter (separate XIB file for the view), I don't see why this should be a pre-requisite otherwise I would have suspected that Apple would not have included the view in the same bundle as the collection view item.

I know this is going to be a "can't see the wood for the trees" issue - so I'm prepared for the "doh!" moment.

As usual, any and all help much appreciated.

Update 1

OK, so I figured finding the selected item(s), but have yet to figure the highlighting. For the interested on figuring the selected items (assuming you are following the Apple guide):

In the controller (in my test case the App Delegate) I added the following:

In awakeFromNib

[personArrayController addObserver:self
       forKeyPath:@"selectionIndexes" 
       options:NSKeyValueObservingOptionNew
       context:nil];

New Method

-(void)observeValueForKeyPath:(NSString *)keyPath 
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
{
    if([keyPath isEqualTo:@"selectionIndexes"])
    {
        if([[personArrayController selectedObjects] count] > 0)
        {
            if ([[personArrayController selectedObjects] count] == 1)
            {
                personModel * pm = (PersonModel *) 
                       [[personArrayController selectedObjects] objectAtIndex:0];
                NSLog(@"Only 1 selected: %@", [pm name]);
            }
            else
            {
                // More than one selected - iterate if need be
            }
        }
    }

Don't forget to dealloc for non-GC

-(void)dealloc
{
    [personArrayController removeObserver:self 
                               forKeyPath:@"selectionIndexes"];
    [super dealloc];
}

Still searching for the highlight resolution...

Update 2

Took Macatomy's advice but still had an issue. Posting the relevant class methods to see where I've gone wrong.

MyView.h

#import <Cocoa/Cocoa.h>

@interface MyView : NSView {
    BOOL selected;
}

@property (readwrite) BOOL selected;

@end

MyView.m

#import "MyView.h"

@implementation MyView

@synthesize selected;

-(id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }
    return self;
}

-(void)drawRect:(NSRect)dirtyRect
{
    NSRect outerFrame = NSMakeRect(0, 0, 143, 104);
    NSRect selectedFrame = NSInsetRect(outerFrame, 2, 2);

    if (selected)
        [[NSColor yellowColor] set];
    else
        [[NSColor redColor] set];

    [NSBezierPath strokeRect:selectedFrame];
}

@end

MyCollectionViewItem.h

#import <Cocoa/Cocoa.h>
@class MyView;

@interface MyCollectionViewItem : NSCollectionViewItem {

}

@end

"MyCollectionViewItem.m*

#import "MyCollectionViewItem.h"
#import "MyView.h"

@implementation MyCollectionViewItem

-(void)setSelected:(BOOL)flag
{

    [(MyView *)[self view] setSelected:flag];
    [(MyView *)[self view] setNeedsDisplay:YES];
}

@end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

对你再特殊 2024-09-02 20:50:11

如果不同的背景颜色足以作为突出显示,您可以简单地使用 NSBox 作为集合项视图的根项。
用您选择的突出显示颜色填充 NSBox。
将 NSBox 设置为 Custom,以便填充起作用。
将 NSBox 设置为透明。

将 NSBox 的透明度属性绑定到 File Owner(Collection Item) 的选中属性
将透明绑定的值转换器设置为 NSNegateBoolean。

我尝试附上界面生成器屏幕截图,但被拒绝了,因为我是新手:-(

If a different background color will suffice as a highlight, you could simply use an NSBox as the root item for you collection item view.
Fill the NSBox with the highlight color of your choice.
Set the NSBox to Custom so the fill will work.
Set the NSBox to transparent.

Bind the transparency attribute of the NSBox to the selected attribute of File Owner(Collection Item)
Set the value transformer for the transparent binding to NSNegateBoolean.

I tried to attach Interface builder screenshots but I was rejected bcos I'm a newbie :-(

丑丑阿 2024-09-02 20:50:11

这并不难做到。确保在 Interface Builder 中为 NSCollectionView 启用“选择”。然后在用于原型视图的 NSView 子类中,声明一个名为“selected”的属性:

@property (readwrite) BOOL selected;

此处更新代码:(添加了超级调用)

子类 NSCollectionViewItem 并覆盖 -setSelected:

- (void)setSelected:(BOOL)flag
{
    [super setSelected:flag];
    [(PrototypeView*)[self view] setSelected:flag];
    [(PrototypeView*)[self view] setNeedsDisplay:YES];
}

然后您需要添加在原型视图的 drawRect: 方法中编写代码来绘制突出显示:

- (void)drawRect:(NSRect)dirtyRect 
{
    if (selected) {
       [[NSColor blueColor] set];
       NSRectFill([self bounds]);
    }
}

这只是在选择视图时将视图填充为蓝色,但可以自定义以以您想要的任何方式绘制突出显示。我已经在自己的应用程序中使用了它,并且效果很好。

Its not too hard to do. Make sure "Selection" is enabled for the NSCollectionView in Interface Builder. Then in the NSView subclass that you are using for your prototype view, declare a property called "selected" :

@property (readwrite) BOOL selected;

UPDATED CODE HERE: (added super call)

Subclass NSCollectionViewItem and override -setSelected:

- (void)setSelected:(BOOL)flag
{
    [super setSelected:flag];
    [(PrototypeView*)[self view] setSelected:flag];
    [(PrototypeView*)[self view] setNeedsDisplay:YES];
}

Then you need to add code in your prototype view's drawRect: method to draw the highlight:

- (void)drawRect:(NSRect)dirtyRect 
{
    if (selected) {
       [[NSColor blueColor] set];
       NSRectFill([self bounds]);
    }
}

That just simply fills the view in blue when its selected, but that can be customized to draw the highlight any way you want. I've used this in my own apps and it works great.

梦过后 2024-09-02 20:50:11

如果您不为原型视图创建 NSView 子类,您也可以采用另一种方式。

在您的子类 NSCollectionViewItem 中重写 setSelected:

- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];
    if (selected)
        self.view.layer.backgroundColor = [NSColor redColor].CGColor;
    else
        self.view.layer.backgroundColor = [NSColor clearColor].CGColor;
}

当然,正如我之前所有智者所说,请确保在 Interface Builder 中为 NSCollectionView 启用“选择”。

You can also go another way, if you're not subclassing NSView for your protoype view.

In your subclassed NSCollectionViewItem override setSelected:

- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];
    if (selected)
        self.view.layer.backgroundColor = [NSColor redColor].CGColor;
    else
        self.view.layer.backgroundColor = [NSColor clearColor].CGColor;
}

And of course, as said by all the wise people before me, make sure "Selection" is enabled for the NSCollectionView in Interface Builder.

从﹋此江山别 2024-09-02 20:50:11

在您的 NSCollectionViewItem 子类中,重写 isSelected 并更改图层的背景颜色。在 macOS 10.14 和 Swift 4.2 中测试

class Cell: NSCollectionViewItem {

  override func loadView() {    
    self.view = NSView()
    self.view.wantsLayer = true
  }

  override var isSelected: Bool {
    didSet {
      self.view.layer?.backgroundColor = isSelected ? NSColor.gray.cgColor : NSColor.clear.cgColor
    }
  }
}

In your NSCollectionViewItem subclass, override isSelected and change background color of the layer. Test in macOS 10.14 and Swift 4.2

class Cell: NSCollectionViewItem {

  override func loadView() {    
    self.view = NSView()
    self.view.wantsLayer = true
  }

  override var isSelected: Bool {
    didSet {
      self.view.layer?.backgroundColor = isSelected ? NSColor.gray.cgColor : NSColor.clear.cgColor
    }
  }
}
累赘 2024-09-02 20:50:11

由于现有的答案都不适合我,所以这是我的看法。将 CollectionView 项的子类更改为 SelectableCollectionViewItem。这是它的代码。带有可绑定的 textColor 属性,用于将文本标签 textColor 绑定到。

@implementation SelectableCollectionViewItem

+ (NSSet *)keyPathsForValuesAffectingTextColor
{
    return [NSSet setWithObjects:@"selected", nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.wantsLayer = YES;
}

- (void) viewDidAppear
{
    // seems the inital selection state is not done by Apple in a KVO compliant manner, update background color manually
    [self updateBackgroundColorForSelectionState:self.isSelected];
}

- (void)updateBackgroundColorForSelectionState:(BOOL)flag
{
    if (flag)
    {
        self.view.layer.backgroundColor = [[NSColor alternateSelectedControlColor] CGColor];
    }
    else
    {
        self.view.layer.backgroundColor = [[NSColor clearColor] CGColor];
    }
}

- (void)setSelected:(BOOL)flag
{
    [super setSelected:flag];
    [self updateBackgroundColorForSelectionState:flag];
}

- (NSColor*) textColor
{
    return self.selected ? [NSColor whiteColor] : [NSColor textColor];
}

Since none of the existing answers worked super well for me, here is my take on it. Change the subclass of the CollectionView item to SelectableCollectionViewItem. Here is it's code. Comes with a bindable textColor property for hooking your text label textColor binding to.

@implementation SelectableCollectionViewItem

+ (NSSet *)keyPathsForValuesAffectingTextColor
{
    return [NSSet setWithObjects:@"selected", nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.wantsLayer = YES;
}

- (void) viewDidAppear
{
    // seems the inital selection state is not done by Apple in a KVO compliant manner, update background color manually
    [self updateBackgroundColorForSelectionState:self.isSelected];
}

- (void)updateBackgroundColorForSelectionState:(BOOL)flag
{
    if (flag)
    {
        self.view.layer.backgroundColor = [[NSColor alternateSelectedControlColor] CGColor];
    }
    else
    {
        self.view.layer.backgroundColor = [[NSColor clearColor] CGColor];
    }
}

- (void)setSelected:(BOOL)flag
{
    [super setSelected:flag];
    [self updateBackgroundColorForSelectionState:flag];
}

- (NSColor*) textColor
{
    return self.selected ? [NSColor whiteColor] : [NSColor textColor];
}
明月夜 2024-09-02 20:50:11

就我而言,我想要一个图像(复选标记)来指示对象的选择。将 ImageWell 拖到集合项笔尖。设置所需的图像并将其标记为隐藏。转到绑定检查器并将隐藏属性绑定到集合视图项。

在此处输入图像描述

(在我的例子中,我为 CollectionViewItem 创建了一个单独的笔尖,因此它绑定到文件的所有者如果情况并非如此,并且 Item 视图与 CollectionView 位于同一笔尖,则绑定到 Collection View Item)

将模型键路径设置为 selected 并将值转换器设置为 NSNegateBoolean。现在,每当选择单个单元格/项目时,图像就会可见,从而指示选择。

添加到 Alter 的答案。

将 NSBox 设置为根项。只需创建一个新的 IB 文档(例如 CollectionItem)并将 NSBox 拖到空白区域即可。现在根据需要在框中添加所有元素。现在单击“文件所有者”并将“自定义类”设置为 NSCollectionViewItem

在此处输入图像描述

在添加 NSCollectionView 的笔尖中,更改 的笔尖名称>CollectionViewItem

在此处输入图像描述

在 NSBox 中,将剩余元素绑定到 Files Owner.对于标签,它类似于:

在此处输入图像描述

现在将突出显示颜色设置为 Alter 在他的回答中提到,在“填充颜色”选项中设置所需的颜色组合,将 NSBox 设置为透明并绑定透明度属性,如下所示:

在此处输入图像描述

现在,当选择“集合视图项目”时,您应该能够看到该框的填充颜色。

In my case I wanted an image(check mark) to indicate selection of object. Drag an ImageWell to the Collection Item nib. Set the desired image and mark it as hidden. Go to bindings inspector and bind hidden attribute to Collection View Item.

enter image description here

(In my case I had created a separate nib for CollectionViewItem, so its binded to File's owner. If this is not the case and Item view is in the same nib as the CollectionView then bind to Collection View Item)

Set model key path as selected and Value transformer to NSNegateBoolean. Thats it now whenever the individual cells/items are selected the image will be visible, hence indicating the selection.

Adding to Alter's answer.

To set NSBox as root item. Simply create a new IB document(say CollectionItem) and drag an NSBox to the empty area. Now add all the elements as required inside the box. Now click on File's Owner and set Custom Class as NSCollectionViewItem.

enter image description here

And in the nib where NSCollectionView is added change the nib name for CollectionViewItem

enter image description here

In the NSBox, bind the remaining elements to Files Owner. For a label it would be similar to :

enter image description here

Now to get the highlight color as Alter mentioned in his answer, set desired color combination in the Fill Color option, set the NSBox to transparent and bind the transparency attribute as below:

enter image description here

Now when Collection View Items are selected you should be able to see the fill color of the box.

一页 2024-09-02 20:50:11

这太棒了,非常感谢!我正在为此苦苦挣扎!

为了向其他人澄清:

 [(PrototypeView*)[self view] setSelected:flag];
 [(PrototypeView*)[self view] setNeedsDisplay:YES];

将 PrototypeView* 替换为原型类名称。

This was awesome, thanks alot! i was struggling with this!

To clarify for to others:

 [(PrototypeView*)[self view] setSelected:flag];
 [(PrototypeView*)[self view] setNeedsDisplay:YES];

Replace PrototypeView* with the name of your prototype class name.

谜兔 2024-09-02 20:50:11

如果您正在寻找更新的 Swift 解决方案,请查看此回复

class MyViewItem: NSCollectionViewItem {
  override var isSelected: Bool {
      didSet {
        self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
      }
  }
  etc...
}

In case you are digging around for the updated Swift solution, see this response.

class MyViewItem: NSCollectionViewItem {
  override var isSelected: Bool {
      didSet {
        self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
      }
  }
  etc...
}
我的痛♀有谁懂 2024-09-02 20:50:11

这是带有选择的完整 Swift NSCollectionViewItem。不要忘记将 NSCollectioView 设置为可以在 IB 中或以编程方式选择。
在 macOS Mojave (10.14) 和 High Sierra (10.13.6) 下测试。

import Cocoa

class CollectionViewItem: NSCollectionViewItem {

private var selectionColor : CGColor {
    let selectionColor : NSColor = (isSelected ? .alternateSelectedControlColor : .clear)
    return selectionColor.cgColor
}

override var isSelected: Bool {
    didSet {
        super.isSelected = isSelected
        updateSelection()
        // Do other stuff if needed
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    view.wantsLayer = true
    updateSelection()
}

override func prepareForReuse() {
    super.prepareForReuse()
    updateSelection()
}

private func updateSelection() {
    view.layer?.backgroundColor = self.selectionColor
}
}

Here is the complete Swift NSCollectionViewItem with selection. Don't forget to set the NSCollectioView to selectable in IB or programmatically.
Tested under macOS Mojave (10.14) and High Sierra (10.13.6).

import Cocoa

class CollectionViewItem: NSCollectionViewItem {

private var selectionColor : CGColor {
    let selectionColor : NSColor = (isSelected ? .alternateSelectedControlColor : .clear)
    return selectionColor.cgColor
}

override var isSelected: Bool {
    didSet {
        super.isSelected = isSelected
        updateSelection()
        // Do other stuff if needed
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    view.wantsLayer = true
    updateSelection()
}

override func prepareForReuse() {
    super.prepareForReuse()
    updateSelection()
}

private func updateSelection() {
    view.layer?.backgroundColor = self.selectionColor
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文