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;
        }
      }
    }
  }
}

 

posted @ 2020-09-17 22:47  天天晒日头  阅读(788)  评论(0)    收藏  举报