Выделение в NSCollectionView
-
23-09-2019 - |
Вопрос
У меня есть рабочий NSCollectionView
за одним незначительным, но важным исключением.Получение и выделение выбранного элемента в коллекции.
Я работал над всем этим до Snow Leopard, но, похоже, что-то изменилось, и я не могу точно определить, в чем дело, поэтому я взял свой NSCollectionView
вернемся к базовому тестированию и рассмотрим документацию Apple по созданию NSCollectionView здесь:
Представление коллекции отлично работает в соответствии с кратким руководством по началу работы.Однако в этом руководстве не обсуждается выбор, кроме "There are such features as incorporating image views, setting objects as selectable or not selectable and changing colors if they are selected"
.
Используя это в качестве примера, я перешел к следующему шагу привязки контроллера массива к NSCollectionView
с помощью клавиши управления selectionIndexes
, думая , что это свяжет любой выбор, который я сделаю между NSCollectionView
и контроллер массива и, таким образом, запускает уведомление KVO.Я также установил NSCollectionView
выбирается в IB.
По-видимому, нет делегата для выбора NSCollectionView
и в отличие от большинства представлений пользовательского интерфейса Cocoa, здесь, похоже, нет выделенной по умолчанию подсветки.
Так что моя проблема действительно сводится к смежному вопросу, но двум разным вопросам.
- Как мне зафиксировать выделение элемента?
- Как мне показать выделенный элемент элемента?
NSCollectionView
руководств по программированию, похоже, немного, и большинство поисковых запросов через Google, похоже, выдают реализации, созданные до Snow Leopard, или используют представление в отдельном файле XIB.
Для последнего (отдельный файл XIB для представления) я не понимаю, почему это должно быть предварительным условием, иначе я бы заподозрил, что Apple не включила бы представление в тот же пакет, что и элемент представления коллекции.
Я знаю, что это будет проблема типа "за деревьями не видно леса", так что я готов к моменту "дох!".
Как обычно, любая помощь очень ценится.
Обновление 1
Хорошо, итак, я решил найти выбранный элемент (ы), но еще не определился с выделением.Для тех, кому интересно ознакомиться с выбранными элементами (при условии, что вы следуете руководству Apple):
В контроллере (в моем тестовом примере делегате приложения) Я добавил следующее:
В awakeFromNib
[personArrayController addObserver:self
forKeyPath:@"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
Новый Метод
-(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
Последовал совету Макатоми, но проблема все еще оставалась.Публикую соответствующие методы класса, чтобы увидеть, где я ошибся.
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
Решение
Это не так уж сложно сделать.Убедитесь, что "Выбор" включен для NSCollectionView в Interface Builder.Затем в подклассе 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]);
}
}
Это просто заливает вид синим цветом, когда он выбран, но это можно настроить, чтобы нарисовать подсветку любым удобным для вас способом.Я использовал это в своих собственных приложениях, и это отлично работает.
Другие советы
Если другого цвета фона будет достаточно в качестве выделения, вы могли бы просто использовать NSBox в качестве корневого элемента для представления элемента коллекции.Заполните NSBox цветом выделения по вашему выбору.Установите для NSBox значение Custom, чтобы заливка работала.Установите для NSBox значение transparent (прозрачный).
Привяжите атрибут прозрачности NSBox к выбранному атрибуту владельца файла (элемента коллекции) Установите преобразователь значений для прозрачной привязки в NSNegateBoolean.
Я попытался прикрепить скриншоты Interface builder, но мне было отказано, потому что я новичок:-(
Вы также можете пойти другим путем, если вы не создаете подкласс NSView для своего прототипа view.
В вашем подклассе 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;
}
И, конечно, как говорили все мудрые люди до меня, убедитесь, что "Выбор" включен для NSCollectionView в Interface Builder.
Поскольку ни один из существующих ответов не сработал для меня очень хорошо, вот мой взгляд на это.Измените подкласс элемента 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];
}
В моем случае я хотел, чтобы изображение (галочка) указывало на выбор объекта.Перетащите изображение на кончик элемента коллекции.Установите нужное изображение и пометьте его как скрытое.Перейдите в инспектор привязок и привяжите скрытый атрибут к элементу представления коллекции.
(В моем случае я создал отдельный файл для CollectionViewItem, поэтому он привязан к владельцу файла.Если это не так, и представление элемента находится в том же разделе, что и CollectionView, тогда привяжитесь к элементу представления коллекции)
Задайте путь к ключу модели следующим образом selected
и преобразователь значения в NSNegateBoolean
.Вот и все, теперь всякий раз, когда будут выбраны отдельные ячейки / элементы, изображение будет видно, следовательно, указывая на выделение.
Добавление к ответу Алтера.
Чтобы установить NSBox в качестве корневого элемента.Просто создайте новый документ IB (скажем, CollectionItem) и перетащите NSBox в пустую область.Теперь добавьте все необходимые элементы внутри коробки.Теперь нажмите на Владельца файла и установите пользовательский класс как NSCollectionViewItem
.
И в том наконечнике , где NSCollectionView
добавлено изменение названия наконечника для CollectionViewItem
В NSBox привяжите остальные элементы к Files Owner
.Для ярлыка это было бы похоже на :
Теперь, чтобы получить цвет подсветки в виде Изменить упомянутый в его ответе, установите желаемую цветовую комбинацию в опции "Цвет заливки", установите NSBox
сделать прозрачным и привязать атрибут прозрачности, как показано ниже:
Теперь, когда выбраны элементы представления коллекции, вы должны иметь возможность видеть цвет заливки поля.
В вашем 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
}
}
}
Это было потрясающе, большое спасибо!я боролся с этим!
Разъяснять другим:
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
Замените PrototypeView* именем вашего прототипа class name.
На случай, если вы ищете обновленное решение Swift, посмотрите на этот ответ.
class MyViewItem: NSCollectionViewItem {
override var isSelected: Bool {
didSet {
self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
}
}
etc...
}
Вот полный 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
}
}