Как заставить UIMenuController работать для пользовательского представления?

Я пытаюсь заставить работать следующий код:

UIMenuController * menu = [UIMenuController sharedMenuController];
[menu setTargetRect: CGRectMake(100, 100, 100, 100) inView: self.view];
[menu setMenuVisible: YES animated: YES];

Экземпляр меню готов, но не отображается - ширина всегда равна нулю.

Или есть пример кода в этой UIPasteboard/UIMenuController теме?


person al_lea    schedule 18.07.2009    source источник


Ответы (9)


Я не смог заставить его работать, даже когда прочитал все ваши ответы. Я представляю готовый код, который будет работать для всех.

Допустим, у нас есть класс контроллера с именем Controller. Вы можете просто вставить следующий код в этот контроллер, чтобы меню работало в его представлении:


- (void)loadView {
    [super loadView];

    UILongPressGestureRecognizer *gr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    [self.view addGestureRecognizer:gr];    
}

- (void) longPress:(UILongPressGestureRecognizer *) gestureRecognizer {
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
        CGPoint location = [gestureRecognizer locationInView:[gestureRecognizer view]];
        UIMenuController *menuController = [UIMenuController sharedMenuController];
        UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:@"Item" action:@selector(menuItemClicked:)];

        NSAssert([self becomeFirstResponder], @"Sorry, UIMenuController will not work with %@ since it cannot become first responder", self);
        [menuController setMenuItems:[NSArray arrayWithObject:resetMenuItem]];
        [menuController setTargetRect:CGRectMake(location.x, location.y, 0.0f, 0.0f) inView:[gestureRecognizer view]];
        [menuController setMenuVisible:YES animated:YES];
    }
}

- (void) copy:(id) sender {
    // called when copy clicked in menu
}

- (void) menuItemClicked:(id) sender {
    // called when Item clicked in menu
}

- (BOOL) canPerformAction:(SEL)selector withSender:(id) sender {
    if (selector == @selector(menuItemClicked:) || selector == @selector(copy:)) {
        return YES;
    }
    return NO;
}

- (BOOL) canBecomeFirstResponder {
    return YES;
}

Что нужно сделать, чтобы меню работало, так это то, что firstResponder (в нашем случае наш контроллер - см. Строку с [self beFirstResponder]) должен быть в состоянии стать первым ответчиком (метод переопределения canBecomeFirstResponder, потому что реализация по умолчанию возвращает NO), а также как - (BOOL) canPerformAction:(SEL)selector withSender:(id) sender, который должен возвращать YES на любое действие, которое может быть выполнено firstResponder

person krasnyk    schedule 06.10.2010
comment
Возможно, вы захотите изменить это утверждение, чтобы использовать canBecomeFirstResponder, и поместить становлениеFirstResponder вне утверждения;) - person antsyawn; 07.01.2011
comment
Это работает отлично. Сначала я немного беспокоился, что [self beFirstResponder] вызовет клавиатуру, но этого не произошло. Спасибо! - person R.S; 18.05.2013
comment
В методе копирования, как я могу получить выделенный текст? скопировать в UIPastboard - person Khaled Annajar; 11.05.2015

Если вы реализуете пользовательское представление, и это представление должно быть ответчиком (в отличие от какого-либо другого представления, такого как UITextField), вам необходимо переопределить функцию canBecomeFirstResponder в вашем представлении и вернуть YES:

- (BOOL)canBecomeFirstResponder {
    return YES;
}

затем, когда вы отображаете меню, вы должны сделать что-то вроде следующего:

- (void)myMenuFunc {
    if (![self becomeFirstResponder]) {
        NSLog(@"couldn't become first responder");
        return;
    }

    UIMenuController *theMenu = [UIMenuController sharedMenuController];
    CGRect selectionRect = CGRectMake(0, 0, 0, 0);
    [theMenu setTargetRect:selectionRect inView:self];
    [theMenu setMenuVisible:YES animated:YES];
}
person Cam    schedule 02.09.2009

На случай, если у кого-то все еще есть проблемы: Мое меню работало и однажды чудесным образом перестало работать. Все остальное в моем приложении все еще работало. Теперь я удалил метод [window makeKeyAndVisible] из метода application:didFinishLaunchingWithOptions:, и хотя все остальное по-прежнему работало, это ломает UIMenuController!

Глупая ошибка с моей стороны, сложно найти виновника...

person Pascal    schedule 21.09.2011
comment
Спасибо тебе за это. Оказывается, это случилось со мной, не знаю, как была удалена строка makeKeyAndVisible, но без нее я чуть не уничтожил свой монитор и использовал его острые края, чтобы зафиксировать Hari-Kari. - person Rob Booth; 24.05.2012
comment
Большое Вам спасибо. Я представлял лупу в собственном окне, и мне нужно было снова сделать ключ главного окна, чтобы UIMenuController работал. - person hatfinch; 12.06.2012

для отображения UIMenuController необходимо добавить следующее

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(cut:))
        return NO;
    else if (action == @selector(copy:))
        return YES;
    else if (action == @selector(paste:))
        return NO;
    else if (action == @selector(select:) || action == @selector(selectAll:))
        return NO;
    else
        return [super canPerformAction:action withSender:sender];
}
person JOA80    schedule 11.01.2010
comment
Можете ли вы сказать мне, как добавить опцию Forward в этом ?? Я ищу его, так что помогите мне в этом ..! - person Vivek2012; 25.01.2012

Я думаю, что Кэм прав, нужно переопределить как canPerformAction, так и canBecomeFirstResponder.

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

- (BOOL)canBecomeFirstResponder {
    return YES;
}
person Joe Yang    schedule 02.09.2010

// MyView.h

@interface MyView : UIView {
    IBOutlet UITextField * textField_;
}

@end

// MyView.m

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"show menu");

    [textField_ becomeFirstResponder];
    // [self.window becomeFirstResponder];

    UIMenuController * menu = [UIMenuController sharedMenuController];
    [menu setTargetRect: CGRectMake(0, 0, 100, 10) inView: self];
    [menu setMenuVisible: YES animated: YES];

    NSLog(@"menu width %f, visible %d", menu.menuFrame.size.width, menu.menuVisible);
}

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

Вам нужно добавить больше кода в canPerformAction:withSender: - он должен проверять монтажный стол и состояние вашего выбора. Руководство Apple по программированию приложений для iPhone содержит несколько фрагментов кода.

person al_lea    schedule 18.07.2009

Вам не нужно добавлять UIMenuController* menu к основному или подвиду, E.G. self.view? Я думаю, это что-то вроде [self.view addSubView:menu.view]; Или я не понимаю сути вашего вопроса. Вы также можете установить рамку представления меню.

person dlamblin    schedule 18.07.2009

UIMenuController не имеет представления. Я только что искал код в iPhone Application Programming. Руководство: обработка событий:

Листинг 3-4 Отображение меню редактирования

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *theTouch = [touches anyObject];

    if ([theTouch tapCount] == 2  && [self becomeFirstResponder]) {

        // selection management code goes here...

        // bring up editing menu.
        UIMenuController *theMenu = [UIMenuController sharedMenuController];
        CGRect selectionRect = CGRectMake(currentSelection.x, currentSelection.y, SIDE, SIDE);
        [theMenu setTargetRect:selectionRect inView:self];
        [theMenu setMenuVisible:YES animated:YES];
    }
}
person al_lea    schedule 18.07.2009
comment
комментарий имеет ограничение на количество символов, поэтому я публикую код здесь. - person al_lea; 18.07.2009
comment
только что нашел обходной путь: поместите невидимый UITextField в представление и сделайте его первым ответчиком. Тогда меню копирования-вставки появится корректно. - person al_lea; 18.07.2009
comment
У меня такая же проблема (мое пользовательское представление является подклассом UITableViewCell, но в остальном такая же ситуация). Невидимый UITextField мне не помогает. Меню по-прежнему не отображается, а в menuFrame по-прежнему все нули. Кроме того, клавиатура появляется всякий раз, когда я делаю UITextField firstResponder. Можете ли вы опубликовать пример кода для вашего обхода? - person James Williams; 18.07.2009

Я сделал это следующим образом ниже. Просто вызовите метод, который показывает меню после очень короткой задержки в инициализации. Я не хотел вызывать его из View Controller, а также не нашел события, указывающего на то, что появилось мое пользовательское представление, и я готов показать меню. Так что, с моей точки зрения, все в порядке. Задержка может быть меньше, но решать вам.

@implementation DTSignatureImageView

- (id)initWithImage:(UIImage *)image
{
    self = [super initWithImage:image];
    if(self){
        self.contentMode = UIViewContentModeScaleAspectFit;
        self.frame = CGRectMake(0, 0, image.size.width / 2.5, image.size.height / 2.5);
        self.userInteractionEnabled = YES;

        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(signatureDidPan:)];
        [self addGestureRecognizer:pan];

        [self becomeFirstResponder];

        [self performSelector:@selector(showMenu) withObject:nil afterDelay:0.5];
    }

    return self;
}

- (BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)showMenu
{
    UIMenuController *menu = [UIMenuController sharedMenuController];
    menu.menuItems = @[
       [[UIMenuItem alloc] initWithTitle:@"Apply" action:@selector(applySignature)],
       [[UIMenuItem alloc] initWithTitle:@"Update" action:@selector(updateSignature)],
       [[UIMenuItem alloc] initWithTitle:@"Clear" action:@selector(delegateSignature)]];
    [menu setTargetRect:self.bounds inView:self];
    [menu setMenuVisible:YES animated:YES];
}

- (NSArray *)menuActions
{
    static NSArray *actions = nil;
    if (actions == nil){
        actions = @[
                    NSStringFromSelector(@selector(applySignature)),
                    NSStringFromSelector(@selector(updateSignature)),
                    NSStringFromSelector(@selector(delegateSignature))];
    }

    return actions;
}

- (void) signatureDidPan: (UIPanGestureRecognizer *)gesture
{
    switch (gesture.state) {
        case UIGestureRecognizerStateBegan: {
            [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES];
            break;
        }

        case UIGestureRecognizerStateEnded: {
            [self becomeFirstResponder];
            [self showMenu];
        }

        default:
            break;
    }

    CGPoint point = [gesture locationInView:gesture.view.superview];
    gesture.view.center = point;
}
person Madman    schedule 03.06.2014