在选择保持一段时间后,UITableViewCell 的子类是否有一种简单的方法来显示“复制”UIMenuController 弹出窗口,就像在地址簿应用程序(见截图)中一样?
(来源:icog.net)
在选择保持一段时间后,UITableViewCell 的子类是否有一种简单的方法来显示“复制”UIMenuController 弹出窗口,就像在地址簿应用程序(见截图)中一样?
(来源:icog.net)
现在有在 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 没有帮助;我无法让我的自定义菜单项出现。从我的实验来看,似乎只支持复制、剪切和粘贴。[编辑自从发布后,我已经学会了如何添加自定义菜单项。]
请注意,这仅在实现所有三个委托方法时才有效。
iOS 5之前的方法是获取UIMenuController的共享实例,设置目标rect和view调用-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;
这是用于复制的 Swift 语法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
}
}
您的 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];
}
#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];
}
}
对于 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])
}
注意:最终结果会有所不同。但这是 Apple 提供的开箱即用功能。但是检查 iOS 13 Sim >> 常规 >> 关于上的设置应用程序。长按一个单元格不会有下面的UI,会是旧的UI,有点不一致。
根据亚历山大的代码创建了 2 个场景:
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.如果您有带有 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. */
}
}
}
顺便说一句,你应该在 viewDidLoad 中将你的 tableView 委托设置为 self ,这样它们才能工作,如下所示:
override func viewDidLoad() {
yourTableView.delegate = self
}