美文网首页
UITableViewCell中button跳转下一界面的几种方

UITableViewCell中button跳转下一界面的几种方

作者: Mi欧阳 | 来源:发表于2019-12-17 13:30 被阅读0次

开发中最为常见的功能,UITableViewCell中有个button需要跳转到下一个界面。
那么如何实现该功能,大致有三种办法:

PlanA:利用UIResponder响应链,寻找父层视图,直到找到UIViewController

概述:App使用响应者对象接收和处理事件,响应者对象是任何UIResponder的实例。UIResponder的子类包括UIView,UIViewController,UIApplication等。响应者接收到原始事件数据,必须处理事件或者转发到另一个响应者对象。当你的App接收到一个事件时,UIKit自动引导事件到最合适的响应者对象,也叫做第一响应者。

简单的说,我们一个界面会堆叠很多层的view,而点击事件一般会认为最上层的view是第一响应者。其他是二三四五六响应者。
我们可以通过nextResponder方法来获取这些响应者队列中的对象。

这种方法最好,低耦合,无循环引用的风险。(唯独对于不熟悉的人,阅读代码需要转个弯)

写法1:直接找下一个响应者对象

//只要是你的view在ViewController中,这条语句都会成功
//view在ViewController外,会陷入死循环,切记
-(UIViewController*)parentController{
    UIResponder *responder = [self nextResponder];
    while (responder) {
        if ([responder isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)responder;
        }
        responder = [responder nextResponder];
    }
    //返回nil时候默认不会执行跳转
    return nil;
}

写法2:先找当前view的superview,再找这个superview的下一个响应对象

//这种写法要求你的view在vc.view中
//因为这个写法会直接找self的superview,而[vc.view superview] = nil
- (UIViewController *)parentController{
    for (UIView *next = [self superview]; next; next = next.superview) {
        UIResponder *nextResponder = [next nextResponder];
        if ([nextResponder isKindOfClass:[UIViewController class]]) {
            return (UIViewController *)nextResponder;
        }
    }
    //返回nil时候默认不会执行跳转
    return nil;
}

注:其实代码片段里的循环次数不会很多,不用担忧消耗内存。

PlanB:把view或者nav作为属性传进cell中

cell无法跳转下一界面的问题,主要是拿不到vc或者nav,所以我们把对象传进cell中,并控制好循环引用就好。
这种方法最符合普通逻辑,但在某些复杂情况下,可能造成内存泄漏。

cell的.h文件中声明

@interface TestTableViewCell : UITableViewCell
//注意,这里一定是weak修饰符
@property(nonatomic,weak)UINavigationController *nav;
//注意,这里一定是weak修饰符
@property(nonatomic,weak)UIViewController *rootView;
@end

然后在vc中的tableView:cellForRowAtIndexPath:方法中如此写

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifier" forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%d",self.count];
    //只需要在cell中把nav属性声明为weak即可,这里不需要若引用
    cell.nav = self.navigationController;
    //只需要在cell中把rootView属性声明为weak即可,这里不需要若引用
    cell.rootView = self;
    //总结,外部是否使用弱应用对于cell无影响,cell中的属性设置weak才有效
    
    return cell;
}

然后即可正常跳转

-(IBAction)goNextView:(id)sender{
    TestTableViewController *testTableView = [[UIStoryboard storyboardWithName:@"TestStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:@"TestTableView"];
    //带nav执行跳转
    [self.nav pushViewController:testTableView animated:YES];
    //不带nav执行跳转
    [self.rootView presentViewController:testTableView animated:YES completion:nil];
}

PlanC:利用bolck方式把点击事件传回ViewController

最早,可能大家用的都是这个方法,但实际上他的缺点有三:
1.block有造成内存泄漏的风险,需要使用__weak修饰符
2.如果这个cell中包含多个按钮,就意味着每个按钮都需要一个点击block。增加代码量
3.如果这个cell被多个tableView使用,每个tableView都需要把block代码块带上,这就产生了重复代码。

TestTableViewCell.h

@interface TestTableViewCell : UITableViewCell
//block一般用copy修饰符
@property(nonatomic,copy)void (^buttonClickBlock)(void);

@end

TestTableViewCell.m

-(IBAction)goNextView:(id)sender{
    if (self.buttonClickBlock) {
        self.buttonClickBlock();
    }
}

然后在vc中的tableView:cellForRowAtIndexPath:方法中如此写

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifier" forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%d",self.count];
    //切记,这里一定创建弱引用的weakSelf,再在cell的block中使用,否则必定会内存泄漏
    __weak UIViewController *weakSelf = self;
    [cell setButtonClickBlock:^{
        TestTableViewController *testTableView = [[UIStoryboard storyboardWithName:@"TestStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:@"TestTableView"];
        [weakSelf.navigationController pushViewController:testTableView animated:YES];
    }];
    
    return cell;
}

附录:几种错误的写法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"identifier" forIndexPath:indexPath];
    cell.textLabel.text = [NSString stringWithFormat:@"%d",self.count];
    //经测试,如果cell中不对于nav属性声明weak,那么及时在此弱应用也是无效的
    __weak UINavigationController *weakNav = self.navigationController;
    cell.nav = weakNav;
    //经测试,如果cell中不对于rootView属性声明weak,那么及时在此弱应用也是无效的
    __weak UIViewController *weakSelf = self;
    cell.rootView = weakSelf;
    //总结,外部是否使用弱应用对于cell无影响,cell中的属性设置weak才有效
    
    return cell;
}

相关文章

网友评论

      本文标题:UITableViewCell中button跳转下一界面的几种方

      本文链接:https://www.haomeiwen.com/subject/fgydnctx.html