Как заставить UIMenuController работать для пользовательского представления?
Я пытаюсь выполнить следующую работу кода:
UIMenuController * menu = [UIMenuController sharedMenuController];
[menu setTargetRect: CGRectMake(100, 100, 100, 100) inView: self.view];
[menu setMenuVisible: YES animated: YES];
Экземпляр меню готов, но он не отображается - ширина всегда равна нулю.
Или есть ли пример кода в этом теге UIPasteboard/UIMenuController
?
Ответы
Ответ 1
Мне не удалось заставить его работать, даже когда я прочитал все ваши ответы. Я представляю готовый код, который будет работать для всех.
Скажем, у нас есть класс контроллера с именем 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 стать FirstResponder]) должен быть в состоянии стать первым ответчиком (метод переопределения canBecomeFirstResponder вызывает выполнение по умолчанию возвращает NO), а также - (BOOL) canPerformAction:(SEL)selector withSender:(id) sender
, который должен возвращать YES для любого действия, которое может быть выполнено firstResponder
Ответ 2
Если вы реализуете пользовательский вид, и этот вид должен быть ответчиком (в отличие от какого-либо другого представления, такого как 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];
}
Ответ 3
В случае, если у кого-то все еще есть проблемы: мое меню использовалось для работы и в какой-то день прекратило работать чудесным образом. Все остальное в моем приложении все еще работало. Теперь я удалил метод [window makeKeyAndVisible]
из метода application:didFinishLaunchingWithOptions:
, и пока все остальное все еще работает, это прерывает UIMenuController
!
Глупые ошибки на моей стороне, трудно найти виновника...
Ответ 4
для отображения 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];
}
Ответ 5
Я думаю, что Cam прав, нужно переопределить как canPerformAction, так и canBecomeFirstResponder
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
if (action == @selector(doSomething:)) {
return YES;
}
return NO;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
Ответ 6
// 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 содержит несколько фрагментов кода.
Ответ 7
Вам не нужно добавлять UIMenuController* menu
к основному или подзону, E.G. self.view
?
Я думаю, что-то вроде [self.view addSubView:menu.view];
Или я пропущу точку вашего вопроса. Вы также можете установить рамку в виде меню.
Ответ 8
UIMenuController не имеет представления. Я просто искал код из apple Руководство по программированию приложений для iPhone: Обработка событий:
Листинг 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];
}
}
Ответ 9
Я сделал это следующим образом. Просто вызовите метод, который показывает меню после очень короткой задержки в init. Я не хотел вызывать его из 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;
}