Evidenziare Selezione in NSCollectionView
-
23-09-2019 - |
Domanda
Ho un NSCollectionView
di lavoro con un minore, ma critico, eccezione. Ottenere ed evidenziando l'elemento selezionato all'interno della raccolta.
Ho avuto tutto questo lavoro prima di Snow Leopard, ma qualcosa sembra essere cambiato e io non riesco a mettere il dito su di esso, così ho preso il mio NSCollectionView
destra di nuovo a un test di base e seguito la documentazione di Apple per la creazione di un NSCollectionView qui:
La vista di raccolta funziona bene seguendo la guida di avvio rapido. Tuttavia, questa guida non discute selezione diversa "There are such features as incorporating image views, setting objects as selectable or not selectable and changing colors if they are selected"
.
Usando questo come esempio sono andato alla fase successiva di legare il controller di array al NSCollectionView
con il controller selectionIndexes
chiave, pensando che questo avrebbe associare qualsiasi scelta che faccio tra il NSCollectionView
e il controller di array e sparando quindi fuori un KVO notifica. Ho anche impostato il NSCollectionView
per essere selezionabile in IB.
Non sembra esserci nessun delegato di selezione per NSCollectionView
e, a differenza maggior parte delle viste cacao dell'interfaccia utente, non sembra esserci alcuna evidenziazione di default selezionato.
Quindi il mio problema tratta veramente basso per un problema analogo, ma di due questioni distinte.
- Come faccio a catturare una selezione di un elemento?
- Come faccio a visualizzare un punto culminante di un articolo?
guide di programmazione di NSCollectionView
sembrano essere pochi e rari e la maggior parte le ricerche tramite Google sembrano tirare su pre-neve implementazioni Leopard, oppure utilizzare la visualizzazione in un file XIB separato.
Per questi ultimi (file XIB separato per la vista), non vedo il motivo per cui questo dovrebbe essere un pre-requisito altrimenti avrei sospettato che Apple non avrebbe incluso la vista nello stesso fascio come elemento vista collezione .
So che questo sta per essere una "non può vedere il legno per gli alberi" problema - quindi sono preparato per il "doh!" momento.
Come al solito, ogni e qualsiasi aiuto molto apprezzato.
Aggiorna 1
OK, così ho pensato di trovare l'elemento selezionato (s), ma non hanno ancora capire l'evidenziazione. Per gli interessati sul calcolare gli elementi selezionati (supponendo che si sta seguendo la guida di Apple):
Nel controllore (nel mio caso di test App Delegato) ho aggiunto il seguente:
In awakeFromNib
[personArrayController addObserver:self
forKeyPath:@"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
Nuovo metodo
-(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
}
}
}
Non dimenticare di dealloc per i non-GC
-(void)dealloc
{
[personArrayController removeObserver:self
forKeyPath:@"selectionIndexes"];
[super dealloc];
}
Ancora alla ricerca per la risoluzione clou ...
Aggiorna 2
Ha preso il consiglio di Macatomy ma aveva ancora un problema. Distacco i metodi della classe rilevanti per vedere dove ho sbagliato.
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
Soluzione
La sua non è troppo difficile da fare. Assicurarsi che "Selezione" è abilitata per la NSCollectionView in Interface Builder. Poi nella sottoclasse NSView che si sta utilizzando per la visualizzazione del prototipo, dichiarare una proprietà chiamata "selezionato":
@property (readwrite) BOOL selected;
codice aggiornato QUI: (aggiunto chiamata super)
sottoclasse NSCollectionViewItem e sovrascrivere -setSelected:
- (void)setSelected:(BOOL)flag
{
[super setSelected:flag];
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
}
Poi si deve aggiungere il codice in drawRect del vostro vista prototipo: metodo per disegnare il momento clou:
- (void)drawRect:(NSRect)dirtyRect
{
if (selected) {
[[NSColor blueColor] set];
NSRectFill([self bounds]);
}
}
Questo semplicemente riempie la vista in blu quando la sua scelta, ma che possono essere personalizzati per disegnare l'evidenziazione qualsiasi modo si desidera. Ho usato questo nei miei proprie applicazioni e funziona benissimo.
Altri suggerimenti
Se un colore di sfondo diverso sarà sufficiente come un punto culminante, si può semplicemente utilizzare un NSBox come l'elemento principale per voce della raccolta vista. Riempire il NSBox con il colore di evidenziazione della vostra scelta. Impostare il NSBox su Personalizzata in modo che il riempimento funzionerà. Impostare il NSBox a trasparente.
Associare l'attributo trasparenza del NSBox all'attributo selezionato di file proprietario (insieme di elementi) Impostare il trasformatore valore per il trasparente legame NSNegateBoolean.
Ho cercato di collegare l'interfaccia screenshot builder ma sono stato respinto bcos Sono un principiante: - (
Si può anche andare in un altro modo, se non sei sottoclassi NSView per la visualizzazione prototipo.
Nel vostro sottoclasse NSCollectionViewItem esclusione 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;
}
E naturalmente, come detto da tutte le persone sagge prima di me, assicurarsi che "Selezione" è abilitata per la NSCollectionView in Interface Builder.
Dal momento che nessuna delle risposte esistenti lavorato super bene per me, ecco il mio prendere su di esso. Modificare la sottoclasse della voce CollectionView a SelectableCollectionViewItem. Ecco che è il codice. Viene fornito con una proprietà textColor associabile per collegare la vostra etichetta di testo textColor vincolante.
@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];
}
Nel mio caso ho voluto un'immagine (segno di spunta) per indicare la selezione di oggetti. Trascinare un ImageWell al pennino insieme di elementi. Impostare l'immagine desiderata e segnare come nascosto. Vai a attacchi ispettore e legarsi attributo nascosto a Collection Visualizza Articolo.
(Nel mio caso avevo creato un pennino separato per CollectionViewItem, per cui il suo binded proprietario del file. Se questo non è il caso e la vista: L'articolo è nella stessa pennino come il CollectionView quindi associare alla Collezione Visualizza Articolo)
Imposta percorso della chiave modello come selected
e trasformatore Valore da NSNegateBoolean
. Quello ora ogniqualvolta le singole cellule / articoli sono selezionati l'immagine sarà visibile, indicando l'selezione.
L'aggiunta alla risposta di Alter.
Per impostare NSBox come elemento principale. È sufficiente creare un nuovo documento IB (dire collectionItem) e trascinare un NSBox per la zona vuota. Ora aggiungere tutti gli elementi, come richiesto all'interno della scatola. Ora cliccate su File del proprietario e impostare classe personalizzata come NSCollectionViewItem
.
E nel pennino in cui viene aggiunto NSCollectionView
cambiare il nome pennino per CollectionViewItem
Nel NSBox, legare i rimanenti elementi a Files Owner
. Per un'etichetta, sarebbe simile a:
Ora per ottenere il colore di evidenziazione come Alter menzionato nella sua risposta, impostare la combinazione di colore desiderata nell'opzione Colore di riempimento, impostare il NSBox
a trasparente e rilegare la trasparenza attributo come di seguito:
Ora, quando si selezionano Collection Visualizzare gli articoli si dovrebbe essere in grado di vedere il colore di riempimento della casella.
Nel vostro NSCollectionViewItem
sottoclasse, isSelected
override e cambiamento di colore del livello di sfondo. Prova in MacOS 10.14 e 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
}
}
}
Questo è stato fantastico, grazie molto! Stavo lottando con questo!
Per chiarire per gli altri:
[(PrototypeView*)[self view] setSelected:flag];
[(PrototypeView*)[self view] setNeedsDisplay:YES];
Sostituire PrototypeView * con il nome del prototipo nome della classe.
Nel caso in cui si sta scavando in giro per la soluzione Swift aggiornato, vedere questa risposta .
class MyViewItem: NSCollectionViewItem {
override var isSelected: Bool {
didSet {
self.view.layer?.backgroundColor = (isSelected ? NSColor.blue.cgColor : NSColor.clear.cgColor)
}
}
etc...
}
Ecco la completa Swift NSCollectionViewItem con la selezione. Non dimenticare di impostare il NSCollectioView a selezionabili in IB o di programmazione. Testato sotto MacOS Mojave (10.14) e 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
}
}