说在前面的话
一看标题:说的是不是NSLayoutConstraint?不是.那是不是Autoresizing?不是.那是不是XIB中的自动布局(Auto Layout)?不是.那是什么??????恭喜你,你差不多说完了iOS中的布局方式,但是接下来,我所要说的不是这些,当然如果你能用以上3个方法中的其中一种都可以,但是今天我所要介绍的,都会避开以上的3中情况.具体内容,请往下看...
需求
需要做一个如下的界面,不要使用XIB.
这个界面很简单:
在控制器里面,有一个UITableview.然后自定义了一个简单的cell.
这是再简单不过的界面了,我会介绍如下的东西:
- 在控制器中,如何创建一个UITableView.
- 在控制器中,如何布局.
- 在视图(cel为例l)中,如何创建字控件,又将如何布局.
- 控制器与视图(cell为例)最好如何关联.
开始
来到事先准备好的控制器(HGSubLayoutViewController)中,最初的代码如下:(什么都木有)
#import "HGSubLayoutViewController.h"
@interface HGSubLayoutViewController ()
@end
@implementation HGSubLayoutViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
- 定义一个UITableView如下:
@property (nonatomic, strong) UITableView* tableView;
这里有一个问题,这个tableView,是应该定义成strong还是weak?答案是:都可以!但是,我强烈建议:在没有特殊的情况下,尽量定义成weak.代码会显得高大上一些!我暂且定义成strong.
- 那这个tableView应该在哪里创建呢?首当其冲的会想到在控制器的init方法中吧.那我就实现一下.
// 构造方法
- (instancetype)init {
self = [super init];
UITableView* tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds];
tableView.backgroundColor = [UIColor redColor];
[self.view addSubview:tableView];
self.tableView = tableView;
return self;
}
咋一看,没有问题.运行一下,也没有问题.但是我要告诉你,这段代码是有问题的!
由上图结论得出:上面的那段代码是很危险的!为什么危险?这个问题,我不做过多的解释.如果你知道:1.init方法应该在什么时候调用,2.viewDidLoad方法又是在什么时候调用?3. viewDidLoad的主要用途是什么?你清楚了这3个问题,你就能幻想到上面带密码的危险性了!那么上面的代码中,怎么就调用了viewDidLoad方法呢?我可以直接告诉你:因为你在init方法中调用了self.view.
在init方法中不要调用self.view!!!!!!这是大忌!!!!!!!
那你会说,OK我在viewDidLoad方法中调用self.view,这总算可以吧.恩可以!Verry Good!😍,你也会说,我可否将init里面创建tableView的代码放到一个独立的方法中,在viewDidLoad方法中调用,这也可以.Verry Verry Good!😍.你还可以说, 想使用懒加载,直接早viewDidLoad方法中调用:
// 添加tableView到self.view中.一定要写成:self.tableView,你写成_tableView试试.
[self.view addSubview:self.tableView];
OK了,tableView的创建总算搞定了.
等等,别忘了.这是你将tableView设置成strong的情况.那设置成weak,有该如何呢?换成weak,是我所推荐的,因为我一致坚持:属性能用weak的尽量用weak!无奈之下才会用strong.将刚刚所有的代码delete了.换成如下代码:
#import "HGSubLayoutViewController.h"
@interface HGSubLayoutViewController ()
@property (nonatomic, weak) UITableView* tableView;
@end
@implementation HGSubLayoutViewController
// 创建tableView并添加到self.view中.
- (void)setupTableView {
UITableView* tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
tableView.backgroundColor = [UIColor redColor];
[self.view addSubview:tableView];
self.tableView = tableView;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupTableView];
}
@end
很高兴的告诉你,这就是我最终选择的方式!通过以后的解说,如果设置成weak,你只能这么做!为啥?没啥!😍
代理的那些事
代理的全部操作如下:
#import "HGSubLayoutViewController.h"
#import "HGHGSubLayoutCell.h"
@interface HGSubLayoutViewController () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, weak) UITableView* tableView;
@end
@implementation HGSubLayoutViewController
// 创建tableView并添加到self.view中.
- (void)setupTableView {
UITableView* tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.view addSubview:tableView];
self.tableView = tableView;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupTableView];
}
#pragma mark - UITableViewDelegate, UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 30;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* const ID = @"HGHGSubLayoutCell";
HGHGSubLayoutCell* cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
[tableView registerClass:[HGHGSubLayoutCell class] forCellReuseIdentifier:ID];
cell = [tableView dequeueReusableCellWithIdentifier:ID];
}
// 在实际开发中,这里我会给这个cell弄一个mode
// cell.mode = mode; // 一个cell对应一个mode
return cell;
}
@end
这段代码,很简单,也很简洁!但是有的地方依然需要说一下:
- <UITableViewDelegate, UITableViewDataSource>协议应该在.h还是在.m中写?答案是:都可以.但是我强烈建议在.m中写.因为在写代码的时候,在代码的规范上,也应该要坚持代码的封装性!能在.m实现的,不必让外界知道的,尽量放到.m文件中.在.h文件中尽量不要暴露不该暴露的东西!!
- 关于tableView的声明.同理,强烈建议方到.m文件中!
- 关于代理:tableView: cellForRowAtIndexPath:.看看我实现的,是不是很简洁,很简单呢?........
显示
细心的同学,都能想到:这个tableView是看不到的.因为没有尺寸!!!那么,我为什么没有在创建tableView的时候给此存呢?我是想在另一个方法中设置.代码如下:
// 布局控制器中控件的位置
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.tableView.frame = self.view.bounds;
}
这个viewDidLayoutSubviews方法,才是最接近我们的这个主题!对,我的初衷就是想告诉大家,在控制器中,布局控件的的所有操作,尽量都在这个方法里面的做!这个方法的,大家可以查看官方文档.
先来介绍一下要用到的模型
.h文件
#import <Foundation/Foundation.h>
@interface HGHGSubLayoutMode : NSObject
@property (nonatomic, copy) NSString* hgTitle;
@end
.m文件
#import "HGHGSubLayoutMode.h"
@implementation HGHGSubLayoutMode
@end
cell里面的实现
当你看到这里,也就说明了.在控制器里面的布局问题,我已经说完了,接下来,就是UIView种的布局.这里我是借助Cell来说明视图中的布局.
现在我将要在cell里面添加一个简单的UILabel.
- cell里面的所有代码,如下:
.h文件
#import <UIKit/UIKit.h>
@class HGHGSubLayoutMode;
@interface HGHGSubLayoutCell : UITableViewCell
// cell对应的mode
@property (nonatomic, strong) HGHGSubLayoutMode* mode;
@end
.m文件
#import "HGHGSubLayoutCell.h"
#import "HGHGSubLayoutMode.h"
@interface HGHGSubLayoutCell ()
@property (nonatomic, weak) UILabel* hgLabel;
@end
@implementation HGHGSubLayoutCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
// 在构造方法中的只是创建控件与对该控件做固定的操作.
UILabel* hgLabel = [[UILabel alloc] init];
hgLabel.textAlignment = NSTextAlignmentCenter;
hgLabel.textColor = [UIColor redColor];
hgLabel.backgroundColor = [UIColor grayColor];
// 添加
[self.contentView addSubview:hgLabel];
self.hgLabel = hgLabel;
self.selectionStyle = UITableViewCellSelectionStyleNone;
return self;
}
// 在模型的setter方法中将对应的属性与对应的控件链接起来
- (void)setMode:(HGHGSubLayoutMode *)mode {
_mode = mode;
self.hgLabel.text = mode.hgTitle;
}
// 在视图中,所有子控件的的布局,都放到这个系统方法中.看看这个方法的名字就明白了.
- (void)layoutSubviews {
[super layoutSubviews];
// 布局
self.hgLabel.frame = self.contentView.bounds;
}
@end
我个人感觉,我的cell写得还不错的:代码不乱,层次清楚就拿这个cell来说吧.一个cell就三大步:1,构造方法中,只是创建与添加.2,通过模型设置所有的控件.3,就是布局.这就用到视图中的一个系统方法: layoutSubviews.关于我的代码,建议多欣赏!哈哈哈哈,今天我的心情有点怪异,我在嘚瑟!
视图中的这个系统layoutSubviews方法,也很重要,是这个主题的第二个重要方法!具体详细,查看官方文档.
总结:
- 控制器主要布局方法: viewDidLayoutSubviews.(记得调用[super viewDidLayoutSubviews];)
- 视图的主要布局方法: layoutSubviews.(记得调用:[super layoutSubviews])
- 其实,我写这个.主要是最近喜欢写点东西.由于我水平有限,如有不当之处,往评论指出!谢谢!