2016-06-24 58 views
5

Non riesco a far scorrere sia verticalmente che orizzontalmente per lavorare con un layout personalizzato per NSCollectionView. Secondo i documenti, nella mia sottoclasse restituisco `collectionViewContentSize 'e, se questo è troppo grande, lo scorrimento è abilitato automaticamente nella vista a scorrimento che racchiude la vista di raccolta. Tuttavia, anche se ordino tutti gli elementi in una riga orizzontale, solo lo scorrimento verticale è abilitato.NSCollectionView layout personalizzato abilita lo scorrimento

Ecco uno screenshot: http://i.stack.imgur.com/tMbCN.png

Ecco il mio codice di layout:

import Cocoa 
class Layout: NSCollectionViewLayout { 

var cellSize = CGSize(width: 100, height: 30) 

var cellSpacing: CGFloat = 10 
var sectionSpacing: CGFloat = 20 

private var contentSize = CGSize.zero 
private var layoutAttributes = [NSIndexPath: NSCollectionViewLayoutAttributes]() 


override func prepareLayout() { 
    guard let collectionView = collectionView else { return } 

    let sections = collectionView.numberOfSections 
    guard sections > 0 else { return } 

    contentSize.height = cellSize.height 

    for section in 0..<sections { 
     let items = collectionView.numberOfItemsInSection(section) 
     guard items > 0 else { break } 

     for item in 0..<items { 
      let origin = CGPoint(x: contentSize.width, y: 0) 
      let indexPath = NSIndexPath(forItem: item, inSection: section) 
      let attributes = NSCollectionViewLayoutAttributes(forItemWithIndexPath: indexPath) 
      attributes.frame = CGRect(origin: origin, size: cellSize) 
      layoutAttributes[indexPath] = attributes 

      contentSize.width += cellSize.width + cellSpacing 
     } 
     contentSize.width += sectionSpacing 
    } 
} 

override var collectionViewContentSize: NSSize { 
    return contentSize 
} 

override func layoutAttributesForElementsInRect(rect: NSRect) -> [NSCollectionViewLayoutAttributes] { 

    return layoutAttributes.values.filter { $0.frame.intersects(rect) } 
} 

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> NSCollectionViewLayoutAttributes? { 
    return layoutAttributes[indexPath] 
} 

override func shouldInvalidateLayoutForBoundsChange(newBounds: NSRect) -> Bool { 
    return false 
} 
} 
+0

In 'override var collectionViewContentSize', è possibile stampare' contentSize' e 'self.collectionView.frame' (o assimilati)? – Larme

+0

contentSize: (22200.0, 30.0) frame: (0.0, 0.0, 438.0, 228.0) – DasNilpferd

+1

E funziona in verticale, quando cambio il mio layout in una colonna verticale, posso scorrere fino all'ultima voce. Solo lo scorrimento orizzontale è sempre disabilitato. – DasNilpferd

risposta

1

E 'una sostanza bug in NSCollectionView. Come soluzione alternativa, è possibile implementare il metodo scrollDirection (da NSCollectionViewFlowLayout) e restituire NSCollectionViewScrollDirectionHorizontal.

+0

Puoi fornire un esempio? Non vedo un metodo 'scrollDirection' come parte dei delegati' NSCollectionView'. –

0

Ho trovato una soluzione. Ho creato una sottoclasse di NSCollectionView. E usare questa sottoclasse per sostituire NSCollectionView di default. Ho creato la funzione per sovrascrivere setFrameSize. Ecco!

-(void)setFrameSize:(NSSize)newSize{ 

    if (newSize.width != self.collectionViewLayout.collectionViewContentSize.width) { 

     newSize.width = self.collectionViewLayout.collectionViewContentSize.width; 

    } 

    [super setFrameSize:newSize]; 

    //NSLog(@"setFrameSize%@", CGSizeCreateDictionaryRepresentation(newSize)); 

} 

Durante la prova, la setFrameSize verrà richiamato e qualche meccanismo impostare la dimensione del frame di {0,0} prima impostare nuovamente per rendere la larghezza della stessa larghezza della ClipView e mantenere l'altezza dal dimensione del contenuto del layout.

+1

a Peder: si tratta di un NSCollectionViewLayout personalizzato, non di NSCollectionViewFlowLayout. Non penso che tu possa utilizzare NSCollectionViewScrollDirectionHorizontal per un NSCollectionViewLayout personalizzato. –

+0

to dckuehn: Grazie per la formattazione. –

+0

Peccato che questo non possa essere convertito in Swift :(Speriamo che il bug venga risolto presto. – Ash

4

Questo bug è ancora in corso. Ho convertito il brutto trucco di Bo Yuan in Swift. (Senza offesa, Bo Yuan, non è colpa TUA che dobbiamo fare questa soluzione orribile! Sto ancora strappando questo codice non appena c'è una correzione ufficiale però. Per il momento, questo continuerà a far andare avanti i miei sforzi di sviluppo .)

class HackedCollectionView: NSCollectionView { 
    override func setFrameSize(_ newSize: NSSize) { 
     let size = collectionViewLayout?.collectionViewContentSize ?? newSize 
     super.setFrameSize(size) 
     if let scrollView = enclosingScrollView { 
      scrollView.hasHorizontalScroller = size.width > scrollView.frame.width 
     } 
    } 
} 

si noti che questo ha decisamente bisogno di andare al più presto perché è sempre chiamato per ogni singolo fotogramma di animazione durante lo scorrimento. Non bello. Per favore, per favore, qualcuno ha risolto il problema o trovato una soluzione adeguata. Non essere in grado di scorrere orizzontalmente è ridicolo.

+0

Funziona, ma non riesco a ridimensionare la mia finestra orizzontalmente. :( –

+0

Questo funziona per me tranne che io prendo la larghezza massima perché contentSize può essere più piccolo dei limiti della vista.Posso ridimensionare la finestra e scorrere in entrambe le direzioni. – Andy

0

Sembra che solo NSCollectionViewFlowLayout sia in grado di dettare un frame che ha una larghezza maggiore rispetto al frame di scroll principale.

La soluzione è di sottoclasse NSCollectionViewFlowLayout anziché NSCollectionViewLayout. Tratta la sottoclasse come qualsiasi altra sottoclasse di layout, ma aggiungi la scrollDirection critica in prepareLayout().

Ecco l'implementazione minima per un layout che scorre in orizzontale e imposta semplicemente tutti gli elementi uno accanto all'altro.

-(void) prepareLayout 
{ 
    [super prepareLayout]; 
    self.scrollDirection = NSCollectionViewScrollDirectionHorizontal; 
} 

-(NSSize) collectionViewContentSize 
{ 
    NSSize itemSize = self.itemSize; 
    NSInteger num = [self.collectionView numberOfItemsInSection:0]; 
    NSSize contentSize = NSMakeSize((num * itemSize.width) + ((num+1) * self.minimumLineSpacing), NSHeight(self.collectionView.frame)); 
    return contentSize; 
} 

-(BOOL) shouldInvalidateLayoutForBoundsChange:(NSRect)newBounds { return YES; } 

-(NSArray<__kindof NSCollectionViewLayoutAttributes *> *) layoutAttributesForElementsInRect:(NSRect)rect 
{ 
    int numItems = [self.collectionView numberOfItemsInSection:0]; 
    NSMutableArray* attributes = [NSMutableArray arrayWithCapacity:numItems]; 
    for (int i=0; i<numItems; i++) 
    { 
     [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]]; 
    } 
    return attributes; 
} 

-(NSCollectionViewLayoutAttributes*) layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSSize itemSize = self.itemSize; 
    NSRect fr = NSZeroRect; 
    fr.size = itemSize; 
    fr.origin.y = (NSHeight(self.collectionView.frame) - itemSize.height)/2.0; 
    fr.origin.x = (indexPath.item+1 * self.minimumLineSpacing) + (indexPath.item * itemSize.width); 
    NSCollectionViewLayoutAttributes* attr = [NSCollectionViewLayoutAttributes layoutAttributesForItemWithIndexPath:indexPath]; 
    attr.frame = fr; 
    return attr; 
}