Swift3.0之cell的三種創建方式


  該文介紹Swift3.0中分別采用系統、xib、代碼自定義三種方式創建UITableViewCell,並與Objective-C創建cell作對比,比較語法的不同之處

  下圖是Objective-C編寫的創建cell的項目結構:

 

  可以看到在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代碼的項目結構:

  需要注意的是在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 UIKit

class 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 UIKit

class 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 UIKit

class 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 UIKit

class 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版本。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM