概念
多态的概念详细见
iOS封装、继承、多态项目应用-tableview(OC篇)
应用场景

图1
分析:
上图整体采用tableView来实现,分为三个section(1,2,3)
区域2的做法有几种:
1、采用tableView嵌套tableView来实现,不好计算高度,考虑到可能会有多行,把内容撑大,需要分别计算高度
2、把区域2切割成tableView的row,如下图

图2
如上图,需要好几个cell,title样式、开关样式、输入框样式、点击更多样式等,但是考虑到以下几点:
- 第一个cell上部分有圆角、最后一个cell下部分有圆角,中间无圆角,因为样式不确定(不同设备详情样式不确定,不同设备详情cell不同)
- 每个cell有相同的地方,如icon,title,有不同的地方,如content,开关等
考虑创建一个基类,用于设置公共的部分(icon、title、圆角等)
基类WCDeviceBaseTableViewCell代码如下
import UIKit
enum LocationType {
case first //第一行
case middle //中间行
case last //最后一行
}
class WCDeviceBaseTableViewCell: WCBaseTableViewCell {
//MARK: - public variable
//cell的位置,用于处理圆角位置
var locationType:LocationType = .first
//标题
var title:String? {
didSet {
if let title = title {
self.titleLabel.text = title
}
}
}
//图标名
var iconName:String? {
didSet {
if let iconName = iconName {
self.iconIV.image = UIImage(named: iconName)
}
}
}
//内容
var content:String?
//view
//圆角view
lazy var cornerView:UIView = {
let cornerView = UIView()
cornerView.backgroundColor = .white
return cornerView
}()
//图标
lazy var iconIV:UIImageView = {
let iconIV = UIImageView()
return iconIV
}()
lazy var titleLabel:UILabel = {
let titleLabel = UILabel()
titleLabel.textColor = .black
titleLabel.font = UIFont.systemFont(ofSize: 16)
return titleLabel
}()
//MARK: - function
//override UI
override func prepareUI() {
super.prepareUI()
self.containerView.addSubview(self.cornerView)
self.cornerView.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.left.equalTo(12)
make.right.equalTo(-12)
}
self.cornerView.addSubview(self.iconIV)
self.iconIV.snp.makeConstraints { make in
make.left.top.equalTo(20)
make.width.height.equalTo(32)
make.bottom.lessThanOrEqualTo(-20)
}
self.cornerView.addSubview(self.titleLabel)
self.titleLabel.snp.makeConstraints { make in
make.centerY.equalTo(self.iconIV)
make.left.equalTo(self.iconIV.snp.right).offset(8)
}
}
//MARK: - system
override func layoutSubviews() {
super.layoutSubviews()
if self.bounds.height > 0 && self.bounds.width > 0 {
var rect = self.bounds
rect.size.width = rect.width - 12 * 2
var corner:UIRectCorner = [.allCorners]
var radii = CGSize(width: 12, height: 12)
if self.locationType == .first {
corner = [.topLeft, .topRight]
} else if self.locationType == .last {
corner = [.bottomLeft, .bottomRight]
} else if self.locationType == .middle {
corner = [.allCorners]
radii = CGSize(width: 0, height: 0)
}
self.cornerView.layer.mask = self.configRectCorner(view: self.cornerView, bounds: rect, corner: corner, radii: radii)
}
}
//MARK: - private function
//设置部分圆角
private func configRectCorner(view: UIView, bounds:CGRect? = nil, corner: UIRectCorner, radii: CGSize) -> CALayer {
let maskPath = UIBezierPath.init(roundedRect: bounds ?? view.bounds, byRoundingCorners: corner, cornerRadii: radii)
let maskLayer = CAShapeLayer.init()
maskLayer.frame = bounds ?? view.bounds
maskLayer.path = maskPath.cgPath
return maskLayer
}
}
基类中设置了一些公共的变量、UI布局等,并且声名子类的变量
子类cell代码如下
WCDeviceTitleTableViewCell
class WCDeviceTitleTableViewCell: WCDeviceBaseTableViewCell {
override func prepareUI() {
super.prepareUI()
self.iconIV.isHidden = true
self.iconIV.snp.removeConstraints()
self.titleLabel.snp.remakeConstraints { make in
make.left.top.equalTo(20)
make.bottom.equalTo(-12)
}
}
}
WCDeviceSwitchTableViewCell
class WCDeviceSwitchTableViewCell: WCDeviceBaseTableViewCell {
//MARK: - prvate variable
private lazy var switchButton:UIButton = {
let switchButton = UIButton(type: .custom)
switchButton.setImage(UIImage(named: "ic_switch_on"), for: .normal)
return switchButton
}()
override func prepareUI() {
super.prepareUI()
self.cornerView.addSubview(self.switchButton)
self.switchButton.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalTo(-20)
make.width.equalTo(44)
make.height.equalTo(22)
}
}
}
WCDeviceLabelTableViewCell
class WCDeviceLabelTableViewCell: WCDeviceBaseTableViewCell {
//MARK: - private variable
private lazy var moreIV:UIImageView = {
let moreIV = UIImageView()
moreIV.image = UIImage(named: "ic_list_more")
return moreIV
}()
//override
override var content: String? {
didSet {
self.contentLabel.text = content
}
}
private lazy var contentLabel:UILabel = {
let contentLabel = UILabel()
contentLabel.textColor = .gray
contentLabel.font = UIFont.systemFont(ofSize: 13)
contentLabel.textAlignment = .right
contentLabel.numberOfLines = 0
return contentLabel
}()
override func prepareUI() {
super.prepareUI()
self.cornerView.addSubview(self.moreIV)
self.moreIV.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalTo(-20)
make.width.height.equalTo(24)
}
self.cornerView.addSubview(self.contentLabel)
self.contentLabel.snp.makeConstraints { make in
make.right.equalTo(self.moreIV.snp.left)
make.width.equalTo(148)
make.top.equalTo(14)
make.bottom.equalTo(-14)
make.height.greaterThanOrEqualTo(40)
}
}
}
WCDeviceTFTableViewCell
//MARK: - private variable
private lazy var textField:UITextField = {
let textField = UITextField()
textField.textColor = .gray
textField.font = UIFont.systemFont(ofSize: 13)
textField.textAlignment = .right
return textField
}()
//override
override var content: String? {
didSet {
self.textField.text = content
}
}
override func prepareUI() {
super.prepareUI()
self.cornerView.addSubview(self.textField)
self.textField.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.right.equalTo(-20)
make.width.equalTo(200)
}
}
}
控制器中代码如下
fileprivate enum CellType {
case title
case openSwitch
case name
case area
case timimg
case countdown
}
class TestViewController: UIViewController {
//MARK: - private variable
private lazy var tableView:UITableView = {
let tableView = UITableView(frame: self.view.bounds)
tableView.backgroundColor = .clear
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
return tableView
}()
//cell内容
private var cellTypes:[CellType] = [
.title,
.openSwitch,
.name,
.area,
.timimg,
.countdown,
]
private let titleDic:[CellType:String] = [
.title : "设备信息",
.openSwitch : "开关",
.name : "名称",
.area : "区域",
.timimg : "定时",
.countdown : "倒计时",
]
private let iconDic:[CellType:String] = [
.openSwitch : "ic_area_switch",
.name : "ic_area_name",
.area : "ic_area_area",
.timimg : "ic_area_timing",
.countdown : "ic_area_countdown",
]
private let classDic:[CellType:WCDeviceBaseTableViewCell.Type] = [
.title : WCDeviceTitleTableViewCell.self,
.openSwitch : WCDeviceSwitchTableViewCell.self,
.name : WCDeviceTFTableViewCell.self,
.area : WCDeviceLabelTableViewCell.self,
.timimg : WCDeviceLabelTableViewCell.self,
.countdown : WCDeviceLabelTableViewCell.self,
]
//MARK: - lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .gray
self.view.addSubview(self.tableView)
}
}
extension TestViewController:UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.cellTypes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellType = self.cellTypes[indexPath.row]
var aclass:WCDeviceBaseTableViewCell.Type = self.classDic[cellType] ?? WCDeviceBaseTableViewCell.self
if indexPath.row == 0 {
aclass = WCDeviceTitleTableViewCell.self
}
let cell = aclass.wc_baseTableViewCell(tableView: tableView) as! WCDeviceBaseTableViewCell
if indexPath.row == 0 {
cell.locationType = .first
} else if indexPath.row == self.cellTypes.count - 1 {
cell.locationType = .last
} else {
cell.locationType = .middle
}
cell.iconName = self.iconDic[cellType]
cell.title = self.titleDic[cellType]
var content:String = ""
if cellType == .name {
content = "照明吊灯"
} else if cellType == .area {
content = "主卧"
} else if cellType == .countdown {
content = "03:45"
}
cell.content = content
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 12
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return UIView()
}
}
运行结果

运行结果
总结
控制器中的代码
var aclass:WCDeviceBaseTableViewCell.Type = self.classDic[cellType] ?? WCDeviceBaseTableViewCell.self
let cell = aclass.wc_baseTableViewCell(tableView: tableView) as! WCDeviceBaseTableViewCell
使用了多态的特性,声名的cell类型为WCDeviceBaseTableViewCell,但是根据实际调用的class(aclass)调用对应的类函数,会返回实际的实例变量(但都是WCDeviceBaseTableViewCell的子类),后续的赋值、显示等操作均会根据实际的变量类型来执行。
可应用的项目: 后端返回的数据是相同的model,但根据数据的类型不同(可能是数据中的某个字段来判断),UI等有区别的情况