Swizzles the iOS contextual menu and share sheet to improve usability by showing the icon on the leading side. Read more: https://douglashill.co/menu-icon-swizzling/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Douglas Hill, March 2020 | |
@import Foundation; | |
NS_ASSUME_NONNULL_BEGIN | |
/** | |
Swizzles a method with no parameters and no return value by adding in the passed in block to the method call. | |
This should be called once for each modification. | |
@param theClass The class on which the method should be swizzled. | |
@param selector The instance method of the class to swizzle. | |
@param blockToAdd The block that will be added in when the method is called. The original method implementation (which may call super) will run before this block. | |
@return Whether the swizzle was successfully applied. | |
*/ | |
BOOL swizzleVoidVoidMethod(Class theClass, SEL selector, void (^blockToAdd)(__unsafe_unretained id _self)); | |
NS_ASSUME_NONNULL_END |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Douglas Hill, March 2020 | |
#import "Swizzling.h" | |
@import ObjectiveC; | |
NS_ASSUME_NONNULL_BEGIN | |
#define let __auto_type const | |
BOOL swizzleVoidVoidMethod(Class classToSwizzle, SEL selector, void (^updateBlock)(__unsafe_unretained id _self)) { | |
let method = class_getInstanceMethod(classToSwizzle, selector); | |
// Bail if the method does not exist for this class or one of its parents. | |
if (method == nil) { | |
NSLog(@"Method %@ doesn’t exist on %@.", NSStringFromSelector(selector), classToSwizzle); | |
return NO; | |
} | |
// In case the method is only implemented by a superclass, add an implementation that just calls super. | |
// This won’t do anything if the target class already has the method. | |
let types = method_getTypeEncoding(method); | |
class_addMethod(classToSwizzle, selector, imp_implementationWithBlock(^(__unsafe_unretained id _self) { | |
struct objc_super _super = {_self, [classToSwizzle superclass]}; | |
return ((id(*)(struct objc_super *, SEL))objc_msgSendSuper)(&_super, selector); | |
}), types); | |
// Swizzle the method to first call the original implementation and then call the custom block. | |
__block IMP originalImp = class_replaceMethod(classToSwizzle, selector, imp_implementationWithBlock(^(__unsafe_unretained id _self) { | |
((void (*)(id, SEL))originalImp)(_self, selector); | |
updateBlock(_self); | |
}), types); | |
return originalImp != NULL; | |
} | |
NS_ASSUME_NONNULL_END |
This has been tested on iOS 13 and 14 and doesn’t yet work with iOS 15. On iOS 15, classes have been renamed but also the view hierarchy has changed, and so far I didn’t manager to make it work in all cases.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, Doug. I'm trying out this class but it's bailing out on trying to find contextMenuActionViewClass. XCode version 13.3.1 (13E500a). Any ideas? TIA