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

后端 未结 7 1389
渐次进展
渐次进展 2020-12-12 13:38

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 hel

相关标签:
7条回答
  • 2020-12-12 13:57

    Here is the Swift syntax for copying 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
        }
    }
    
    0 讨论(0)
  • 2020-12-12 13:57
     #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];
        }
    }
    
    0 讨论(0)
  • 2020-12-12 13:59

    Your UITableViewCell subclass may look like this

    @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

    - (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];
    }
    
    0 讨论(0)
  • 2020-12-12 14:01

    created 2 scenarios out of Alexander's Code:

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

    //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:

    //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:

     override func viewDidLoad() {
      yourTableView.delegate = self
       }
    
    0 讨论(0)
  • 2020-12-12 14:07

    For pre iOS13, refer to Alexander's answer. For iOS13 shouldShowMenuForRowAt and canPerformAction are deprecated, hence you have to do use the following 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.

    0 讨论(0)
  • 2020-12-12 14:09

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

    - (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 canPerformAction message 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. [EDIT Since this was posted, I've learned how to add custom menu items.]

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

    0 讨论(0)
提交回复
热议问题