You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何跨视图层级适配布局?Cell内菜单移至控制器视图的实现方案

更自适应的菜单视图布局实现方案

首先得给你点个赞,已经意识到硬编码坐标的问题了——这种方式在Cell高度变化、屏幕旋转或者有导航栏/状态栏调整时很容易出bug。下面我就给你拆解怎么用convertRect:toView:这类方法实现完全自适应的布局,同时把菜单视图移到控制器层面管理。

第一步:重构Cell的点击事件传递

先把Cell里的菜单视图代码删掉,改成通过Block/代理把点击事件传递给控制器,这样控制器能拿到触发点击的按钮和Cell,方便后续计算坐标:

// ZBMyProductsCell.h
typedef void(^OperationButtonTapBlock)(ZBMyProductsCell *cell, UIButton *tapButton);

@interface ZBMyProductsCell : UITableViewCell
@property (nonatomic, copy) OperationButtonTapBlock operationTapBlock;
@end

// ZBMyProductsCell.m
@implementation ZBMyProductsCell
- (void)awakeFromNib {
    [super awakeFromNib];
    // 移除原来的_operationMenu初始化代码
}

- (IBAction)operationButtonClick:(UIButton *)sender {
    if (self.operationTapBlock) {
        self.operationTapBlock(self, sender);
    }
}
@end

第二步:控制器层面管理菜单视图

在控制器里初始化菜单视图,默认隐藏并添加到控制器的view上:

// 控制器类中
@property (nonatomic, strong) ProductsOperationMenu *operationMenuView;
@property (nonatomic, weak) NSLayoutConstraint *menuLeadingConstraint;
@property (nonatomic, weak) NSLayoutConstraint *menuBottomConstraint;

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 初始化菜单视图,用Auto Layout管理
    self.operationMenuView = [[ProductsOperationMenu alloc] initWithFrame:CGRectZero];
    self.operationMenuView.hidden = YES;
    self.operationMenuView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:self.operationMenuView];
    
    // 固定菜单的宽高
    [self.operationMenuView.widthAnchor constraintEqualToConstant:205].active = YES;
    [self.operationMenuView.heightAnchor constraintEqualToConstant:60].active = YES;
    
    // 初始化动态约束(后续会修改)
    self.menuBottomConstraint = [self.operationMenuView.bottomAnchor constraintEqualToAnchor:nil constant:0];
    self.menuLeadingConstraint = [self.operationMenuView.leadingAnchor constraintEqualToAnchor:nil constant:0];
    self.menuBottomConstraint.active = YES;
    self.menuLeadingConstraint.active = YES;
}

第三步:用坐标转换实现自适应布局

在控制器的Cell回调里,利用convertPoint:toView:/convertRect:toView:把按钮的坐标从Cell坐标系转换到控制器的view坐标系,再动态调整菜单的位置:

// 在tableView:cellForRowAtIndexPath:中绑定点击Block
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ZBMyProductsCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ZBMyProductsCell" forIndexPath:indexPath];
    __weak typeof(self) weakSelf = self;
    cell.operationTapBlock = ^(ZBMyProductsCell *cell, UIButton *tapButton) {
        [weakSelf handleOperationButtonTap:tapButton fromCell:cell];
    };
    return cell;
}

// 核心处理方法
- (void)handleOperationButtonTap:(UIButton *)button fromCell:(ZBMyProductsCell *)cell {
    BOOL shouldShowMenu = !self.operationMenuView.isHidden;
    
    // 隐藏菜单的逻辑
    if (!shouldShowMenu) {
        [UIView animateWithDuration:0.25 animations:^{
            // 把菜单滑回按钮右侧外
            self.menuLeadingConstraint.constant += 205 + 10;
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
            self.operationMenuView.hidden = YES;
        }];
        return;
    }
    
    // 显示菜单的逻辑:转换按钮坐标到控制器view
    // 获取按钮底部在控制器view中的坐标
    CGPoint buttonBottomPoint = [button convertPoint:CGPointMake(0, button.bounds.size.height) toView:self.view];
    // 获取按钮左侧在控制器view中的x坐标
    CGFloat buttonLeftX = [button convertPoint:CGPointZero toView:self.view].x;
    
    // 设置菜单初始位置(在按钮右侧外)
    self.operationMenuView.hidden = NO;
    self.menuBottomConstraint.constant = buttonBottomPoint.y;
    self.menuLeadingConstraint.constant = buttonLeftX + 10;
    [self.view layoutIfNeeded];
    
    // 动画滑到按钮左侧
    [UIView animateWithDuration:0.25 animations:^{
        self.menuLeadingConstraint.constant = buttonLeftX - 205 - 10;
        [self.view layoutIfNeeded];
    }];
}

为什么这种方式更自适应?

  1. 脱离硬编码依赖:不再依赖Cell高度、屏幕尺寸这类固定值,不管Cell怎么复用、滚动,坐标转换都会自动处理TableView的偏移量。
  2. 适配布局变化:屏幕旋转、导航栏隐藏/显示时,convert方法会自动调整坐标,菜单位置始终和按钮对齐。
  3. Auto Layout友好:用约束动画替代frame动画,更符合iOS的布局规范,避免AutoresizingMask和Auto Layout冲突的问题。

如果需要点击菜单外区域隐藏菜单,还可以给控制器view添加一个全屏的点击手势,在手势回调里触发隐藏逻辑即可。

内容的提问来源于stack exchange,提问作者dengApro

火山引擎 最新活动