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

后端 未结 2 528
-上瘾入骨i
-上瘾入骨i 2021-01-30 05:27

When text is selected, by default a UIMenuController pops up with cut/copy/paste etc.

\"enter

相关标签:
2条回答
  • 2021-01-30 06:25

    I think this may help you https://github.com/cxa/UIMenuItem-CXAImageSupport

    UIMenuItem uses UILabel to display its title, that means we can swizzle -drawTextInRect: to support image.

    UIMenuItem+CXAImageSupport is a dirty hack but should be safe in most cases. Contains no any private API.

    Make a category instead of subclassing for UIMenuItem gains more flexibility. Yes, this category can be applied to the awesome PSMenuItem too!

    enter image description here

    0 讨论(0)
  • 2021-01-30 06:27

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

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