Как вручную показать UIMenu

Случается, что дизайнер или менеджер просит невозможное.

Надо значит надо.

Например, могут попросить показывать меню в процессе онбординга пользователю в новую фичу. Но UIMenu в iOS показываются только при нажатии на элемент, к которому оно прикреплено, и никак иначе.

Обращаемся к приватному API, но помним, что это запрещено Apple и на проверке ваше приложение может быть отклонено.

// ищем на кнопке нужный приватный жест
UIGestureRecognizer *gesture = nil;
for (UIGestureRecognizer *r in button.gestureRecognizers) {
    if ([r isKindOfClass:NSClassFromString(@"_UITouchDownGestureRecognizer")]) {
        gesture = r;
        break;
    }
}
// если таковой найден - ищем пары target-action
if (gesture != nil) { // https://stackoverflow.com/a/23944568/602249
    Ivar targetsIvar = class_getInstanceVariable([UIGestureRecognizer class], "_targets");
    id targetActionPairs = object_getIvar(gesture, targetsIvar);

    Class targetActionPairClass = NSClassFromString(@"UIGestureRecognizerTarget");
    Ivar targetIvar = class_getInstanceVariable(targetActionPairClass, "_target");
    Ivar actionIvar = class_getInstanceVariable(targetActionPairClass, "_action");

    for (id targetActionPair in targetActionPairs)
    {
        id target = object_getIvar(targetActionPair, targetIvar);
        SEL action = (__bridge void *)object_getIvar(targetActionPair, actionIvar);

        // обязательное условие - жест должен "стать" законченным!
        gesture.state = UIGestureRecognizerStateEnded;
        // запускаем действие жеста
        [[UIApplication sharedApplication] sendAction:action to:target from:gesture forEvent:nil];
    }
}

Как это стало возможно? Исследовать работу приватного кода можно в рантайм в моменте останова с помощью дебаггера lldb, испектора стектрейса, испектора UI иерархии, символических брейкпоинтов и графа памяти в Xcode. Но чтобы досконально разобрать в принципе работы чужого кода, я советую Hopper Disassembler. Он бесплатный в демо-версии, а её хватает за глаза для подобных изысканий. Бинарные файлы можно найти в директории вашего Mac:

/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks

Я исследовал библиотеку UIKitCore.framework, так как весь код, связанный с UIMenu, находится именно в ней.

Данный способ также опубликован на Stackoverflow.


Опубликовано

в

от

Метки: