美文网首页
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