父子控制器

控制器之间可以建立一个联系

  • 在项目中不是所有的Tabbar都在底部,不是所有的控制器都用tabbarcontroller来管理(默认是在底部)。这时候我们需要自定义一个控制器管理。

父子控制器逻辑 如图所示的父子控制器的逻辑。点击0切换控制器0.这个时候我们就需要自定义管理。

实现阶段1


#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {
    JXOneController * one = [[JXOneController alloc] init];
    one.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:one.view];
}
- (IBAction)two {
    JXTwoController * one = [[JXTwoController alloc] init];
    one.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:one.view];
}
- (IBAction)three {
    JXThreeController * one = [[JXThreeController alloc] init];
    one.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:one.view];
}

问题:

1.多次点击导航栏上的三个按钮,会创建很多个控制器的view

2.页面one的tableView网上拖动内容会消失

3.在任意界面添加一个按钮,点击按钮之后会crash(在最新的xcode中只是不会crash,但是不能响应点击事件)

解决:

1.因为每次点击都会重新创建一个控制器,并将控制器的view添加到跟控制器

2.one拖动之后消失的原因是,当控制器创建,但是点击方法调用完毕之后控制器就会释放,虽然有将控制器的view添加到跟控制器的view上,但是控制器已经释放。这时候view还在内存中,所以就是控制器已经释放,但是view还在内存中。。拖动的时候回调用数据源的方法来添加数据,数据源的代理是控制器,已经释放,所以没办法提供数据。又因为代理方法是弱引用,所以不会crash

3.任意界面添加按钮,点击之后crash是因为控制器已经释放,当我点击按钮的时候,会调用控制器的来响应,但是此时控制器已经释放

可以使用代码来检测控制器是否释放

- (void)dealloc {
    NSLog(@"%s",__func__);
}

改进阶段

针对控制器一创建,方法执行完成之后就会死亡,我们可以使用全局变量的强引用来创建

#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()
/** one */
@property (nonatomic,strong) JXOneController * oneController;
/** two */
@property (nonatomic,strong) JXTwoController * twoController;
/** three */
@property (nonatomic,strong) JXThreeController * threeController;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {

    self.oneController = [[JXOneController alloc] init];
    self.oneController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.oneController.view];
}
- (IBAction)two {
    self.twoController = [[JXTwoController alloc] init];
    self.twoController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.twoController.view];
}
- (IBAction)three {
    self.threeController = [[JXThreeController alloc] init];
    self.threeController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.threeController.view];
}

@end

问题:

1.虽然第一次创建之后不会立即释放,但是当我们创建之后再次点击导航栏按钮的时候,之前的控制器已然会被释放

解决:

1.控制器已然会被释放是因为当再次点击创建的时候会再次创建一个控制器,这时候之前的控制器指针会被覆盖

改进方法2

针对会第二次创建覆盖第一次创建的指针导致之前创建的控制器释放,我们可以只允许控制器创建一个

这里有两种方法,一种是懒加载,一种就是直接在代码中创建

#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()
/** one */
@property (nonatomic,strong) JXOneController * oneController;
/** two */
@property (nonatomic,strong) JXTwoController * twoController;
/** three */
@property (nonatomic,strong) JXThreeController * threeController;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {
    if (self.oneController == nil) {

        self.oneController = [[JXOneController alloc] init];
        self.oneController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }
    [self.view addSubview:self.oneController.view];
}
- (IBAction)two {
    if (self.twoController == nil) {

        self.twoController = [[JXTwoController alloc] init];
        self.twoController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }
    [self.view addSubview:self.twoController.view];
}
- (IBAction)three {
    if (self.threeController == nil) {

        self.threeController = [[JXThreeController alloc] init];
        self.threeController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }
    [self.view addSubview:self.threeController.view];
}

解释

if (self.threeController == nil) {

        self.threeController = [[JXThreeController alloc] init];
        self.threeController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }
    [self.view addSubview:self.threeController.view];

这段代码中view添加到根控制器view上,写到外面,是因为同一个view加到同一个控制器上不管多少次都是添加一次,因为是同一个地址。

问题:

**1.这虽然只是创建了一次控制器,但是会有三个view层叠显示,虽然我们看不出来有任何变化,但是iOS会渲染,消耗性能绘制,如果是隐藏状态下就不会有性能损耗来渲染,绘制了**

解决:

**1.一个思路就是当显示当前的view,移除之前的view
(removeFromSuperView),虽然是移除但是并没有销毁,已然在内存中存在**

改进方法3

需在添加view之前移除其他控制器就可以实现目的,只有一个控制器的view。不会有重叠view显示

#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()
/** one */
@property (nonatomic,strong) JXOneController * oneController;
/** two */
@property (nonatomic,strong) JXTwoController * twoController;
/** three */
@property (nonatomic,strong) JXThreeController * threeController;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {
    // 创建控制器
    if (self.oneController == nil) {

        self.oneController = [[JXOneController alloc] init];
        self.oneController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    // 移除其他的view
    [self.twoController.view removeFromSuperview];
    [self.threeController.view removeFromSuperview];

    // 添加控制器的view
    [self.view addSubview:self.oneController.view];
}
- (IBAction)two {
    if (self.twoController == nil) {

        self.twoController = [[JXTwoController alloc] init];
        self.twoController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    [self.oneController.view removeFromSuperview];
    [self.threeController.view removeFromSuperview];

    [self.view addSubview:self.twoController.view];

}
- (IBAction)three {
    if (self.threeController == nil) {

        self.threeController = [[JXThreeController alloc] init];
        self.threeController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    [self.oneController.view removeFromSuperview];
    [self.twoController.view removeFromSuperview];

    [self.view addSubview:self.threeController.view];
}

@end

控制器大小。

控制器大小并不是占据真个屏幕大小,只需要设置其view.frame大小就可设置控制器大小。 衡量一块控件的是否需要设置控制器来管理只需要衡量其业务逻辑是不是独立的,但是同样可以自定义一个view也行

功能扩展

移除当前控制器的view,添加新的控制器view

基本功能已经实现,但是如果后期需要扩展,添加一个按钮的时候,就会很麻烦

思路:

实际上每次我们需要移除的只是当前的view,所以我们只需要再次定义一个属性来保存当前的view
又因为当前的view都是用强指针引用,所以我们这里只需要用一个弱指针来引用就可以
#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()
/** one */
@property (nonatomic,strong) JXOneController * oneController;
/** two */
@property (nonatomic,strong) JXTwoController * twoController;
/** three */
@property (nonatomic,strong) JXThreeController * threeController;
/** 引用当前的控制器的view */
@property (nonatomic,weak) UIViewController * currentView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {
    // 创建控制器
    if (self.oneController == nil) {

        self.oneController = [[JXOneController alloc] init];
        self.oneController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    // 移除其他的view
//    [self.twoController.view removeFromSuperview];
//    [self.threeController.view removeFromSuperview];

    // 将当前控制器的view移除
    [self.currentView.view removeFromSuperview];
    // 添加控制器的view
    [self.view addSubview:self.oneController.view];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.oneController;
}
- (IBAction)two {
    if (self.twoController == nil) {

        self.twoController = [[JXTwoController alloc] init];
        self.twoController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    [self.currentView.view removeFromSuperview];

    [self.view addSubview:self.twoController.view];
    self.currentView = self.twoController;

}
- (IBAction)three {
    if (self.threeController == nil) {

        self.threeController = [[JXThreeController alloc] init];
        self.threeController.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    }

    [self.currentView.view removeFromSuperview];

    [self.view addSubview:self.threeController.view];
    self.currentView = self.threeController;
}

@end

添加新的一个控制器,这时候如果我们添加一个按钮,就需要添加属性,然后初始化等操作,这里我们做一个优化

思路:

这里我们可以直接设置一个强引用属性数组,用来保存我们一共需要展示的控制器。这样就不需要每次添加都要设置属性等操作
#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()

/** 引用当前的控制器的view */
@property (nonatomic,weak) UIViewController * currentView;

/** 保存控制器数组 */
@property (nonatomic,strong) NSArray * arrayVc;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.arrayVc = @[
                     [[JXOneController alloc] init],
                     [[JXTwoController alloc] init],
                     [[JXThreeController alloc] init]
                     ];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {

    // 将当前控制器的view移除
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.arrayVc[0];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    // 添加控制器的view
    [self.view addSubview:self.currentView.view];
}
- (IBAction)two {
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.arrayVc[1];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.currentView.view];

}
- (IBAction)three {


    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.arrayVc[2];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.currentView.view];
}

@end

获取控件中某个子控件的个数方法

NSUInteger count = button.superview.subviews;

获取控件中某个子控件在这个子控件中数组的引导下标

NSUInteger count = [button.superview.subviews indexOfObject:button];

功能缺点说明

虽然我们已经完成了部分功能的实现,但是有个系统性的缺陷就是我们没办法监听系统性的操作,比如旋转屏幕,摇动手机等

原因:

因为我们这些功能都被窗口的根控制器拦截,因为传递事件的方式是从上往下传递。
从UIApplication先感应到,然后传到窗口,然后传到根控制器。
还是因为窗口的根控制器跟我们创建的控制器没有父子关系,所以事件就没必要传递到我们创建的控制器上

解决办法:

创建他们之间的关系
    self.childViewControllers = @[
                                  [[JXOneController alloc] init],
                                  [[JXTwoController alloc] init],
                                  [[JXThreeController alloc] init]
                                  ];

这个方法会报错,因为 childViewControllers是readonly属性

系提供另外一个方法来进行添加

  [self addChildViewController:[[JXOneController alloc] init]];
    [self addChildViewController:[[JXTwoController alloc] init]];
    [self addChildViewController:[[JXThreeController alloc] init]];
#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()

/** 引用当前的控制器的view */
@property (nonatomic,weak) UIViewController * currentView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self addChildViewController:[[JXOneController alloc] init]];
    [self addChildViewController:[[JXTwoController alloc] init]];
    [self addChildViewController:[[JXThreeController alloc] init]];
        // 将JXOneController从父控制器中移除
//    [self.childViewControllers[0] removeFromParentViewController];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {

    // 将当前控制器的view移除
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[0];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    // 添加控制器的view
    [self.view addSubview:self.currentView.view];
}
- (IBAction)two {
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[1];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.currentView.view];

}
- (IBAction)three {


    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[2];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:self.currentView.view];
}

@end

直接用self.childViewControllers[2]来读取,不需要用我们自定义的数组来存储。这样做的好处是不仅进行了存储,还建立了控制器之间的父子关系

总结:如果两个控制器的view是父子关系,那么这两个控制器也应该是父子关系

[a.view addSubView:b.view]
[a addChildViewController:b]

将控制器从父控制器中移除

// one 这个控制器从父控制器中移除
[one removeFromParentViewController];

特殊情况

一般情况:一般情况下,控制器在哪个控制器上就应该设置父子关系。例如a控制器的view上有两个控制器b,c的view。那么b,c就应该成为a控制器的子控制器。b上有c,d两个控制器的view。那么c,d最好成为b控制器的子控制器,这样方便管理

特殊情况:如果a控制器的view上先放置一个普通的UIView。然后这个普通的UIView上面继续放一个b控制器的view。那么这个b控制器就需要设置成为a控制器的子控制器

添加动画

- (IBAction)one {

    // 将当前控制器的view移除
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[0];
    self.currentView.view.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    // 添加控制器的view
    [self.view addSubview:self.currentView.view];
    // 添加动画
    CATransition * animation = [CATransition animation];
    animation.type = @"cube";
    animation.duration = 0.7;
    animation.subtype = kCATransitionFromLeft;
    [self.view.layer addAnimation:animation forKey:nil];
}

包装

有时候在我们控件外层继续包装一个控件,比较方便我们操作,改以后的代码的位置等


#import "ViewController.h"
#import "JXOneController.h"
#import "JXTwoController.h"
#import "JXThreeController.h"


@interface ViewController ()

/** 引用当前的控制器的view */
@property (nonatomic,weak) UIViewController * currentView;
/** 包装容器view */
@property (nonatomic,weak) UIView * contentView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView * contentView = [[UIView alloc] init];
    contentView.frame = CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64);
    [self.view addSubview:contentView];
    self.contentView = contentView;

    [self addChildViewController:[[JXOneController alloc] init]];
    [self addChildViewController:[[JXTwoController alloc] init]];
    [self addChildViewController:[[JXThreeController alloc] init]];

    // 将JXOneController从父控制器中移除
//    [self.childViewControllers[0] removeFromParentViewController];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
- (IBAction)one {

    // 将当前控制器的view移除
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[0];
    self.currentView.view.frame = self.contentView.bounds;
    // 添加控制器的view
    [self.contentView addSubview:self.currentView.view];
    // 添加动画
    CATransition * animation = [CATransition animation];
    animation.type = @"cube";
    animation.duration = 0.7;
    animation.subtype = kCATransitionFromLeft;
    [self.contentView.layer addAnimation:animation forKey:nil];
}
- (IBAction)two {
    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[1];
    self.currentView.view.frame = self.contentView.bounds;
    [self.contentView addSubview:self.currentView.view];
    CATransition * animation = [CATransition animation];
    animation.type = @"cube";
    animation.duration = 0.7;
    animation.subtype = kCATransitionFromRight;
    [self.contentView.layer addAnimation:animation forKey:nil];
}
- (IBAction)three {


    [self.currentView.view removeFromSuperview];
    // 将添加的控制器赋值给引用控制器
    self.currentView = self.childViewControllers[2];
    self.currentView.view.frame = self.contentView.bounds;
    [self.contentView addSubview:self.currentView.view];
    CATransition * animation = [CATransition animation];
    animation.type = @"cube";
    animation.duration = 0.7;
    animation.subtype = kCATransitionFromRight;
    [self.contentView.layer addAnimation:animation forKey:nil];
}

@end

这种将控制器的view添加到包装的view上,旋转的时候直接旋转的就是内容页的大小,不用带动导航栏一同旋转

添加子控制器的view到父控件view上尺寸问题

    // 默认情况下所有控制器的view的autoresizingMask都包含了UIViewAutoresizingFlexibleWidth和UIViewAutoresizingFlexibleHeight两个值
    self.currentView.view.autoresizingMask = UIViewAutoresizingNone;

例如我们设置一个控制器用xib来设置,但是xib设置初始值尺寸没有改变假设为600 X 600 。

那么加载调用的时候在viewDidLoad方法中刚加载的时候的尺寸就是为600 X 600 。

当加载完成屏幕的尺寸假设为 375 X 667 。

那么这时候因为有UIViewAutoresizingFlexibleWidth和UIViewAutoresizingFlexibleHeight这两个属性。所以可能他的width会按比例(375:600)缩很小,但是height可能会按比例变大

view.autoresizingMask = UIViewAutoresizingNone就是取消自带的两个属性

父子关系添加

// 当前控制器已经被添加到某个父控制器上会调用这个方法
- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];
}

可以在其中添加判断,不同控制器添加不同操作。 但是需要在添加到子控制器的时候

[self addChildViewController:[[JXOneController alloc] init]];

调用这个方法之后,才能重写上面那个方法

[self didMoveToParentViewController:UINavigationController];

示例:层级菜单

层级菜单就是一个页面有两个菜单,例如艺龙的房间选着。左边一个是一级菜单,右边的是二级菜单

思路:

有三种做法:
1.就是直接左右两边有两个tableViewController控制器,分别管理两个菜单,放到跟控制上
2.左边或者右边是tableView,另外一边是一个tableViewController控制器。添加到跟控制器上,同事跟控制器为tableView的数据源代理。
3.两边都是tableView,放到跟控制器上,跟控制器分别为两个tableView的数据源代理。

实现

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

ViewController.m

#import "ViewController.h"
#import "JXCategoryController.h"
#import "JXCategorySubController.h"


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setup];
}

- (void)setup {

    CGFloat width = self.view.frame.size.width * 0.5;
    CGFloat height = self.view.frame.size.height - 64;
    // 添加一级菜单
    JXCategoryController * category = [[JXCategoryController alloc] init];
    // 添加父子控制器
    [self addChildViewController:category];
    category.view.frame = CGRectMake(0, 64, width, height);
    [self.view addSubview:category.view];


    JXCategorySubController * sub = [[JXCategorySubController alloc] init];
    [self addChildViewController:sub];
    sub.view.frame = CGRectMake(width, 64, width, height);
    [self.view addSubview:sub.view];

}
@end

JXCategoryController.h

#import <UIKit/UIKit.h>

@interface JXCategoryController : UITableViewController

@end

JXCategoryController.m

#import "JXCategoryController.h"
#import "JXCategoryModel.h"
@interface JXCategoryController ()
/** 一级菜单数组 */
@property (nonatomic,strong) NSMutableArray * items;

@end

@implementation JXCategoryController

static NSString * identifier = @"cell";

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:identifier];
}

- (NSMutableArray *)items {
    if (_items == nil) {
        _items = [NSMutableArray array];
        NSArray * dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"categories" ofType:@"plist"]];
        NSMutableArray * categorys = [NSMutableArray array];
        for (NSDictionary * dict in dictArray) {
            JXCategoryModel * item = [JXCategoryModel categoryModelWithDict:dict];
            [categorys addObject:item];
        }
        _items = categorys;
    }
    return _items;
}
#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.items.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    JXCategoryModel * item = self.items[indexPath.row];
    // 图片
    cell.imageView.image = [UIImage imageNamed:item.icon];
    // 高亮图片(cell选中 --> cell.imageView.highlighted=YES --> cell.imageView显示highlightedImage)
    cell.imageView.highlightedImage = [UIImage imageNamed:item.highlighted_icon];
    cell.textLabel.text = item.name;
    cell.textLabel.highlightedTextColor = [UIColor redColor];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    JXCategoryModel * item = self.items[indexPath.row];
    NSArray * array = item.subcategories;
    if ([self.delegate respondsToSelector:@selector(categoryController:didSelectedSubcategories:)]) {
        [self.delegate categoryController:self didSelectedSubcategories:array];
    }
}

@end

JXCategorySubController.h

#import <UIKit/UIKit.h>
#import "JXCategoryController.h"

@interface JXCategorySubController : UITableViewController<JXCategoryControllerDelegate>

@end

JXCategorySubController.m

#import "JXCategorySubController.h"

@interface JXCategorySubController ()
/** 子类 */
@property (nonatomic,strong) NSArray * subArray;
@end

@implementation JXCategorySubController

static NSString * indetifier = @"subCell";

- (void)viewDidLoad {
    [super viewDidLoad];


    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:indetifier];

}

#pragma mark - Table view data source



- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.subArray.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indetifier];

    cell.textLabel.text = self.subArray[indexPath.row];

    return cell;
}



#pragma mark - 代理方法
- (void)categoryController:(JXCategoryController *)categoryController didSelectedSubcategories:(NSArray *)subcategories {
    self.subArray = subcategories;
    [self.tableView reloadData];
}

@end


@end

JXCategoryModel.h


#import <Foundation/Foundation.h>

@interface JXCategoryModel : NSObject
/** 选中图像 */
@property (nonatomic,strong) NSString * highlighted_icon;
/** 图像 */
@property (nonatomic,strong) NSString * icon;
/** 名称 */
@property (nonatomic,strong) NSString * name;
/** 内容数组 */
@property (nonatomic,strong) NSArray * subcategories;

+ (instancetype)categoryModelWithDict:(NSDictionary *)dict;
@end

JXCategoryModel.m

#import "JXCategoryModel.h"

@implementation JXCategoryModel
+ (instancetype)categoryModelWithDict:(NSDictionary *)dict {
    JXCategoryModel * model = [[self alloc] init];

    [model setValuesForKeysWithDictionary:dict];

    return model;
}
@end

实现思路:

1.设置左右两个控制器
2.点击左边的某一行的时候需要右边做出相应的相应,所以右边的控制器应该监听左边的数据变化,所以右边的控制器应该设置为左边控制器的代理

小知识点

图片也有高亮状态

    cell.imageView.highlightedImage = [UIImage imageNamed:item.highlighted_icon];
    cell.textLabel.text = item.name;
    cell.textLabel.highlightedTextColor = [UIColor redColor];

出现的问题

直接将两个口控制器添加到控制器上的时候,当根控制器为一个导航控制器的时候,会出现一个问题就是,两个并排的控制器添加,第一个控制器会默认,向下偏移64,但是第二个控制器不会偏移,所以就需要我们手动添加

跟控制器只会取出按顺序添加的第一个控件进行向下偏移64.不管第一个控件是什么,例如在两个控制器添加之前,现在根控制器上添加一个UIButton,那么两个控制器都不会向下偏移64

contentInset,导航控制器会默认取出第一个控件设置他的contentInset属性为64

也就是说系统为tableView主动添加64需要满足两个条件

 1.根控制器为导航控制器
 2.tableView为当前控制器的第一个子控件。

总结

只要导航控制器的跟控制器的第一个子控制器为UIScrollView,或者他的子类的,导航控制器都会默认将他的contentInset设置为64.跟他的位置没有关系 没有导航控制器

没有导航控制器的时候,控件1为UIScrollView或者其子类,2为任意一个控件,假设1的frame为(0,0,300,300),2的frame为(0,0,30,30);那么显示的就是2在1的左上角,1在根控制器的左上角。

有导航控制器

有导航控制器的时候,同样的控件,那么1的frame虽然还是(0,0,300,300)。但是他的contentInset变成了(64,0,0,0);

所以最终显示的内容就是2控件显示的位置向下偏移64.(contentInset代表控件内哪些区域不能显示内容)

那么2的位置就是(0,64,30,30);

控件位置变化

这种情况下是控件1的frame原始为(0,100,300,300);

控件2依然是控件1的子控件,frame为(0,0,30,30);

当加入导航控制器的时候1的frame没有变化,但是控件2的位置向下偏移64

苹果会自动调整第一个子控件的contentInset。设置 self.automaticallyAdjustsScrollViewInsets = NO;告诉苹果不自动帮我们调,如果没有导航栏就会自动调20

static

static的作用:

  1. 修饰局部变量

    • 让局部变量只初始化一次
    • 局部变量在程序中只有一份内存
    • 并不会改变局部变量的作用域,仅仅是改变了局部变量的生命周期(只到程序结束,这个局部变量才会销毁)
  2. 修饰全局变量

    • 全局变量的作用域仅限于当前文件

copy strong 之间的区别

/** 名字 */
@property (nonatomic,strong) NSString * name;
/** copy名字 */
@property (nonatomic,copy) NSString * copyname;
- (void)setName:(NSString *)name {
    _name = name;
}

- (void)setCopyname:(NSString *)copyname {
    _copyname = [copyname copy];
}

他们之间的主要区别就是set方法的不同,copy的主要作用是加入定义一个NSString * str ;然后用copyname=str。那么以后str改变不会影响到copyname,strong则是会影响到的

@property (nonatomic,copy) NSMutableString * mutableName;

这个属性定义是没有问题,但是在使用的时候,我们会将之当成一个可变字符串来使用,但是在其set方法中返回的是一个copy来的不可变的字符串

- (void)setMutableName:(NSMutableString *)mutableName {
    _mutableName = [mutableName copy];
}

results matching ""

    No results matching ""