iOS 修改UITableView长按Cell弹出菜单位置及名称
一、修改弹出菜单位置
系统默认的弹出菜单位置在单元格顶部或底部,指示箭头刚好指向边缘位置,这就导致弹出框的主体出现在另一个单元格上面,容易造成误解,而且我也觉得不好看,所以想修改下位置,并进行了以下尝试。
首先,UIMenuController类中menuFrame是readonly状态,不能直接通过该属性修改位置,也无法获取对应的变量名,不能通过KVC修改;
其次,也没有比较好的时机去调用方法来修改位置,在UITableViewDelegate的`tableView:canPerformAction:forRowAtIndexPath:withSender:`方法中,或者UIMenuControllerWillShowMenuNotification通知中,都能获取UIMenuController的单例,但无法通过调用`setTargetRect:inView:`方法来修改位置;再次,通过查看弹出菜单的分层视图,知道菜单的显示视图名称为UICalloutBar,而该视图的实例对象可以在UITableViewDelegate的`tableView:canPerformAction:forRowAtIndexPath:withSender:`方法中获取到,在UIMenuControllerWillShowMenuNotification通知中,该视图的位置也已经确定,但无法通过修改其frame的方式来改变位置,因为会出现视图被遮挡的现象,具体原因不明;最后,通过runtime来交换UIMenuController类中`setTargetRect:inView:`方法的实现来修改位置,
具体代码如下:
#import "UIMenuController+Extension.h" #import <objc/runtime.h> @implementation UIMenuController (Extension) + (void)initialize { [super initialize]; Method originalMethod = class_getInstanceMethod(self, @selector(setTargetRect:inView:)); Method replaceMethod = class_getInstanceMethod(self, @selector(resetTargetRect:inView:)); method_exchangeImplementations(originalMethod, replaceMethod); } - (void)resetTargetRect:(CGRect)targetRect inView:(UIView *)targetView { // 放在目标视图中间高度位置 targetRect.origin.y += targetRect.size.height / 2 + 10; [self resetTargetRect:targetRect inView:targetView]; } @end
二、修改弹出菜单名称
通过在UITableViewDelegate的`tableView:canPerformAction:forRowAtIndexPath:withSender:`方法中获取UICalloutBar的实例对象,并作为属性保存下来,然后在UIMenuControllerWillShowMenuNotification通知中修改名称。
这样做的原因,是因为在ITableViewDelegate中获取UICalloutBar的实例对象时,其包含的UICalloutBarButton按钮视图并不是最终显示的按钮,需要等到在通知中,其最终显示的按钮才会在subviews数组中依次加上去,所以需要在通知中修改按钮的名称,也即菜单名称,具体代码如下:
@interface MenuTestController () < UITableViewDataSource, UITableViewDelegate > @property (nonatomic, strong) UITableView *tableView; // 分配列表 @property (nonatomic, strong) UIView *calloutBar; // 菜单视图 @end - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillShow:) name:UIMenuControllerWillShowMenuNotification object:nil]; } #pragma mark - UITableViewDelegate - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { return YES; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { if ([sender isKindOfClass:NSClassFromString(@"UICalloutBar")]) { self.calloutBar = sender; } return action == @selector(copy:); } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { UIPasteboard *paste = [UIPasteboard generalPasteboard]; paste.string = @"lalalalala\nlalalalalall"; } - (void)menuWillShow:(NSNotification *)notice { UIButton *button = [[self.calloutBar subviews] lastObject]; [button setTitle:@"复制" forState:UIControlStateNormal]; }
其实,也可以通过window查找到当前弹出菜单实例对象,进而修改菜单名称,代码如下:
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillShow:) name:UIMenuControllerWillShowMenuNotification object:nil]; } - (void)menuWillShow:(NSNotification *)notice { for (UIWindow *window in [UIApplication sharedApplication].windows) { if ([window isKindOfClass:NSClassFromString(@"UITextEffectsWindow")]) { for (UIView *view in window.subviews) { if ([view isKindOfClass:NSClassFromString(@"UICalloutBar")]) { UIButton *button = [[view subviews] lastObject]; [button setTitle:@"黏贴" forState:UIControlStateNormal]; return; } } } } }

浙公网安备 33010602011771号