Случается, что дизайнер или менеджер просит невозможное.
Надо значит надо.
Например, могут попросить показывать меню в процессе онбординга пользователю в новую фичу. Но 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.