Mac开发基础19-NSTableView(二)

进阶使用和技巧

1. 单击和双击行事件处理

Objective-C

// 单击行时的处理
- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn {
    NSInteger clickedRow = [tableView clickedRow];
    if (clickedRow >= 0) {
        NSLog(@"Single click on row: %ld", (long)clickedRow); // 处理单击事件
    }
}

// 双击行时的处理
- (void)tableViewDoubleClick:(id)sender {
    NSInteger clickedRow = [tableView clickedRow];
    if (clickedRow >= 0) {
        NSLog(@"Double click on row: %ld", (long)clickedRow); // 处理双击事件
    }
}

// 配置双击事件
- (void)setupDoubleClickEventForTableView:(NSTableView *)tableView {
    [tableView setTarget:self];  // 设置点击事件的目标
    [tableView setDoubleAction:@selector(tableViewDoubleClick:)];  // 设置双击事件处理方法
}

Swift

// 单击行时的处理
func tableView(_ tableView: NSTableView, didClick tableColumn: NSTableColumn) {
    let clickedRow = tableView.clickedRow
    if clickedRow >= 0 {
        print("Single click on row: \(clickedRow)") // 处理单击事件
    }
}

// 双击行时的处理
@objc func tableViewDoubleClick(_ sender: AnyObject) {
    let clickedRow = tableView.clickedRow
    if clickedRow >= 0 {
        print("Double click on row: \(clickedRow)") // 处理双击事件
    }
}

// 配置双击事件
func setupDoubleClickEventForTableView(_ tableView: NSTableView) {
    tableView.target = self  // 设置点击事件的目标
    tableView.doubleAction = #selector(tableViewDoubleClick(_:))  // 设置双击事件处理方法
}

2. 拖动行重新排序

Objective-C

// 开始拖动行时的处理
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pasteboard {
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
    [pasteboard declareTypes:@[NSPasteboardTypeString] owner:self];
    [pasteboard setData:data forType:NSPasteboardTypeString];
    return YES; // 开始拖动
}

// 拖动时的验证
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
    return NSDragOperationMove; // 验证拖动操作
}

// 接受拖放后的处理
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id<NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation {
    NSPasteboard *pasteboard = [info draggingPasteboard];
    NSData *data = [pasteboard dataForType:NSPasteboardTypeString];
    NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    
    // 获取拖动的行并重新排序数据源
    NSInteger movingRow = [rowIndexes firstIndex];
    id object = _dataArray[movingRow];
    [_dataArray removeObjectAtIndex:movingRow];
    [_dataArray insertObject:object atIndex:row];
    
    [tableView reloadData]; // 重新加载表格
    return YES;
}

Swift

// 开始拖动行时的处理
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pasteboard: NSPasteboard) -> Bool {
    let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes)
    pasteboard.declareTypes([.string], owner: self)
    pasteboard.setData(data, forType: .string)
    return true // 开始拖动
}

// 拖动时的验证
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
    return .move // 验证拖动操作
}

// 接受拖放后的处理
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
    guard let pasteboard = info.draggingPasteboard.data(forType: .string),
          let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: pasteboard) as? IndexSet
    else {
        return false
    }
    
    // 获取拖动的行并重新排序数据源
    let movingRow = rowIndexes.first ?? 0
    let object = dataArray[movingRow]
    dataArray.remove(at: movingRow)
    dataArray.insert(object, at: row)
    
    tableView.reloadData() // 重新加载表格
    return true
}

3. 预加载内容和缓存优化

Objective-C

// 使用内存缓存来优化单元格的创建
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    NSString *identifier = [tableColumn identifier];
    NSTableCellView *cellView = [tableView makeViewWithIdentifier:identifier owner:self];
    
    if (!cellView) {
        cellView = [[NSTableCellView alloc] init];
        cellView.identifier = identifier;
    }
    
    // 配置单元格的内容
    cellView.textField.stringValue = _dataArray[row];
    return cellView;
}

Swift

// 使用内存缓存来优化单元格的创建
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    let identifier = tableColumn?.identifier ?? NSUserInterfaceItemIdentifier(rawValue: "")
    var cellView = tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView
    
    if cellView == nil {
        cellView = NSTableCellView()
        cellView?.identifier = identifier
    }
    
    // 配置单元格的内容
    cellView?.textField?.stringValue = dataArray[row]
    return cellView
}

4. 自定义表头视图和排序功能

Objective-C

#import "CustomHeaderView.h"
@implementation CustomHeaderView
// 自定义表头视图,可以加入按钮和其他控件
@end

@interface MyViewController ()
// 记录当前的排序键和排序方向
@property (nonatomic, strong) NSString *sortedColumnIdentifier;
@property (nonatomic, assign) BOOL isAscending;
@end

@implementation MyViewController

- (void)tableView:(NSTableView *)tableView didClickTableColumn:(NSTableColumn *)tableColumn {
    NSString *identifier = tableColumn.identifier;
    if ([self.sortedColumnIdentifier isEqualToString:identifier]) {
        self.isAscending = !self.isAscending; // 切换排序方向
    } else {
        self.sortedColumnIdentifier = identifier;
        self.isAscending = YES; // 默认升序
    }
    
    // 按照列标识符排序数据源
    [self.dataArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        if (self.isAscending) {
            return [[obj1 valueForKey:identifier] compare:[obj2 valueForKey:identifier]];
        } else {
            return [[obj2 valueForKey:identifier] compare:[obj1 valueForKey:identifier]];
        }
    }];
    
    [tableView reloadData];
}

Swift

import Cocoa

class CustomHeaderView: NSView {
    // 自定义表头视图,可以加入按钮和其他控件
}

class MyViewController: NSViewController {
    // 记录当前的排序键和排序方向
    var sortedColumnIdentifier: String?
    var isAscending: Bool = true

    func tableView(_ tableView: NSTableView, didClick tableColumn: NSTableColumn) {
        let identifier = tableColumn.identifier.rawValue
        if sortedColumnIdentifier == identifier {
            isAscending.toggle() // 切换排序方向
        } else {
            sortedColumnIdentifier = identifier
            isAscending = true // 默认升序
        }
        
        // 按照列标识符排序数据源
        dataArray.sort {
            if isAscending {
                return ($0[identifier] as? String ?? "") < ($1[identifier] as? String ?? "")
            } else {
                return ($0[identifier] as? String ?? "") > ($1[identifier] as? String ?? "")
            }
        }
        
        tableView.reloadData()
    }
}

5. 单元格内嵌按钮事件处理

Objective-C

// 自定义单元格视图,带按钮
@interface CustomButtonCellView : NSTableCellView
@property (nonatomic, strong) NSButton *actionButton;
@end

@implementation CustomButtonCellView
- (instancetype)initWithFrame:(NSRect)frameRect {
    self = [super initWithFrame:frameRect];
    if (self) {
        _actionButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 50, 20)];
        _actionButton.title = @"Click";
        _actionButton.target = self;
        _actionButton.action = @selector(buttonClicked:);
        [self addSubview:_actionButton];
    }
    return self;
}

// 按钮事件处理
- (void)buttonClicked:(id)sender {
    NSLog(@"Button in row %ld clicked", self.row);
}
@end

// 在视图控制器中返回自定义单元格
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    CustomButtonCellView *cellView = [tableView makeViewWithIdentifier:@"CustomButtonCell" owner:self];
    if (!cellView) {
        cellView = [[CustomButtonCellView alloc] initWithFrame:NSMakeRect(0, 0, tableColumn.width, 20)];
        cellView.identifier = @"CustomButtonCell";
    }
    cellView.row = row;
    return cellView;
}

Swift

// 自定义单元格视图,带按钮
class CustomButtonCellView: NSTableCellView {
    var actionButton: NSButton!

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        actionButton = NSButton(frame: NSRect(x: 0, y: 0, width: 50, height: 20))
        actionButton.title = "Click"
        actionButton.target = self
        actionButton.action = #selector(buttonClicked(_:))
        addSubview(actionButton)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // 按钮事件处理
    @objc func buttonClicked(_ sender: Any) {
        print("Button in row \(self.row ?? -1) clicked")
    }
}

// 在视图控制器中返回自定义单元格
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
    var cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("CustomButtonCell"), owner: self) as? CustomButtonCellView
    if cellView == nil {
        cellView = CustomButtonCellView(frame: NSRect(x: 0, y: 0, width: tableColumn?.width ?? 100, height: 20))
        cellView?.identifier = NSUserInterfaceItemIdentifier("CustomButtonCell")
    }
    cellView?.row = row
    return cellView
}

封装工具类

为了更方便地使用 NSTableView,我们可以封装一个工具类,提供常见功能的高层接口。

Objective-C

#import <Cocoa/Cocoa.h>

@interface NSTableViewHelper : NSObject

+ (NSTableView *)createTableViewWithColumns:(NSArray<NSString *> *)columnIdentifiers target:(id)target;
+ (void)setupDragAndDropForTableView:(NSTableView *)tableView;
+ (void)enableDoubleClickEventForTableView:(NSTableView *)tableView target:(id)target action:(SEL)action;

@end

@implementation NSTableViewHelper

+ (NSTableView *)createTableViewWithColumns:(NSArray<NSString *> *)columnIdentifiers target:(id)target {
    NSTableView *tableView = [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 400, 300)];
    for (NSString *identifier in columnIdentifiers) {
        NSTableColumn *column = [[NSTableColumn alloc] initWithIdentifier:identifier];
        column.headerCell.title = identifier;
        column.width = 100;
        [tableView addTableColumn:column];
    }
    tableView.dataSource = target;
    tableView.delegate = target;
    return tableView;
}

+ (void)setupDragAndDropForTableView:(NSTableView *)tableView {
    [tableView registerForDraggedTypes:@[NSPasteboardTypeString]];
    tableView.draggingSourceOperationMask = NSDragOperationEvery;
}

+ (void)enableDoubleClickEventForTableView:(NSTableView *)tableView target:(id)target action:(SEL)action {
    [tableView setTarget:target];
    [tableView setDoubleAction:action];
}

@end

Swift

import Cocoa

class NSTableViewHelper {
    
    // 创建表格视图并添加列
    static func createTableView(columnIdentifiers: [String], target: NSObject) -> NSTableView {
        let tableView = NSTableView(frame: NSRect(x: 0, y: 0, width: 400, height: 300))
        for identifier in columnIdentifiers {
            let column = NSTableColumn(identifier: NSUserInterfaceItemIdentifier(rawValue: identifier))
            column.headerCell.title = identifier
            column.width = 100
            tableView.addTableColumn(column)
        }
        tableView.dataSource = target as? NSTableViewDataSource
        tableView.delegate = target as? NSTableViewDelegate
        return tableView
    }
    
    // 配置拖放功能
    static func setupDragAndDrop(for tableView: NSTableView) {
        tableView.registerForDraggedTypes([.string])
        tableView.draggingSourceOperationMask = .every
    }
    
    // 启用双击事件处理
    static func enableDoubleClickEvent(for tableView: NSTableView, target: AnyObject, action: Selector) {
        tableView.target = target
        tableView.doubleAction = action
    }
}

使用示例

Objective-C

// 创建表格视图
NSTableView *tableView = [NSTableViewHelper createTableViewWithColumns:@[@"Column1", @"Column2"] target:self];

// 配置拖放功能
[NSTableViewHelper setupDragAndDropForTableView:tableView];

// 启用双击事件
[NSTableViewHelper enableDoubleClickEventForTableView:tableView target:self action:@selector(tableViewDoubleClick:)];

Swift

// 创建表格视图
let tableView = NSTableViewHelper.createTableView(columnIdentifiers: ["Column1", "Column2"], target: self)

// 配置拖放功能
NSTableViewHelper.setupDragAndDrop(for: tableView)

// 启用双击事件
NSTableViewHelper.enableDoubleClickEvent(for: tableView, target: self, action: #selector(tableViewDoubleClick(_:)))
posted @ 2024-08-06 17:23  Mr.陳  阅读(17)  评论(0编辑  收藏  举报