TableViewPlayground/ATComplexTableViewController.m
/* |
Copyright (C) 2017 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
The basic controller for the demo app. An instance exists inside the MainMenu.xib file. |
*/ |
#import "ATComplexTableViewController.h" |
#import "ATColorView.h" |
#import "ATTableCellView.h" |
#import "ATObjectTableRowView.h" |
#import "ATDesktopEntity.h" |
#import "ATColorTableController.h" |
@interface ATComplexTableViewController () <NSTableViewDelegate, NSTableViewDataSource, ATColorTableControllerDelegate, NSSplitViewDelegate> |
@property (weak) IBOutlet NSImageView *imageViewMain; |
@property (weak) IBOutlet ATColorView *colorViewMain; |
@property (strong) NSMutableArray *observedVisibleItems; |
@property (strong) NSTimer *animationDoneTimer; |
@property (strong) NSWindow *windowForAnimation; |
@property (weak) IBOutlet NSSplitView *mainSplitView; |
@property (weak) IBOutlet NSTableView *tableViewMain; |
@property (strong) IBOutlet NSImageView *imageViewForTransition; |
@property (strong) NSMutableArray *tableContents; |
@property (assign) BOOL useSmallRowHeight; |
@property (assign) NSInteger rowForEditingColor; |
@property (weak) IBOutlet NSTextField *txtFldFromRow; |
@property (weak) IBOutlet NSTextField *txtFldToRow; |
@property (weak) IBOutlet NSTextField *txtFldRowToEdit; |
@end |
#pragma mark - |
@implementation ATComplexTableViewController |
- (void)dealloc { |
// Stop any observations that we may have. |
for (ATDesktopEntity *imageEntity in self.observedVisibleItems) { |
if ([imageEntity isKindOfClass:[ATDesktopImageEntity class]]) { |
[imageEntity removeObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage]; |
} |
} |
} |
- (NSString *)windowNibName { |
return @"ATComplexTableViewWindow"; |
} |
- (void)windowDidLoad { |
[super windowDidLoad]; |
// Setup our content to be the contents of the Desktop Pictures folder. |
NSURL *picturesURL = |
[[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSLocalDomainMask].lastObject; |
picturesURL = [picturesURL URLByAppendingPathComponent:@"Desktop Pictures"]; |
ATDesktopFolderEntity *primaryFolder = [[ATDesktopFolderEntity alloc] initWithFileURL:picturesURL]; |
// Create a flat array of ATDesktopFolderEntity and ATDesktopImageEntity objects to display. |
_tableContents = [NSMutableArray array]; |
// We first do a pass over the children and add all the images under the "Desktop Pictures" category. |
[self.tableContents addObject:primaryFolder]; |
for (ATDesktopEntity *entity in primaryFolder.children) { |
if ([entity isKindOfClass:[ATDesktopImageEntity class]]) { |
[self.tableContents addObject:entity]; |
} |
} |
// Then do another pass through and add all the folders - including their children. |
// A recursive loop could be used too, but we want to only go one level deep. |
// |
for (ATDesktopEntity *entity in primaryFolder.children) { |
if ([entity isKindOfClass:[ATDesktopFolderEntity class]]) { |
[self.tableContents addObject:entity]; |
ATDesktopFolderEntity *subFolder = (ATDesktopFolderEntity *)entity; |
for (ATDesktopEntity *subFolderChildEntity in subFolder.children) { |
if ([subFolderChildEntity isKindOfClass:[ATDesktopImageEntity class]]) { |
[self.tableContents addObject:subFolderChildEntity]; |
} |
} |
} |
} |
self.colorViewMain.drawBorder = YES; |
self.colorViewMain.backgroundColor = [NSColor whiteColor]; |
// Initialize the main image view to our current desktop background. |
NSImage *initialImage = [[NSImage alloc] initByReferencingURL:[[NSWorkspace sharedWorkspace] desktopImageURLForScreen:[NSScreen mainScreen]]]; |
self.imageViewMain.image = initialImage; |
self.tableViewMain.doubleAction = @selector(tblvwDoubleClick:); |
self.tableViewMain.target = self; |
[self.tableViewMain reloadData]; |
// Allow drags to go everywhere. |
[self.tableViewMain setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO]; |
} |
- (ATDesktopEntity *)entityForRow:(NSInteger)row { |
return (ATDesktopEntity *)self.tableContents[row]; |
} |
- (ATDesktopImageEntity *)imageEntityForRow:(NSInteger)row { |
id result = row != -1 ? self.tableContents[row] : nil; |
if ([result isKindOfClass:[ATDesktopImageEntity class]]) { |
return result; |
} |
return nil; |
} |
- (void)reloadRowForEntity:(id)object { |
NSInteger row = [self.tableContents indexOfObject:object]; |
if (row != NSNotFound) { |
ATDesktopImageEntity *entity = [self imageEntityForRow:row]; |
ATTableCellView *cellView = [self.tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
if (cellView) { |
// Fade the imageView in, and fade the progress indicator out. |
[NSAnimationContext beginGrouping]; |
[NSAnimationContext currentContext].duration = 0.8; |
cellView.imageView.alphaValue = 0; |
cellView.imageView.image = entity.thumbnailImage; |
cellView.imageView.hidden = NO; |
cellView.imageView.animator.alphaValue = 1.0; |
cellView.progessIndicator.hidden = YES; |
[NSAnimationContext endGrouping]; |
} |
} |
} |
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { |
if ([keyPath isEqualToString:ATEntityPropertyNamedThumbnailImage]) { |
// Find the row and reload it. |
// Note that KVO notifications may be sent from a background thread (in this case, we know they will be) |
// We should only update the UI on the main thread, and in addition, |
// we use NSRunLoopCommonModes to make sure the UI updates when a modal window is up. |
// |
[self performSelectorOnMainThread:@selector(reloadRowForEntity:) |
withObject:object |
waitUntilDone:NO |
modes:@[NSRunLoopCommonModes]]; |
} |
} |
#pragma mark - NSTableViewDataSource |
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { |
return self.tableContents.count; |
} |
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row { |
// Support for us being a dragging source. |
return [self entityForRow:row]; |
} |
#pragma mark - NSTableViewDelegate |
- (void)tableView:(NSTableView *)tableView didRemoveRowView:(ATObjectTableRowView *)rowView forRow:(NSInteger)row { |
// Stop observing visible things. |
ATDesktopImageEntity *imageEntity = rowView.objectValue; |
NSInteger index = imageEntity ? [self.observedVisibleItems indexOfObject:imageEntity] : NSNotFound; |
if (index != NSNotFound) { |
[imageEntity removeObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage]; |
[self.observedVisibleItems removeObjectAtIndex:index]; |
} |
} |
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row { |
// Make the row view keep track of our main model object. |
ATObjectTableRowView *result = [[ATObjectTableRowView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; |
result.objectValue = [self entityForRow:row]; |
return result; |
} |
// We want to make "group rows" for the folders. |
- (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row { |
if ([[self entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
return YES; |
} else { |
return NO; |
} |
} |
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { |
ATDesktopEntity *entity = [self entityForRow:row]; |
if ([entity isKindOfClass:[ATDesktopFolderEntity class]]) { |
NSTextField *textField = [tableView makeViewWithIdentifier:@"TextCell" owner:self]; |
textField.stringValue = entity.title; |
return textField; |
} else { |
ATTableCellView *cellView = [tableView makeViewWithIdentifier:@"MainCell" owner:self]; |
ATDesktopImageEntity *imageEntity = (ATDesktopImageEntity *)entity; |
cellView.textField.stringValue = entity.title; |
cellView.subTitleTextField.stringValue = imageEntity.fillColorName; |
cellView.colorView.backgroundColor = imageEntity.fillColor; |
cellView.colorView.drawBorder = YES; |
// Use KVO to observe for changes of the thumbnail image. |
if (self.observedVisibleItems == nil) { |
_observedVisibleItems = [NSMutableArray new]; |
} |
if (![self.observedVisibleItems containsObject:entity]) { |
[imageEntity addObserver:self forKeyPath:ATEntityPropertyNamedThumbnailImage options:0 context:nil]; |
[imageEntity loadImage]; |
[self.observedVisibleItems addObject:imageEntity]; |
} |
// Hide/show progress based on the thumbnail image being loaded or not. |
if (imageEntity.thumbnailImage == nil) { |
cellView.progessIndicator.hidden = NO; |
[cellView.progessIndicator startAnimation:nil]; |
cellView.imageView.hidden = YES; |
} else { |
cellView.imageView.image = imageEntity.thumbnailImage; |
} |
// Size/hide things based on the row size. |
[cellView layoutViewsForSmallSize:self.useSmallRowHeight animated:NO]; |
return cellView; |
} |
} |
// We make the "group rows" have the standard height, while all other image rows have a larger height. |
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row { |
if ([[self entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
return tableView.rowHeight; |
} else { |
return self.useSmallRowHeight ? 30.0 : 75.0; |
} |
} |
- (NSIndexSet *)tableView:(NSTableView *)tableView selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes { |
// We don't want to change the selection if the user clicked in the fill color area. |
NSInteger row = tableView.clickedRow; |
if (row != -1 && ![self tableView:tableView isGroupRow:row]) { |
ATTableCellView *cellView = [self.tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
if (cellView) { |
// Use hit testing to see if is a color view; if so, don't let it change the selection. |
NSPoint windowPoint = NSApp.currentEvent.locationInWindow; |
NSPoint point = [cellView.superview convertPoint:windowPoint fromView:nil]; |
NSView *view = [cellView hitTest:point]; |
if ([view isKindOfClass:[ATColorView class]]) { |
// Don't allow the selection change. |
return tableView.selectedRowIndexes; |
} |
} |
} |
return proposedSelectionIndexes; |
} |
#pragma mark - Table Animation |
- (void)animationDoneTimerFired:(NSTimer *)timer { |
_animationDoneTimer = nil; |
// Set the normal one to have the final image and alpha value. |
// Set the image and update us before ordering out the animation window. |
self.imageViewMain.image = self.imageViewForTransition.image; |
self.imageViewMain.alphaValue = 1.0; |
// This displays right now, and prevents flicker if the animation window orders out before our display happened. |
[self.imageViewMain.window displayIfNeeded]; |
// Hide the animation window. |
[self.windowForAnimation orderOut:nil]; |
} |
- (void)ensureAnimationWindowCreated { |
if (self.windowForAnimation == nil) { |
NSRect contentRect = self.imageViewForTransition.frame; |
contentRect.origin = NSZeroPoint; |
self.imageViewForTransition.frame = contentRect; |
_windowForAnimation = [[NSWindow alloc] initWithContentRect:contentRect styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO]; |
self.windowForAnimation.releasedWhenClosed = NO; |
self.windowForAnimation.opaque = NO; |
self.windowForAnimation.backgroundColor = [NSColor clearColor]; |
// For ease of use, we setup the imageViewForTransition in the nib and add it as a subview. |
[self.windowForAnimation.contentView addSubview:self.imageViewForTransition]; |
} |
} |
- (NSRect)screenImageRectForRow:(NSInteger)row { |
NSRect result = NSZeroRect; |
// We always want to try to get a view back to do the animation from that rect. |
ATTableCellView *cellView = [self.tableViewMain viewAtColumn:0 row:row makeIfNecessary:YES]; |
if (cellView) { |
result = [cellView.imageView convertRect:cellView.imageView.bounds toView:nil]; |
NSRect convertedRect = [cellView.window convertRectToScreen:NSMakeRect(result.origin.x, result.origin.y, 0.0, 0.0)]; |
result.origin = NSMakePoint(convertedRect.origin.x, convertedRect.origin.y); |
} |
return result; |
} |
- (NSRect)finalScreenImageRect { |
// We are animating to right over the image view's frame. Convert to the right screen coordinates and animate the window there. |
NSRect finalImageFrame = [self.imageViewMain.superview convertRect:self.imageViewMain.frame toView:nil]; |
NSRect convertedRect = [self.window convertRectToScreen:NSMakeRect(finalImageFrame.origin.x, finalImageFrame.origin.y, 0.0, 0.0)]; |
finalImageFrame.origin = NSMakePoint(convertedRect.origin.x, convertedRect.origin.y); |
return finalImageFrame; |
} |
- (void)stopExistingTimerIfNeeded { |
// We want to stop any previous animations. |
if (self.animationDoneTimer != nil) { |
[self.animationDoneTimer invalidate]; |
_animationDoneTimer = nil; |
} |
} |
- (void)animateImageFromRow:(NSInteger)row { |
[self stopExistingTimerIfNeeded]; |
// We create a window to do the animation. |
// The purpose of using a window is to allow an animation to happen from a non-layer backed view to over a layer-backed view. |
// We easily could use a sibling view if everything was layer backed, or non-layer backed. |
[self ensureAnimationWindowCreated]; |
// Grab our model object for this row |
ATDesktopImageEntity *entity = [self imageEntityForRow:row]; |
// Set some initial state |
NSRect startingWindowFrame = [self screenImageRectForRow:row]; |
[self.windowForAnimation setFrame:startingWindowFrame display:NO]; |
self.imageViewForTransition.image = entity.thumbnailImage; |
self.imageViewMain.alphaValue = 1.0; |
// Bring the window above our existing window. |
[self.windowForAnimation orderFront:nil]; |
// We want to sync all the animations together. We use a grouping to do that. |
[NSAnimationContext beginGrouping]; |
{ |
NSTimeInterval animationDuration = 0.4; |
// Do a slow animation if the shift key is down |
if (([NSEvent modifierFlags] & NSEventModifierFlagShift) != 0) { |
animationDuration *= 4; |
} |
[NSAnimationContext currentContext].duration = animationDuration; |
NSRect finalImageFrame = [self finalScreenImageRect]; |
[[self.windowForAnimation animator] setFrame:finalImageFrame display:YES]; |
// Alpha/opacity animations only work for layer-backed views. |
[self.imageViewMain animator].alphaValue = 0.25; |
// Also, animate the background color. This is done with the layer. See ATColorView.h/.m |
[self.colorViewMain animator].backgroundColor = entity.fillColor; |
// At the end of the animation we want to do some cleanup. |
// We keep track of the timer so we can stop the operation if we need to. |
_animationDoneTimer = |
[NSTimer scheduledTimerWithTimeInterval:animationDuration |
target:self |
selector:@selector(animationDoneTimerFired:) |
userInfo:nil |
repeats:NO]; |
} |
[NSAnimationContext endGrouping]; |
} |
#pragma mark - NSSplitViewDelegate |
- (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex { |
return 200; |
} |
- (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex { |
// Make sure the view on the right has at least 200 px wide |
CGFloat splitViewWidth = splitView.bounds.size.width; |
return splitViewWidth - 200; |
} |
#pragma mark - ATColorTableControllerDelegate |
- (void)colorTableController:(ATColorTableController *)controller didChooseColor:(NSColor *)color named:(NSString *)colorName { |
if (self.rowForEditingColor != -1) { |
// Update our model. |
ATDesktopImageEntity *entity = [self imageEntityForRow:self.rowForEditingColor]; |
entity.fillColorName = colorName; |
entity.fillColor = color; |
// Update the view; we could reload things, but this is faster. |
ATTableCellView *cellView = [self.tableViewMain viewAtColumn:0 row:self.rowForEditingColor makeIfNecessary:NO]; |
cellView.colorView.backgroundColor = color; |
cellView.subTitleTextField.stringValue = colorName; |
} else { |
// With no row we are just setting the background color. |
self.colorViewMain.backgroundColor = color; |
} |
} |
#pragma mark - Actions |
- (IBAction)btnSetAsDesktopWallpaperClick:(id)sender { |
NSInteger selectedRow = self.tableViewMain.selectedRow; |
if (selectedRow != -1) { |
ATDesktopEntity *entity = self.tableContents[selectedRow]; |
if ([entity isKindOfClass:[ATDesktopImageEntity class]]) { |
ATDesktopImageEntity *desktopImageEntity = (ATDesktopImageEntity *)entity; |
NSError *error; |
NSURL *imageURL = desktopImageEntity.fileURL; |
NSColor *fillColor = desktopImageEntity.fillColor; |
NSDictionary *options = |
@{NSWorkspaceDesktopImageFillColorKey: fillColor, |
NSWorkspaceDesktopImageAllowClippingKey: @NO, |
NSWorkspaceDesktopImageScalingKey: @(NSImageScaleProportionallyUpOrDown)}; |
BOOL result = [[NSWorkspace sharedWorkspace] setDesktopImageURL:imageURL |
forScreen:[NSScreen screens].lastObject |
options:options |
error:&error]; |
if (!result) { |
[NSApp presentError:error]; |
} |
} |
} |
} |
- (void)editColorOnRow:(NSInteger)row { |
_rowForEditingColor = row; |
ATTableCellView *cellView = [self.tableViewMain viewAtColumn:0 row:row makeIfNecessary:NO]; |
NSColor *color = cellView.colorView.backgroundColor; |
[ATColorTableController sharedColorTableController].delegate = self; |
[[ATColorTableController sharedColorTableController] editColor:color withPositioningView:cellView.colorView]; |
} |
- (IBAction)cellColorViewClicked:(id)sender { |
// Find out what row it was in and edit that color with the popup |
NSInteger row = [self.tableViewMain rowForView:sender]; |
if (row != -1) { |
[self editColorOnRow:row]; |
} |
} |
- (IBAction)textTitleChanged:(id)sender { |
NSInteger row = [self.tableViewMain rowForView:sender]; |
if (row != -1) { |
ATDesktopImageEntity *entity = [self imageEntityForRow:row]; |
entity.title = [sender stringValue]; |
} |
} |
- (IBAction)colorTitleChanged:(id)sender { |
NSInteger row = [self.tableViewMain rowForView:sender]; |
if (row != -1) { |
ATDesktopImageEntity *entity = [self imageEntityForRow:row]; |
entity.fillColorName = [sender stringValue]; |
} |
} |
- (void)selectRowStartingAtRow:(NSInteger)row { |
if (self.tableViewMain.selectedRow == -1) { |
if (row == -1) { |
row = 0; |
} |
// Select the same or next row (if possible) but skip group rows |
while (row < self.tableViewMain.numberOfRows) { |
if (![self tableView:self.tableViewMain isGroupRow:row]) { |
[self.tableViewMain selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; |
return; |
} |
row++; |
} |
row = self.tableViewMain.numberOfRows - 1; |
while (row >= 0) { |
if (![self tableView:self.tableViewMain isGroupRow:row]) { |
[self.tableViewMain selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO]; |
return; |
} |
row--; |
} |
} |
} |
- (IBAction)btnRemoveRowClick:(id)sender { |
NSInteger row = [self.tableViewMain rowForView:sender]; |
if (row != -1) { |
[self.tableContents removeObjectAtIndex:row]; |
[self.tableViewMain removeRowsAtIndexes:[NSIndexSet indexSetWithIndex:row] withAnimation:NSTableViewAnimationEffectFade]; |
[self selectRowStartingAtRow:row]; |
} |
} |
- (IBAction)btnRemoveAllSelectedRowsClick:(id)sender { |
[self.tableContents removeObjectsAtIndexes:self.tableViewMain.selectedRowIndexes]; |
[self.tableViewMain removeRowsAtIndexes:self.tableViewMain.selectedRowIndexes withAnimation:NSTableViewAnimationEffectFade]; |
} |
- (IBAction)btnInsertNewRow:(id)sender { |
NSURL *url = [[NSWorkspace sharedWorkspace] desktopImageURLForScreen:[NSScreen mainScreen]]; |
ATDesktopImageEntity *entity = [[ATDesktopImageEntity alloc] initWithFileURL:url]; |
entity.fillColor = self.colorViewMain.backgroundColor; |
entity.fillColorName = @"Untitled Color"; |
NSInteger index = self.tableViewMain.selectedRow; |
if (index == -1) { |
if (self.tableViewMain.numberOfRows == 0) { |
index = 0; |
} else { |
index = 1; |
} |
} |
[self.tableContents insertObject:entity atIndex:index]; |
[self.tableViewMain beginUpdates]; |
[self.tableViewMain insertRowsAtIndexes:[NSIndexSet indexSetWithIndex:index] withAnimation:NSTableViewAnimationEffectFade]; |
[self.tableViewMain scrollRowToVisible:index]; |
[self.tableViewMain endUpdates]; |
} |
- (IBAction)mainColorViewClicked:(id)sender { |
_rowForEditingColor = -1; |
NSColor *color = self.colorViewMain.backgroundColor; |
[ATColorTableController sharedColorTableController].delegate = self; |
[[ATColorTableController sharedColorTableController] editColor:color withPositioningView:self.colorViewMain]; |
} |
// This is called when the the NSImageView changes. |
- (IBAction)cellBtnAnimateImageClick:(id)sender { |
NSInteger selectedRow = [self.tableViewMain rowForView:sender]; |
if (selectedRow != -1) { |
[self.tableViewMain scrollRowToVisible:selectedRow]; |
// Only animate if the thumbnail image is loaded. |
ATDesktopImageEntity *entity = self.tableContents[selectedRow]; |
if (entity.thumbnailImage != nil) { |
[self animateImageFromRow:selectedRow]; |
} |
} else { |
self.imageViewMain.image = nil; |
} |
} |
- (IBAction)chkbxHorizontalGridLineClicked:(id)sender { |
if (((NSButton *)sender).state == 0) { |
self.tableViewMain.gridStyleMask = NSTableViewGridNone; |
} else { |
self.tableViewMain.gridStyleMask = NSTableViewSolidHorizontalGridLineMask; |
} |
} |
- (IBAction)chkbxUseSmallRowHeightClicked:(id)sender { |
_useSmallRowHeight = ((NSButton *)sender).state == 1; |
// Reload the height for all non group rows. |
NSMutableIndexSet *indexesToNoteHeightChanges = [NSMutableIndexSet indexSet]; |
for (NSUInteger row = 0; row < self.tableContents.count; row++) { |
if (![[self entityForRow:row] isKindOfClass:[ATDesktopFolderEntity class]]) { |
[indexesToNoteHeightChanges addIndex:row]; |
} |
} |
// We also want to synchronize our own animations with the height change. |
// We do this by creating our own animation grouping. |
[NSAnimationContext beginGrouping]; |
[NSAnimationContext currentContext].duration = 1.5; |
// Update all the current visible views animated in sync with the row heights. |
[self.tableViewMain enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row) { |
for (NSUInteger i = 0; i < self.tableViewMain.tableColumns.count; i++) { |
NSView *view = [self.tableViewMain viewAtColumn:i row:row makeIfNecessary:NO]; |
if (view && [view isKindOfClass:[ATTableCellView class]]) { |
[(ATTableCellView *)view layoutViewsForSmallSize:self.useSmallRowHeight animated:YES]; |
} |
} |
}]; |
[self.tableViewMain noteHeightOfRowsWithIndexesChanged:indexesToNoteHeightChanges]; |
[NSAnimationContext endGrouping]; |
} |
- (IBAction)chkbxFloatGroupRowsClicked:(id)sender { |
BOOL checked = ((NSButton *)sender).state == 1; |
self.tableViewMain.floatsGroupRows = checked; |
} |
- (IBAction)btnBeginUpdatesClicked:(id)sender { |
[self.tableViewMain beginUpdates]; |
} |
- (IBAction)btnEndUpdatesClicked:(id)sender { |
[self.tableViewMain endUpdates]; |
} |
- (IBAction)btnMoveRowClick:(id)sender { |
NSInteger fromRow = self.txtFldFromRow.integerValue; |
NSInteger toRow = self.txtFldToRow.integerValue; |
[self.tableViewMain beginUpdates]; |
[self.tableViewMain moveRowAtIndex:fromRow toIndex:toRow]; |
id object = self.tableContents[fromRow]; |
[self.tableContents removeObjectAtIndex:fromRow]; |
[self.tableContents insertObject:object atIndex:toRow]; |
[self.tableViewMain endUpdates]; |
} |
- (IBAction)tblvwDoubleClick:(id)sender { |
NSInteger row = self.tableViewMain.selectedRow; |
if (row != -1) { |
ATDesktopEntity *entity = [self entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:entity.fileURL.path inFileViewerRootedAtPath:@""]; |
} |
} |
- (IBAction)btnManuallyBeginEditingClick:(id)sender { |
NSInteger row = self.txtFldRowToEdit.integerValue; |
[self.tableViewMain editColumn:0 row:row withEvent:nil select:YES]; |
} |
- (NSIndexSet *)indexesToProcessForContextMenu { |
NSIndexSet *selectedIndexes = self.tableViewMain.selectedRowIndexes; |
// If the clicked row was in the selectedIndexes, then we process all selectedIndexes. |
// Otherwise, we process just the clickedRow. |
// |
if (self.tableViewMain.clickedRow != -1 && ![selectedIndexes containsIndex:self.tableViewMain.clickedRow]) { |
selectedIndexes = [NSIndexSet indexSetWithIndex:self.tableViewMain.clickedRow]; |
} |
return selectedIndexes; |
} |
- (IBAction)mnuRevealInFinderSelected:(id)sender { |
NSIndexSet *selectedIndexes = [self indexesToProcessForContextMenu]; |
[selectedIndexes enumerateIndexesUsingBlock:^(NSUInteger row, BOOL *stop) { |
ATDesktopEntity *entity = [self entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:entity.fileURL.path inFileViewerRootedAtPath:@""]; |
}]; |
} |
- (IBAction)btnRevealInFinderSelected:(id)sender { |
NSInteger row = [self.tableViewMain rowForView:sender]; |
ATDesktopEntity *entity = [self entityForRow:row]; |
[[NSWorkspace sharedWorkspace] selectFile:entity.fileURL.path inFileViewerRootedAtPath:@""]; |
} |
- (IBAction)mnuRemoveRowSelected:(id)sender { |
NSIndexSet *indexes = [self indexesToProcessForContextMenu]; |
[self.tableViewMain beginUpdates]; |
[self.tableContents removeObjectsAtIndexes:indexes]; |
[self.tableViewMain removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade]; |
[self.tableViewMain endUpdates]; |
} |
- (IBAction)btnChangeSelectionAnimated:(id)sender { |
if (self.tableViewMain.selectedRow != -1) { |
[[self.tableViewMain animator] selectRowIndexes:[NSIndexSet indexSet] byExtendingSelection:NO]; |
} else { |
[[self.tableViewMain animator] selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO]; |
} |
} |
@end |
Copyright © 2017 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2017-04-14