Swift之cell的三种创建方式

该文介绍Swift3.0中分别采用系统、xib、代码自定义三种方式创建UITableViewCell,并与Objective-C创建cell作对比,比较语法的不同之处
下图是Objective-C编写的创建cell的项目结构:


Swift之cell的三种创建方式_第1张图片

可以看到在APPDelegate中,我创建了一个继承UITableViewController的控制器,并作为根视图控制器显示
以下是ViewController实现文件中的代码:
//// TableViewController.m// 20170317-cell系统方法调用顺序//// Created by 柯其谱 on 17/3/18.// Copyright © 2017年 柯其谱. All rights reserved.//#import "TableViewController.h"#import "SystemTableViewCell.h"#import "XibTableViewCell.h"#import "CustomTableViewCell.h"@interface TableViewController ()@end@implementation TableViewController- (void)viewDidLoad { [super viewDidLoad]; // //系统创建的cell不用注册// [self.tableView registerClass:[SystemTableViewCell class] forCellReuseIdentifier:SystemTableViewCellID]; //xib和自定义cell创建的cell必须注册 [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([XibTableViewCell class]) bundle:nil] forCellReuseIdentifier:XibTableViewCellID]; [self.tableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:CustomTableViewCellID];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 3;}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { SystemTableViewCell *systemCell = [SystemTableViewCell cellWithTableView:tableView indexPath:indexPath]; return systemCell; } else if (indexPath.section == 1) { XibTableViewCell *xibCell = [XibTableViewCell cellWithTableView:tableView indexPath:indexPath]; return xibCell; } else { CustomTableViewCell *customCell = [CustomTableViewCell cellWithTableView:tableView indexPath:indexPath]; return customCell; }}@end

由于代码较为简单,我这里就不作解释,需要注意的是为了控制器的瘦身,我将cell的创建和内容显示全部放在了各自的cell实现文件中,且本文三种cell创建方式显示的cell为同一样式,这里是为了简单起见,到了复杂一点的项目就可将所有的有关cell代码全部写在cell文件中
以下是SystemTableViewCell,即cell的系统样式的实现代码:
//// SystemTableViewCell.m// 20170317-cell系统方法调用顺序//// Created by 柯其谱 on 17/3/17.// Copyright © 2017年 柯其谱. All rights reserved.//#import "SystemTableViewCell.h"NSString * const SystemTableViewCellID = @"SystemCell";@implementation SystemTableViewCell+ (instancetype)cellWithTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath { NSLog(@"%s", func); SystemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SystemTableViewCellID]; if (cell == nil) { cell = [[SystemTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SystemTableViewCellID]; } cell.imageView.image = [UIImage imageNamed:@"image"]; cell.textLabel.text = @"System"; return cell;}//系统创建的cell调用,其他不调用- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { NSLog(@"%s", func); } return self;}- (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { NSLog(@"%s", func); } return self;}- (void)awakeFromNib { [super awakeFromNib]; // Initialization code NSLog(@"%s", func);}- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state}@end

本文也顺便研究了各种创建cell的方式的系统方法调用顺序,可以看到,控制台只打印了initWithStyle方法,也就是说,系统样式创建的cell只在cell未开始复用时调用该方法,其他方法不调用。
以下是XibTableViewCell,即xib创建cell的方式,本文创建的cell内容视图都与系统的default样式相似,故xib的约束未贴出:
//// XibTableViewCell.m// 20170317-cell系统方法调用顺序//// Created by 柯其谱 on 17/3/17.// Copyright © 2017年 柯其谱. All rights reserved.//#import "XibTableViewCell.h"NSString * const XibTableViewCellID = @"XibCell";@implementation XibTableViewCell+ (instancetype)cellWithTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath { NSLog(@"%s", func); XibTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:XibTableViewCellID]; cell.xibImageView.image = [UIImage imageNamed:@"image"]; cell.xibTextLabel.text = @"Xib"; return cell;}- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { NSLog(@"%s", func); } return self;}//xib创建cell先调用此方法,再调用awakeFromNib- (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { NSLog(@"%s", func); } return self;}- (void)awakeFromNib { [super awakeFromNib]; // Initialization code NSLog(@"%s", func);}- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state}@end

控制台中,先打印initWithCoder方法名,再打印awakeFromNib方法名,也就是说用xib创建的cell会先后调用这两个方法,可以在awakeFromNib中作一些cell的初始化工作
以下是CustomCell实现文件,即代码自定义创建cell的方式,由于内容视图较为简单,这里未做约束:
//// CustomTableViewCell.m// 20170317-cell系统方法调用顺序//// Created by 柯其谱 on 17/3/17.// Copyright © 2017年 柯其谱. All rights reserved.//#import "CustomTableViewCell.h"NSString * const CustomTableViewCellID = @"CustomCell";@implementation CustomTableViewCell+ (instancetype)cellWithTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath { NSLog(@"%s", func); CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CustomTableViewCellID]; cell.customImageView.image = [UIImage imageNamed:@"image"]; cell.customTextLabel.text = @"Custom"; return cell;}//代码自定义cell调用,其他不调用- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { NSLog(@"%s", func); [self setupContentView]; } return self;}- (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { NSLog(@"%s", func); [self setupContentView]; } return self;}- (void)awakeFromNib { [super awakeFromNib]; // Initialization code NSLog(@"%s", func);}- (void)setupContentView { UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(16, 0, self.contentView.frame.size.height, self.contentView.frame.size.height)]; self.customImageView = imageView; [self.contentView addSubview:imageView]; UILabel *textLabel = [[UILabel alloc]initWithFrame:CGRectMake(CGRectGetMaxX(imageView.frame)+16, 0, 100, CGRectGetHeight(imageView.frame))]; self.customTextLabel = textLabel; [self.contentView addSubview:textLabel];}- (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state}@end

代码自定义创建cell的方式与系统样式相似,会调用initWithStyle,重写此方法并添加内容视图,以及添加内容视图子视图的约束。
以上是Objective-C语言编写的简单创建cell的代码,接下来介绍Swift3.0编写的创建cell的代码,代码实现的目标与Objective-C完全一致,只是部分代码发生了改变
下图是Swift3.0代码的项目结构:


Swift之cell的三种创建方式_第2张图片

需要注意的是在APPDelegate文件中,初始化UITableViewController子类的方法与Objective-C略有不同,UITableViewController子类中必须重写init(style: UITableViewStyle)方法才能顺利调用UITableViewController子类的空构造方法,否则编译器无法通过
以下是UITableViewController子类中的代码:
//// TableViewController.Swift// Swift demo - 三种cell创建方式//// Created by 柯其谱 on 17/3/18.// Copyright © 2017年 柯其谱. All rights reserved.//import UIKitclass TableViewController: UITableViewController { //MARK: - View life cycle override func viewDidLoad() { super.viewDidLoad() //使tableView向下20个点 tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0) print(NSStringFromClass(XibTableViewCell.self), XibTableViewCell.reuseIdentifier) //Swift有命名空间的概念,使得NSStringFromClass这个方法返回的不只是类名,签名还有类名所在文件相对路径 tableView.register(UINib.init(nibName: "XibTableViewCell", bundle: nil), forCellReuseIdentifier: XibTableViewCell.reuseIdentifier) tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: CustomTableViewCell.reuseIdentifier) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } //MARK: - Construction method //必须重写此方法,UITableViewController才能成功调用空的构造方法 override init(style: UITableViewStyle) { super.init(style: style) print(#function) } //必须重写此方法,否则编译无法通过 required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print(#function) } //重写此方法使得UITableViewController能够调用空的构造方法 override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) print(#function) } }extension TableViewController { //MARK: - UITableViewDelegate override func numberOfSections(in tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 3 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return 1 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch indexPath.section { case 0: let systemCell = SystemTableViewCell.cell(tableView: tableView, indexPath: indexPath) return systemCell case 1: let xibCell = XibTableViewCell.cell(tableView: tableView, indexPath: indexPath) return xibCell default: let customCell = CustomTableViewCell.cell(tableView: tableView, indexPath: indexPath) return customCell } }}

与Objective-C不同的是,Swift3.0需要重写一些构造方法,另外一点,在cell的nib注册方法中,Swift直接将cell的nib文件名作为参数传入,这是因为Swift的命名空间机制,使得每一个类都有一个唯一的命名空间,类名便多了一些诸如项目名的前缀,若此处坚持使用跟Objective-C注册cell 的方式一致,使用NSStringFromClass([XibTableViewCell class]方法作为参数传入,则运行会导致奔溃,cell始终是空值。其余代码与Objective-C大体一致。
以下是SystemTableViewCell文件中的代码:
//// SystemTableViewCell.Swift// Swift demo - 三种cell创建方式//// Created by 柯其谱 on 17/3/18.// Copyright © 2017年 柯其谱. All rights reserved.//import UIKitclass SystemTableViewCell: UITableViewCell { static let reuseIdentifier = "SystemCell" static func cell(tableView: UITableView, indexPath: IndexPath) -> SystemTableViewCell{ print(#function) var cell: SystemTableViewCell? cell = tableView.dequeueReusableCell(withIdentifier: SystemTableViewCell.reuseIdentifier) as! SystemTableViewCell? if cell == nil { cell = SystemTableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: SystemTableViewCell.reuseIdentifier) } cell?.imageView?.image = UIImage(named: "image") cell?.textLabel?.text = "System" return cell! } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) print(#function) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print(#function) } override func awakeFromNib() { super.awakeFromNib() // Initialization code print(#function) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state }}

此处与Objective-C代码类似,这里不作赘述,调用方式也一致
以下是XibTableViewCell文件代码:
//// XibTableViewCell.Swift// Swift demo - 三种cell创建方式//// Created by 柯其谱 on 17/3/18.// Copyright © 2017年 柯其谱. All rights reserved.//import UIKitclass XibTableViewCell: UITableViewCell { static let reuseIdentifier = "XibCell" @IBOutlet weak var xibImageView: UIImageView! @IBOutlet weak var xibTextLabel: UILabel! static func cell(tableView: UITableView, indexPath: IndexPath) -> XibTableViewCell { print(#function) var cell: XibTableViewCell? cell = tableView.dequeueReusableCell(withIdentifier: XibTableViewCell.reuseIdentifier) as! XibTableViewCell? cell?.xibImageView.image = UIImage(named: "image") cell?.xibTextLabel.text = "Xib" return cell! } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) print(#function) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print(#function) } override func awakeFromNib() { super.awakeFromNib() // Initialization code print(#function) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } }

最后是CustomTableViewCell文件:
//// CustomTableViewCell.Swift// Swift demo - 三种cell创建方式//// Created by 柯其谱 on 17/3/18.// Copyright © 2017年 柯其谱. All rights reserved.//import UIKitclass CustomTableViewCell: UITableViewCell { static let reuseIdentifier = "CustomCell" ///左侧imageView var customImageView: UIImageView! ///右侧text label var customTextLabel: UILabel! static func cell(tableView: UITableView, indexPath: IndexPath) -> CustomTableViewCell { print(#function) var cell: CustomTableViewCell? cell = (tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.reuseIdentifier) as! CustomTableViewCell) cell?.customImageView.image = UIImage(named: "image") cell?.customTextLabel.text = "Custom" return cell! } override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) print(#function) self.setupContentView() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print(#function) } override func awakeFromNib() { super.awakeFromNib() // Initialization code print(#function) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } ///添加内容视图 private func setupContentView() { customImageView = UIImageView() self.contentView.addSubview(customImageView) customTextLabel = UILabel() self.contentView.addSubview(customTextLabel) let imageViewH: CGFloat = self.contentView.frame.size.height customImageView.frame = CGRect(x: 16, y: 0, width: imageViewH, height: imageViewH) customTextLabel.frame = CGRect(x: customImageView.frame.maxX+16, y: customImageView.frame.minY, width: 200, height: customImageView.frame.height) }}

此处内容视图的子视图未添加约束,只是简单地设置了frame,当然,在真实项目中可在此处用cocoapods导入snapkit框架作适配,即Objective-C语言masonry矿建的Swift版本。

你可能感兴趣的:(Swift之cell的三种创建方式)