0%

玩转 iOS 剪切板

前言

移动应用风靡的今天,大家越来越觉得,一个 App 的处理事情的效率至关重要,包括一些细节处理,像 本地化localization)和 辅助功能accessibility)的支持是高品质的应用和其它应用区分开的两个特性,今天我们来聊聊一个增加用户体验和效率的功能– 编辑操作

编辑操作

标准编辑动作

处理复制,剪切,删除和粘贴命令

  • 复制 (copy:)
  • 剪切 (cut:)
  • 删除 (delete:)
  • 粘贴 (paste:)

处理选择命令

  • 选择 (select:)
  • 全选 (selectAll:)

富文本编辑命令

  • 加粗 (toggleBoldface:)
  • 斜体 (toggleItalics:)
  • 下划线 (toggleUnderline:)

改变书写方向命令

  • 书写方向左到右 (makeTextWritingDirectionLeftToLeft:)
  • 书写方向右到左 (makeTextWritingDirectionRightToLeft:)

改变文本尺寸命令

  • 变大 (increaseSize:)
  • 变小 (decreaseSize:)

实现 label 的复制粘贴功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// RFLabel.h
@interface RFLabel : UILabel
@end

// RFLabel.m
@implementation RFLabel

- (BOOL)canBecomeFirstResponder {
return YES;
}

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

#pragma mark - UIResponderStandardEditActions

- (void)copy:(id)sender {
[[UIPasteboard generalPasteboard] setString:self.text];
}

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

@end

在控制器中使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#import "ViewController.h"
#import "RFLabel.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet RFLabel *topLabel;
@property (weak, nonatomic) IBOutlet RFLabel *bottomLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

self.topLabel.userInteractionEnabled = YES;
self.bottomLabel.userInteractionEnabled = YES;

UIGestureRecognizer *gestureRecognizer1 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
UIGestureRecognizer *gestureRecognizer2 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];

[self.topLabel addGestureRecognizer:gestureRecognizer1];
[self.bottomLabel addGestureRecognizer:gestureRecognizer2];
}

- (void)handleLongPressGesture:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateRecognized) {
[recognizer.view becomeFirstResponder];

UIMenuController *menuController = [UIMenuController sharedMenuController];
[menuController setTargetRect:recognizer.view.frame inView:recognizer.view.superview];
[menuController setMenuVisible:YES animated:YES];
}
}

@end

总结一下,为了能够支持复制粘贴一个 label 的文字,需要完成下面几步:

  • 必须继承 UILabel ,在子类中实现 canBecomeFirstRespondercanPerformAction:withSender: 方法
  • 实现每个可以执行的操作,在方法中和 UIPasteboard 进行交互
  • 初始化 label 时,将 label 的 userInteractionEnabled 属性设置为 YES * 向 label 添加一个手势(或者手动在子类中实现 UIResponder 的方法,例如 touchesBegan:withEvent:
  • 响应手势识别事件,指明 UIMenuController 的位置,设置为可见
  • 最后,使 label 成为第一响应者

UIMenuController

UIMenuController 负责展示编辑动作的菜单项。每个应用都持有自己的一个单例对象 sharedMenuController。
默认情况下,菜单控制器会展示 UIResponderStandardEditActions 这个非正式协议(即不需要对应实现的协议)中的方法,
假如 canPerformAction:withSender: 返回 YES

UIMenuItem

可以通过 UIMenuControllermenuItems 属性添加自定义的命令,每一个都是 UIMenuItem 对象,只需要指定每个动作的 titleaction

1
- (instancetype)initWithTitle:(NSString *)title action:(SEL)action NS_DESIGNATED_INITIALIZER;

UITableView 和 UICollectionView

UITableViewUICollectionView 可以在cell上调出编辑菜单,其实任何自定义的 UIView 都能实现, UIViewController 的子类只是以不同的方式实现而已。为了让 UITableViewUICollectionView 也能实现,你需要使用一些额外的方法否则屏幕上不会显示任何东西。这些方法给予了你更多的控制在显示指定的上下文菜单基于你将要显示在 UITableViewUICollectionView 的内容。

UITableViewDelegate 方法

  • – tableView:shouldShowMenuForRowAtIndexPath:
  • – tableView:canPerformAction:forRowAtIndexPath:withSender:
  • – tableView:performAction:forRowAtIndexPath:withSender:

UICollectionViewDelegate 方法

  • – collectionView:shouldShowMenuForItemAtIndexPath:
  • – collectionView:canPerformAction:forItemAtIndexPath:withSender:
  • – collectionView:performAction:forItemAtIndexPath:withSender:

Bonus

实现 UIImageView 图片的复制粘贴功能

Demo 地址:

参考链接