UIMenuController的使用

1, 基本使用

以對一個UILabel長按彈出菜單為例

子類化UILabel

因為需要覆蓋這幾個方法:
- (BOOL)canBecomeFirstResponder; 返回YES
同時需要在每次UI元素出現的時候去becomeFirstResponder一次,才能顯示出菜單. 在我的實測中, 我在ViewDidLoad里面這么做了, 當UI導航到別的頁面(導航控件, 或modal頁面), 然后回來, 菜單又失效了, 所以我寫到ViewWillAppear里面去了, 通過

- (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender;
這個方法會在每一個menuItem生成的時候調用一次, 因此在方法體里就要根據action來判斷是否需要顯示在菜單里, 如果不需要, 則返回NO. 也就是說, 如果你什么都不做, 直接返一個YES, 那么所有的默認菜單項都會顯示出來, 此處我們只要一個Copy選項吧:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return (action == @selector(copy:));
}

添加觸發方式

如果是以長按為觸發, 則添加長按手勢, 代碼片段如下:

// 在awakeFromNib里面添加即可
UILongPressGestureRecognizer *menuGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(menu:)];
    menuGesture.minimumPressDuration = 0.2;
    [self addGestureRecognizer:menuGesture];

- (void)menu:(UILongPressGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateBegan) {
        UIMenuController *menu = [UIMenuController sharedMenuController];
        [menu setTargetRect:self.frame inView:self.superView]; // 把誰的位置告訴控制器, 菜單就會以其為基準在合適的位置出現
        [menu setMenuVisible:YES animated:YES];
    }
}

編寫菜單行為

上面我們只要了copy, 那么就覆蓋默認的copy方法:

- (void)copy:(id)sender {
    UIPasteboard *paste = [UIPasteboard generalPasteboard];
    paste.string = self.text;
}

2, 添加自定義菜單項

自定義菜單只需要在菜單控制器添加幾個item即可, 結合上例, 我的那個label是顯示電話號碼的, 那么就讓它多顯示一個”打電話”和一個”發短信”菜單吧, 唯一需要注意的是, 在設置自定義菜單項時, 設置的items只影響自定義部分, 標準菜單項仍然是由canPerformAction決定的:

UIMenuItem *itemCall = [[UIMenuItem alloc] initWithTitle:@"Call" action:@selector(call:)];
UIMenuItem *itemMessage = [[UIMenuItem alloc] initWithTitle:@"Message" action:@selector(message:)];
[[UIMenuController sharedMenuController] setMenuItems: @[itemCall, itemMessage]];
[[UIMenuController sharedMenuController] update];

注, 添加了兩個菜單后, canPerformAction需要相應變化, 自己想想應該怎么改. 也可以在下一節看代碼. 當然也要自行寫完里面的call和message方法, 參照copy的寫法即可

3, UITableViewCell長按顯示菜單

標準菜單項

UITableView里面長項條目顯示標準菜單, 只需要實現下述代理方法即可:

- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
    return (action == @selector(copy:)); // 只顯示Copy
}

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
    if (action == @select(copy:)) {
        UIPasteboard *paste = [UIPasteboard generalPasteboard];
        paste.string = cell.detailLabel.text; // 自行寫業務邏輯
    }
}

4, TableViewCell添加自定義菜單項

同樣也得子類化一個TableViewCell,目的也是為了覆蓋同樣的幾個方法:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return (action == @selector(copy:) || action == @selector(call:) || action == @selector(message:)); // 此處我們把三個行為都寫全了, 回答上一節的問題
}

- (BOOL)canBecomeFirstResponder {
    return YES;
}

但因為tableView已經實現了菜單, 所以不需要顯式為每個cell去becomeFirtResponder了.

添加菜單項的方法同上, 寫菜單行為的方法同copy:, 都是一樣的.

注: 你們或許已經發現了, 添加自定義菜單項的時候, 仍然需要canPerformAction, 在這里, 與tableView代理里面的同名方法有什么關系? 是的, 兩個都要寫, tableView里面的只會影響標準菜單, 文檔說只支持這兩個UIResponderStandardEditActions (copy/paste)

注: 然而, - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender這個方法卻有點別扭, 一來不需要去實現了, 二來又不能注釋掉(你們自己試一下), 等于一定要留一個空的方法體在那里…

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容