In fact it's not quite so easy, as on the road show up some strange stuff, but I managed to create a customized one.
The steps are as following:
- Create a subclass of
UITextView
- Override
canPerformAction:
withSender:
for filtering the default actions of the menu
that pops up.
- Configuring
textView
in order not to be able to edit or
select.
- Editing
UIMenuController
items which provide the actions and buttons on the menu
- Add different selectors for each
UIMenuItem
**** (That is because the sender of the selector is not an UIMenuItem
, but a UIMenuController
which leads to another matter. Check here for more info. A gist by me for that)
Code
No more talking so here is the code:
EBCustomTextView.h
//
// EBCustomTextView.h
// TestProject
//
// Created by Erid Bardhaj on 3/8/16.
// Copyright © 2016 Erid Bardhaj. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, EBCustomTextViewMenuAction) {
EBCustomTextViewMenuActionCopy,
EBCustomTextViewMenuActionDefine,
EBCustomTextViewMenuActionRead
};
@class EBCustomTextView;
@protocol EBCustomTextViewMenuActionDelegate <NSObject>
- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action;
@end
@interface EBCustomTextView : UITextView
@property (weak, nonatomic) id<EBCustomTextViewMenuActionDelegate> menuActionDelegate;
@end
EBCustomTextView.m
//
// EBCustomTextView.m
// TestProject
//
// Created by Erid Bardhaj on 3/8/16.
// Copyright © 2016 Erid Bardhaj. All rights reserved.
//
#import "EBCustomTextView.h"
@implementation EBCustomTextView {
EBCustomTextViewMenuAction currentAction;
}
- (void)awakeFromNib {
[super awakeFromNib];
// Configure the textView
self.editable = NO;
self.selectable = NO;
}
#pragma mark - Datasource
- (NSArray *)items {
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle:@"Copy" action:@selector(copyMenuItemPressed)];
UIMenuItem *item1 = [[UIMenuItem alloc] initWithTitle:@"Define" action:@selector(defineMenuItemPressed)];
UIMenuItem *item2 = [[UIMenuItem alloc] initWithTitle:@"Read" action:@selector(readMenuItemPressed)];
return @[item, item1, item2];
}
#pragma mark - Actions
- (void)copyMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionCopy];
}
}
- (void)defineMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionDefine];
}
}
- (void)readMenuItemPressed {
if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:EBCustomTextViewMenuActionRead];
}
}
#pragma mark - Private
- (void)menuItemPressedAtIndex:(NSInteger)index {
currentAction = index;
if ([self.menuActionDelegate respondsToSelector:@selector(customTextView:didSelectMenuAction:)]) {
[self.menuActionDelegate customTextView:self didSelectMenuAction:currentAction];
}
}
#pragma mark Helpers
- (void)showMenuController {
UIMenuController *theMenu = [UIMenuController sharedMenuController];
theMenu.menuItems = [self items];
[theMenu update];
CGRect selectionRect = CGRectMake (0, 0, self.contentSize.width, self.contentSize.height);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:(theMenu.isMenuVisible) ? NO : YES animated:YES];
}
#pragma mark - Overridings
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
// Filter any action for this textView except our custom ones
if (action == @selector(copyMenuItemPressed) || action == @selector(defineMenuItemPressed) || action == @selector(readMenuItemPressed)) {
return YES;
}
return NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 1 && [self becomeFirstResponder]) {
[self showMenuController];
}
}
@end
Implementation
Set your textView's class to EBCustomTextView
and conform to EBCustomTextViewMenuActionDelegate
Interface
@interface ViewController () <EBCustomTextViewMenuActionDelegate>
viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.textView.menuActionDelegate = self;
}
Protocol conformance
#pragma mark - Delegation
#pragma mark EBCustomTextViewMenuActionDelegate
- (void)customTextView:(EBCustomTextView *)textView didSelectMenuAction:(EBCustomTextViewMenuAction)action {
switch (action) {
case EBCustomTextViewMenuActionCopy:
NSLog(@"Copy Action");
break;
case EBCustomTextViewMenuActionRead:
NSLog(@"Read Action");
break;
case EBCustomTextViewMenuActionDefine:
NSLog(@"Define Action");
break;
default:
break;
}
}
Output

Enjoy :)