在UITableViewCells上显示“复制”弹出窗口的简单方法,如地址簿App

时间:2010-03-21 16:49:01

标签: ios objective-c iphone uitableview pasteboard

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

address book

7 个答案:

答案 0 :(得分:38)

现在有用于在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];
    }
}

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

请注意,仅当实现了所有三个委托方法时,此方法才有效。

答案 1 :(得分:28)

iOS 5之前的方法是获取UIMenuController的共享实例,设置目标rect并查看并调用-setMenuVisible:animated:。记得在你的回复者中实施-canPerformAction:withSender:


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; 

答案 2 :(得分:20)

以下是用于复制detailTextLabel的Swift语法。

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
    }
}

答案 3 :(得分:14)

您的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

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];
}

答案 4 :(得分:10)

 #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];
    }
}

答案 5 :(得分:1)

有关iOS13之前的版本,请参阅Alexander's answer。对于iOS13,shouldShowMenuForRowAtcanPerformAction已过时,因此您必须使用以下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])
}

注意:最终结果将有所不同。但这是Apple提供的即用型功能。但是,请在iOS 13 Sim >>常规>>关于中检查“设置”应用。长按一个单元格将没有下面的UI,它将是旧的UI,这有点不一致。

enter image description here

答案 6 :(得分:1)

用亚历山大法典创建了两个场景:

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。如果您的customCell带有customLabels,并且您想复制所有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.   */
    }
}

}

通过这种方式,应在viewDidLoad中将tableView委托设置为self,以使它们正常工作,如下所示:

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