ios 在 UITableViewCells 上显示“复制”弹出窗口的简单方法,如地址簿应用程序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2487844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:01:23  来源:igfitidea点击:

Simple way to show the 'Copy' popup on UITableViewCells like the address book App

iosobjective-ciphoneuitableviewpasteboard

提问by William Denniss

Is there a simple way for subclasses of UITableViewCell to show the 'Copy' UIMenuController popup like in the Address book app (see screenshot), after the selection is held for a while?

在选择保持一段时间后,是否有一种简单的方法让 UITableViewCell 的子类显示“复制”UIMenuController 弹出窗口,就像在地址簿应用程序中一样(参见屏幕截图)?

address book

地址簿

采纳答案by kennytm

The method before iOS 5 is to get the UIMenuController's shared instance, set the target rect and view and call -setMenuVisible:animated:. Remeber to implement -canPerformAction:withSender:in your responder.

iOS 5之前的方法是获取UIMenuController的共享实例,设置目标rect和view调用-setMenuVisible:animated:。记得-canPerformAction:withSender:在你的响应者中实现。



The method after iOS 5 (previously available as undocumented feature) is to implement these 3 methods in your data source (see https://developer.apple.com/reference/uikit/uitableviewdelegate#1653389).

iOS 5 之后的方法(以前作为未记录的功能可用)是在您的数据源中实现这 3 种方法(请参阅https://developer.apple.com/reference/uikit/uitableviewdelegate#1653389)。

-(void)tableView:(UITableView*)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender;
-(BOOL)tableView:(UITableView*)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath*)indexPath withSender:(id)sender;
-(BOOL)tableView:(UITableView*)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath*)indexPath; 

回答by matt

There is now official interface for displaying UITableView cell menus in iOS 5. Example (from the table delegate):

现在在 iOS 5 中有用于显示 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:));
}

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    if (action == @selector(copy:)){
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        [[UIPasteboard generalPasteboard] setString:cell.textLabel.text];
    }
}

I tried modifying the UIMenuController's shared controller to add my own menu item, and I was able to add it and get the canPerformActionmessage for it, but returning YES didn't help; I wasn't able to make my custom menu item appear. From my experiments, it looks like only Copy, Cut, and Paste are supported. [EDITSince this was posted, I've learned how to add custom menu items.]

我尝试修改 UIMenuController 的共享控制器以添加我自己的菜单项,并且我能够添加它并获取它的canPerformAction消息,但返回 YES 没有帮助;我无法显示我的自定义菜单项。从我的实验来看,似乎只支持复制、剪切和粘贴。[编辑自发布以来,我已经学会了如何添加自定义菜单项。]

Note that this works only if all three delegate methods are implemented.

请注意,这仅在实现所有三个委托方法时才有效。

回答by Alexander Bekert

Here is the Swift syntax for copying detailTextLabel.

这是复制detailTextLabel.

func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
    return (tableView.cellForRow(at: indexPath)?.detailTextLabel?.text) != nil
}

func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return action == #selector(copy(_:))
}

func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
    if action == #selector(copy(_:)) {
        let cell = tableView.cellForRow(at: indexPath)
        let pasteboard = UIPasteboard.general
        pasteboard.string = cell?.detailTextLabel?.text
    }
}

回答by zonble

Your UITableViewCell subclass may look like this

您的 UITableViewCell 子类可能如下所示

@interface MenuTableViewCell : UITableViewCell {
}
- (IBAction)copy:(id)sender;
- (void)showMenu;

@end


@implementation MenuTableViewCell

- (BOOL)canBecomeFirstResponder {
    return YES;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(copy:)) {
        return YES;
    }
    return NO;
}
- (IBAction)copy:(id)sender {
}
- (void)showMenu {
    [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
    [self becomeFirstResponder];
    [[UIMenuController sharedMenuController] update];
    [[UIMenuController sharedMenuController] setTargetRect:CGRectZero inView:self];
    [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES];

}

@end

And the UITableView delegate methods are like

而 UITableView 委托方法就像

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    MenuTableViewCell *cell = (MenuTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[MenuTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    // Configure the cell.
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    MenuTableViewCell *cell = (MenuTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
    [cell showMenu];
}

回答by ioopl

 #pragma mark - COPY/PASTE Cell Text via Menu

- (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:));
}

- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
    if (action == @selector(copy:))
    {
        UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
        UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
        [pasteBoard setString:cell.textLabel.text];
    }
}

回答by Honey

For pre iOS13, refer to Alexander's answer. For iOS13 shouldShowMenuForRowAtand canPerformActionare deprecated, hence you have to do use the following API:

对于 iOS13 之前的版本,请参阅Alexander 的回答。对于 iOS13shouldShowMenuForRowAt并且canPerformAction已弃用,因此您必须使用以下 API:

@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

    return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { suggestedActions in

        return self.makeContextMenu(for: indexPath)
    })
}

@available(iOS 13.0, *)
func makeContextMenu(for indexPath: IndexPath) -> UIMenu {

    let copyAction = UIAction(title: "Copy") { [weak self] _ in
        guard let self = self else { return }
        let cell = self.tableView.cellForRow(at: indexPath)
        let pasteboard = UIPasteboard.general
        pasteboard.string = cell?.detailTextLabel?.text

    }

    // Create and return a UIMenu with the share action
    return UIMenu(title: "Options", children: [copyAction])
}

Note:The end result will be different. But this is out of the box functionality that Apple is providing. However checking the Settings app on iOS 13 Sim >> General >> About. long-pressing a cell will not have the UI below, it will be the old UI, which is a bit inconsistent.

注意:最终结果会有所不同。但这是 Apple 提供的开箱即用的功能。但是,请检查 iOS 13 Sim >> 常规 >> 关于上的设置应用程序。长按一个单元格不会有下面的UI,会是旧的UI,有点不一致。

enter image description here

在此处输入图片说明

回答by Developersian

created 2 scenarios out of Alexander's Code:

从亚历山大的代码中创建了 2 个场景:

1.in case you want to copy textLabel and not detailTextLabel just use this code:

1.如果您想复制 textLabel 而不是 detailTextLabel 只需使用以下代码:

//MARK: Delegate
func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
    return (tableView.cellForRow(at: indexPath)?.textLabel?.text) != nil
}

func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return action == #selector(copy(_:))
}

func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
    if action == #selector(copy(_:)) {
        let cell = tableView.cellForRow(at: indexPath)
        let pasteboard = UIPasteboard.general
        pasteboard.string = cell?.textLabel?.text
    }
}

2.if you have customCell with customLabels and you want to copy all customLabels text Do this:

2.如果您有带有 customLabels 的 customCell 并且您想复制所有 customLabels 文本,请执行以下操作:

//MARK: Delegate
func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {

    return (tableView.cellForRow(at: indexPath) != nil)
}

func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return action == #selector(copy(_:))
}

func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {

    if action == #selector(copy(_:)) {

        /* change these array names according to your own array names */
        let customlabel1 = customlabel1Array[indexPath.row]     
        let customlabel2 = customlabel2Array[indexPath.row]
        let customlabel3 = customlabel3Array[indexPath.row]


        let pasteboard = UIPasteboard.general
        pasteboard.string = "\(customlabel1)\n\(customlabel2)\n\(customlabel3)"     /*    \n is for new line.   */
    }
}

}

By the way you should set your tableView delegate to self in viewDidLoad for these to work, like this:

顺便说一句,你应该你的tableView委托设置为self在viewDidLoad中的这些工作,就像这样:

 override func viewDidLoad() {
  yourTableView.delegate = self
   }