How to Create Custom Uimenucontroller with Only Custom Items Other Than Default

how to create Custom UIMenuController with only custom items other than default?

You can achieve this by subclassing UIWebView and overriding canPerformAction (Swift 3). Then, all you need to do is return false for whichever actions you want disabled.

Example:

class EditedUIMenuWebView: UIWebView {

override func canPerformAction(_ action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(cut(_:)) {
return false
}
if action == #selector(paste(_:)) {
return false
}
if action == #selector(select(_:)) {
return false
}
if action == #selector(selectAll(_:)) {
return false
}
...

return super.canPerformAction(action, withSender: sender)
}

}

If you have any questions please ask!

Edit If you want to disable all actions but a few it may be easier to just return false in canPerformAction and return true for the ones you want like so:

override func canPerformAction(_ action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(copy(_:)) || action == #selector(customMethod(_:)) {
return true
}
...
return false
}

Where to add custom menu items to UIMenuController?

It turns out that it works fine to add the items in the app delegate. You need to make sure of course, that other Views supporting the MenuController return NO for your particular selector in -respondsToSelector:.

uimenucontroller in uiwebview with custom menu items without MORE menu

It doesn't appear that there is a way to do this without replacing the UIMenuController. One option is to handle your own UILongPressGestureRecognizer (see How to remove th COPY UIMenuItem in UIMenuController). I've seen proposals where you override canPerformAction, but this does not work. Interestingly, the "copy:" action is never called, though it seems that everything else (cut:,select:,etc.) is.

- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
if (action == @selector(defineSelection:))
{
return YES;
}
else if (action == @selector(translateSelection:))
{
return YES;
}
else if (action == @selector(copy:))
{
return NO;
}

return [super canPerformAction:action withSender:sender];
}

`

How to Remove UIMenuController Default Items In Swift

I tried this but it worked for my by subclassing the WebView and overriding canPerformAction method, inside which I manually removed the default options.

override func canPerformAction(_ action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(cut(_:)) {
return false
}
if action == #selector(paste(_:)) {
return false
}
if action == #selector(select(_:)) {
return false
}
if action == #selector(selectAll(_:)) {
return false
}
...

return super.canPerformAction(action, withSender: sender)
}

I referred to this answer by Ike10 and it had worked for me. Give it a shot.

Append custom UIMenuItems at the beginning of UIMenuController iOS

The UIMenuController documentation says:

Custom items appear in the menu after any system menu items.

So there is probably no approved way around that. It appears to be baked in functionality.

UIMenuController Custom Items

I think this is one of the few cases where you want to subclass UITextView. I just tried this with the following code, and the only menu item that is shown is my Do Something item.

From my TestViewController.m

@implementation TestViewController

- (void) doSomething: (id) sender
{
NSLog(@"Doing something");
}

- (void) viewDidLoad
{
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *item = [[[UIMenuItem alloc] initWithTitle: @"Do Something"
action: @selector(doSomething:)] autorelease];
[menuController setMenuItems: [NSArray arrayWithObject: item]];
}

@end

Code for my MyTextView.h:

//  MyTextView.h

#import <UIKit/UIKit.h>

@interface MyTextView :UITextView {

}

@end

Code for MyTextView.m:

//  MyTextView.m

#import "MyTextView.h"

@implementation MyTextView

- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
return NO;
}

@end

Custom UIMenuItem only showed first time

I have tried below code,It works to me.The point is before menu shows,add custom menu item, and then show the menu yourself.

-(void)viewDidLoad
{
[super viewDidLoad];

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillShow) name:UIMenuControllerWillShowMenuNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuDidShow) name:UIMenuControllerDidShowMenuNotification object:nil];

}

-(void)menuDidShow{

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillShow) name:UIMenuControllerWillShowMenuNotification object:nil];

}

-(void)menuWillShow{

UIMenuItem *shareMenu = [[UIMenuItem alloc] initWithTitle:@"微博分享" action:@selector(shareToWeibo:)];
UIMenuController *menu = [UIMenuController sharedMenuController];
[menu setMenuItems:[NSArray arrayWithObjects:shareMenu, nil]];

[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];

[menu setTargetRect:selectedRect inView:self.view];
//must set,otherwise menu location never changed

[menu setMenuVisible:YES animated:YES];

}

Custom UIMenuController using button event error There can only be one UIMenuController instance

Use the default singleton instance provided (.shared) by UIMenuController instead of creating an instance of your own.

@objc func holdButton(_ sender: UIButton) {
UIMenuController.shared.setTargetRect(sender.frame, in: charTextField)
UIMenuController.shared.setMenuVisible(true, animated: true)
}

Quoting from apple doc:

The singleton UIMenuController instance is referred to as the editing
menu.....

How could I replace UIMenuController with my own view when text is selected?

There are three important things you have to know before you can start:

1) You'll have to write your custom menu controller view, but I guess you kinda expected that. I only know of a commercial implementation of a custom menu controller, but this shouldn't be too hard.

2) There is a useful method on UIResponder called -canPerformAction:withSender:. Read more about it in the UIResponder Class Reference. You can use that method to determine whether your text view supports a specific standard action (defined in the UIResponderStandardEditActions protocol).

This will be useful when deciding which items to show in your custom menu controller. For example the Paste menu item will only be shown when the user's pasteboard contains a string to paste.

3) You can detect when the UIMenuController will be shown by listening to the UIMenuControllerWillShowMenuNotification notification.

Now that you know all of that, this is how I'd start tackling that:

1) Listen for UIMenuControllerWillShowMenuNotifications when the text view is first responder

- (void)textViewDidBeginEditing:(UITextView *)textView {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillBeShown:) name:UIMenuControllerWillShowMenuNotification object:nil];
}

- (void)textViewDidEndEditing:(UITextView *)textView {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];
}

2) Show your custom menu controller instead of the default UIMenuController

- (void)menuWillBeShown:(NSNotification *)notification {
CGRect menuFrame = [[UIMenuController sharedMenuController] menuFrame];
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; // Don't show the default menu controller

CustomMenuController *controller = ...;
controller.menuItems = ...;
// additional stuff goes here

[controller setTargetRectWithMenuFrame:menuFrame]; // menuFrame is in screen coordinates, so you might have to convert it to your menu's presenting view/window/whatever

[controller setMenuVisible:YES animated:YES];
}

Misc. 1) You can use a fullscreen UIWindow for showing your custom menu so it can overlap the status bar.

UIWindow *presentingWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
presentingWindow.windowLevel = UIWindowLevelStatusBar + 1;
presentingWindow.backgroundColor = [UIColor clearColor];

[presentingWindow addSubview:controller];
[presentingWindow makeKeyAndVisible];

Misc. 2) For determining which menu items to show you can use the mentioned -canPerformAction:withSender:

BOOL canPaste = [textView canPerformAction:@selector(paste:) withSender:nil];
BOOL canSelectAll = [textView canPerformAction:@selector(selectAll:) withSender:nil];

Misc. 3) You'll have to handle dismissing the menu yourself by using a UITapGestureRecognizer on the presenting window or something like that.

This won't be easy, but it's doable and I hope it works out well for you. Good luck!

Update:
A new menu implementation popped up on cocoacontrols.com today that you might want to check out: https://github.com/questbeat/QBPopupMenu

Update 2:
As explained in this answer you can get the frame of a text view's selected text using -caretRectForPosition:.



Related Topics



Leave a reply



Submit