Mac開發基礎24-NSToolbar

Mr.陳發表於2024-08-06

NSToolbar 是 macOS 應用中的一個重要控制元件,用於建立視窗頂部的工具欄。工具欄通常包含按鈕和其他控制元件,使用者可以透過這些控制元件快速訪問常用功能。NSToolbarNSToolbarItem 協同工作,NSToolbar 是工具欄容器,而 NSToolbarItem 是工具欄項。下面我們詳細介紹 NSToolbar 的常見 API 和基礎技巧。

基本使用

建立和初始化工具欄

Objective-C

#import <Cocoa/Cocoa.h>

// 建立一個 NSToolbar 例項
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"MainToolbar"];

// 設定 Delegate
[toolbar setDelegate:self];

// 將工具欄新增到視窗
[self.window setToolbar:toolbar];

Swift

import Cocoa

// 建立一個 NSToolbar 例項
let toolbar = NSToolbar(identifier: "MainToolbar")

// 設定 Delegate
toolbar.delegate = self

// 將工具欄新增到視窗
self.window?.toolbar = toolbar

建立工具欄項

Objective-C

// 實現 NSToolbarDelegate 方法,返回所有允許在工具欄中使用的項
- (NSArray<NSString *> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
    return @[@"FirstItem", NSToolbarFlexibleSpaceItemIdentifier, @"SecondItem"];
}

// 實現 NSToolbarDelegate 方法,為特定識別符號建立相應的工具欄項
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag {
    if ([itemIdentifier isEqualToString:@"FirstItem"]) {
        NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:@"FirstItem"];
        item.label = @"First";
        item.toolTip = @"First Tool";
        item.action = @selector(handleToolbarAction:);
        item.target = self;
        item.image = [NSImage imageNamed:NSImageNameAddTemplate];
        return item;
    } else if ([itemIdentifier isEqualToString:@"SecondItem"]) {
        NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:@"SecondItem"];
        item.label = @"Second";
        item.toolTip = @"Second Tool";
        item.action = @selector(handleToolbarAction:);
        item.target = self;
        item.image = [NSImage imageNamed:NSImageNameRemoveTemplate];
        return item;
    }
    return nil;
}

Swift

// 實現 NSToolbarDelegate 方法,返回所有允許在工具欄中使用的項
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    return [.init("FirstItem"), .flexibleSpace, .init("SecondItem")]
}

// 實現 NSToolbarDelegate 方法,為特定識別符號建立相應的工具欄項
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
    if itemIdentifier.rawValue == "FirstItem" {
        let item = NSToolbarItem(itemIdentifier: .init("FirstItem"))
        item.label = "First"
        item.toolTip = "First Tool"
        item.action = #selector(handleToolbarAction(_:))
        item.target = self
        item.image = NSImage(named: NSImage.addTemplateName)
        return item
    } else if itemIdentifier.rawValue == "SecondItem" {
        let item = NSToolbarItem(itemIdentifier: .init("SecondItem"))
        item.label = "Second"
        item.toolTip = "Second Tool"
        item.action = #selector(handleToolbarAction(_:))
        item.target = self
        item.image = NSImage(named: NSImage.removeTemplateName)
        return item
    }
    return nil
}

響應工具欄項點選事件

Objective-C

// 實現工具欄項的點選響應方法
- (void)handleToolbarAction:(id)sender {
    NSToolbarItem *selectedItem = (NSToolbarItem *)sender;
    NSLog(@"Selected item: %@", selectedItem.itemIdentifier);
}

Swift

// 實現工具欄項的點選響應方法
@objc func handleToolbarAction(_ sender: Any?) {
    if let toolbarItem = sender as? NSToolbarItem {
        print("Selected item: \(toolbarItem.itemIdentifier.rawValue)")
    }
}

自定義工具欄項

可以透過子類化 NSToolbarItem 來建立自定義工具欄項。

Objective-C

@interface CustomToolbarItem : NSToolbarItem
@end

@implementation CustomToolbarItem

- (instancetype)initWithItemIdentifier:(NSString *)itemIdentifier {
    self = [super initWithItemIdentifier:itemIdentifier];
    if (self) {
        // 自定義設定
        self.label = @"Custom";
        self.toolTip = @"Custom Tool";
        self.image = [NSImage imageNamed:NSImageNameInfo];
    }
    return self;
}

@end

Swift

class CustomToolbarItem: NSToolbarItem {
    override init(itemIdentifier: NSToolbarItem.Identifier) {
        super.init(itemIdentifier: itemIdentifier)
        // 自定義設定
        self.label = "Custom"
        self.toolTip = "Custom Tool"
        self.image = NSImage(named: NSImage.infoTemplateName)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

動態更新工具欄項

可以透過設定工具欄項的 minSizemaxSize 以及使用 validateToolbarItem 方法來動態更新工具欄項。

Objective-C

- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
    if ([toolbarItem.itemIdentifier isEqualToString:@"FirstItem"]) {
        // 根據條件禁用或啟用工具欄項
        return YES;
    }
    return YES;
}

// 更新工具欄項的大小
- (void)updateToolbarItemsSize {
    NSToolbarItem *firstItem = [self.toolbar.items firstObject];
    [firstItem setMinSize:NSMakeSize(30, 30)];
    [firstItem setMaxSize:NSMakeSize(50, 50)];
}

Swift

func validateToolbarItem(_ item: NSToolbarItem) -> Bool {
    if item.itemIdentifier.rawValue == "FirstItem" {
        // 根據條件禁用或啟用工具欄項
        return true
    }
    return true
}

// 更新工具欄項的大小
func updateToolbarItemsSize() {
    if let firstItem = toolbar.items.first {
        firstItem.minSize = NSSize(width: 30, height: 30)
        firstItem.maxSize = NSSize(width: 50, height: 50)
    }
}

隱藏和顯示工具欄

可以透過 setVisible: 方法控制工具欄的顯示和隱藏。

Objective-C

// 隱藏工具欄
[self.window.toolbar setVisible:NO];
// 顯示工具欄
[self.window.toolbar setVisible:YES];

Swift

// 隱藏工具欄
self.window?.toolbar?.isVisible = false
// 顯示工具欄
self.window?.toolbar?.isVisible = true

高階用法

自定義工具欄項檢視

可以自定義工具欄項的檢視,比如將 NSButton 作為工具欄項。

Objective-C

- (NSToolbarItem *)createCustomViewToolbarItemWithIdentifier:(NSString *)identifier label:(NSString *)label action:(SEL)action {
    NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:identifier];
    item.label = label;
    item.view = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 30)];
    NSButton *button = (NSButton *)item.view;
    [button setTitle:label];
    [button setTarget:self];
    [button setAction:action];
    return item;
}

Swift

func createCustomViewToolbarItem(identifier: NSToolbarItem.Identifier, label: String, action: Selector) -> NSToolbarItem {
    let item = NSToolbarItem(itemIdentifier: identifier)
    item.label = label
    let button = NSButton(frame: NSRect(x: 0, y: 0, width: 100, height: 30))
    button.title = label
    button.target = self
    button.action = action
    item.view = button
    return item
}

動態新增和移除工具欄項

Objective-C

// 動態新增工具欄項
- (void)addToolbarItemWithIdentifier:(NSString *)identifier {
    NSToolbarItem *newItem = [self toolbar:nil itemForItemIdentifier:identifier willBeInsertedIntoToolbar:YES];
    [self.toolbar insertItemWithItemIdentifier:identifier atIndex:self.toolbar.items.count];
}

// 動態移除工具欄項
- (void)removeToolbarItemWithIdentifier:(NSString *)identifier {
    for (NSToolbarItem *item in self.toolbar.items) {
        if ([item.itemIdentifier isEqualToString:identifier]) {
            [self.toolbar removeItemAtIndex:[self.toolbar.items indexOfObject:item]];
            break;
        }
    }
}

Swift

// 動態新增工具欄項
func addToolbarItem(identifier: NSToolbarItem.Identifier) {
    if let newItem = self.toolbar(nil, itemForItemIdentifier: identifier, willBeInsertedIntoToolbar: true) {
        toolbar.insertItem(withItemIdentifier: identifier, at: toolbar.items.count)
    }
}

// 動態移除工具欄項
func removeToolbarItem(identifier: NSToolbarItem.Identifier) {
    for (item in toolbar.items) {
        if item.itemIdentifier == identifier {
            if let index = toolbar.items.firstIndex(of: item) {
                toolbar.removeItem(at: index)
                break
            }
        }
    }
}

工具欄定製皮膚

macOS 提供了工具欄定製功能,使用者可以透過它自行選擇工具欄中的項。

Objective-C

// 允許工具欄定製
[toolbar setAllowsUserCustomization:YES];

// 實現代理方法,返回使用者定製時可以看到的項
- (NSArray<NSString *> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar {
    return @[@"FirstItem", NSToolbarFlexibleSpaceItemIdentifier, @"SecondItem"];
}

- (NSArray<NSString *> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar {
    return @[@"FirstItem", NSToolbarFlexibleSpaceItemIdentifier, @"SecondItem", @"CustomItem"];
}

Swift

// 允許工具欄定製
toolbar.allowsUserCustomization = true

// 實現代理方法,返回使用者定製時可以看到的項
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    return [.init("FirstItem"), .flexibleSpace, .init("SecondItem")]
}

func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
    return [.init("FirstItem"), .flexibleSpace, .init("SecondItem"), .init("CustomItem")]
}

封裝工具類

為了更方便地使用 NSToolbar,可以封裝一個工具類,提供常見功能的高層介面。

Objective-C

#import <Cocoa/Cocoa.h>

@interface NSToolbarHelper : NSObject

+ (NSToolbar *)createToolbarWithIdentifier:(NSString *)identifier delegate:(id<NSToolbarDelegate>)delegate;
+ (NSToolbarItem *)createToolbarItemWithIdentifier:(NSString *)identifier label:(NSString *)label action:(SEL)action target:(id)target image:(NSImage *)image;

@end

@implementation NSToolbarHelper

+ (NSToolbar *)createToolbarWithIdentifier:(NSString *)identifier delegate:(id<NSToolbarDelegate>)delegate {
    NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:identifier];
    [toolbar setDelegate:delegate];
    [toolbar setAllowsUserCustomization:YES];
    return toolbar;
}

+ (NSToolbarItem *)createToolbarItemWithIdentifier:(NSString *)identifier label:(NSString *)label action:(SEL)action target:(id)target image:(NSImage *)image {
    NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:identifier];
    item.label = label;
    item.toolTip = label;
    item.image = image;
    item.action = action;
    item.target = target;
    return item;
}

@end

Swift

import Cocoa

class NSToolbarHelper {
    
    // 建立 NSToolbar 並設定代理
    static func createToolbar(identifier: String, delegate: NSToolbarDelegate) -> NSToolbar {
        let toolbar = NSToolbar(identifier: NSToolbar.Identifier(identifier))
        toolbar.delegate = delegate
        toolbar.allowsUserCustomization = true
        return toolbar
    }
    
    // 建立 NSToolbarItem
    static func createToolbarItem(identifier: String, label: String, action: Selector, target: AnyObject, image: NSImage) -> NSToolbarItem {
        let item = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier(identifier))
        item.label = label
        item.toolTip = label
        item.image = image
        item.action = action
        item.target = target
        return item
    }
}

使用示例

Objective-C

// 建立 NSToolbar
NSToolbar *toolbar = [NSToolbarHelper createToolbarWithIdentifier:@"MainToolbar" delegate:self];
[self.window setToolbar:toolbar];

// 建立並新增工具欄項
NSToolbarItem *firstItem = [NSToolbarHelper createToolbarItemWithIdentifier:@"FirstItem" label:@"First" action:@selector(handleToolbarAction:) target:self image:[NSImage imageNamed:NSImageNameAddTemplate]];
[toolbar insertItemWithItemIdentifier:@"FirstItem" atIndex:0];

NSToolbarItem *secondItem = [NSToolbarHelper createToolbarItemWithIdentifier:@"SecondItem" label:@"Second" action:@selector(handleToolbarAction:) target:self image:[NSImage imageNamed:NSImageNameRemoveTemplate]];
[toolbar insertItemWithItemIdentifier:@"SecondItem" atIndex:1];

Swift

// 建立 NSToolbar
let toolbar = NSToolbarHelper.createToolbar(identifier: "MainToolbar", delegate: self)
self.window?.toolbar = toolbar

// 建立並新增工具欄項
let firstItem = NSToolbarHelper.createToolbarItem(identifier: "FirstItem", label: "First", action: #selector(handleToolbarAction(_:)), target: self, image: NSImage(named: NSImage.addTemplateName)!)
toolbar.insertItem(withItemIdentifier: NSToolbarItem.Identifier("FirstItem"), at: 0)

let secondItem = NSToolbarHelper.createToolbarItem(identifier: "SecondItem", label: "Second", action: #selector(handleToolbarAction(_:)), target: self, image: NSImage(named: NSImage.removeTemplateName)!)
toolbar.insertItem(withItemIdentifier: NSToolbarItem.Identifier("SecondItem"), at: 1)

總結

透過了解 NSToolbar 的基本使用、建立工具欄項、響應工具欄項選擇、自定義工具欄項、動態更新工具欄項、隱藏和顯示工具欄以及工具欄定製皮膚等高階用法,並封裝工具類,你將能夠更高效地使用 NSToolbar 建立複雜的工具欄系統。在實際應用中,合理使用這些技巧可以顯著提升使用者介面的靈活性和使用者體驗。希望本文對你有所幫助,

相關文章