UITableView中有多个UITableViewCells

本教程的重点是如何以尽可能少的代码正确地在UITableView中添加多个UITableViewCells。

在本教程中,我选择了一个场景,其中您的应用程序包含诸如联系我们,注册,登录等屏幕。简单地说,您需要在具有多种单元格类型的多个屏幕上显示表单。 当然,您可以将其应用于在UITableView中需要多个UITableViewCells的任何其他方案。

在开始之前,您需要了解几件事:

  • 扩展名
  • 枚举
  • 关闭
  • 自动版式
 为了更好地理解,我创建了一个示例GitHub项目,您可以在此处下载。 

创建UITableViewCells

首先,我们将从UITableViewCells创建开始。 如果打开项目,将在文件夹Cells下看到创建的各种单元 ,这些单元格包含输入字段,下拉列表,操作按钮,多行输入字段。 出于演示目的,我将使用BaseCellInputCell

我们将首先创建一个主单元(名为BaseCell),该单元将处理所有子级任务。 BaseCell用于存储所有子元素中相互共有的功能。 然后,我们将这些功能用作子单元格中的替代。 此单元格没有UI,但将从其子级中获取合适的UI。

基本单元

import UIKit 
 class BaseCell: UITableViewCell { 
  //MARK: Internal Properties 
var type: CellType!
var pickerOptions: [String]!{
didSet{
pickerOptionsSet()
}
}
  var textChangedBlock: ((String) -> Void)? 

  override func awakeFromNib() { 
super.awakeFromNib()
// Initialization code
}

func setForm(title: String, placeholder: String, keyboardType: UIKeyboardType){
set(title: title, placeholder: placeholder, image: "", secureEntry: false, keyboardType: keyboardType)
}
  func set(title: String, placeholder: String, image: String, secureEntry: Bool, keyboardType: UIKeyboardType){ 
setTitle(title: title)
setPlaceholder(placeholder: placeholder)
setKeyboardType(type: keyboardType)
setImage(image: image)
setSecureEntry(isSecure: secureEntry)
}
  func setTitle(title: String){} 
func setPlaceholder(placeholder: String){}
func setKeyboardType(type: UIKeyboardType){}
func setSecureEntry(isSecure: Bool){}
func setImage(image: String){}
func setTextAlignment(textAlignment: NSTextAlignment){}
func pickerOptionsSet(){}

}

如您所见,我正在创建一个名为set()的主要函数,该函数包含我需要的参数,以便“馈送”所有子单元格数据。 另外,您可以看到另一个名为setForm()的函数,该函数是不需要调用某些参数的辅助函数的示例。 我还为每个参数创建了辅助函数,然后在需要该数据类型的单元格中覆盖它们。 CellType在下面的下一部分中介绍。

现在,当我们拥有主单元时,我们就可以开始创建子单元了。 如上文所述,我将只介绍一个子单元以使内容简短,并且您可以按照相同的流程来创建所需的其他子单元。 我仍然强烈建议从Github下载示例项目。

输入单元

 import UIKit 
 class InputCell: BaseCell { 
  //MARK: Private Properties 
@IBOutlet fileprivate weak var titleLbl: UILabel!
@IBOutlet fileprivate weak var inputTxt: UITextField!
  override func awakeFromNib() { 
super.awakeFromNib()
// Initialization code
}
override func setTitle(title: String) {
titleLbl.text = title
}
override func setPlaceholder(placeholder: String) {
inputTxt.placeholder = placeholder
}
override func setKeyboardType(type: UIKeyboardType) {
inputTxt.keyboardType = type
}

@IBAction func textDidChange(textField: UITextField){
if let txt = textField.text{
textChangedBlock?(txt)
}
}
}
extension InputCell: UITextFieldDelegate{
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return textField.resignFirstResponder()
}
}

InputCell是一个单元格,向我们展示了一个UITextField和一个位于其上方的UILabel,它充当单元格标题。 如您所见,它继承自BaseCell,我们仅覆盖InputCell类内部所需的帮助器方法。 因此,我们需要在该单元格中填充标题,UITextField的占位符,并通过在BaseCell中添加一个名为textChangedBlock?()的闭包来跟踪输入的更改

单元格创建就是这样,现在我们继续创建枚举类型。

枚举类型

这里的枚举类型可以帮助我们正确地组织代码,我强烈建议您在任何可能的情况下使用枚举。 在我们的情况下,您可以将它们想象为我们单元的设置。 本教程将需要2种枚举类型:

  1. CellType-子单元的所有UI设置
  2. FormCellType-用数据填充单元格所需的所有设置

细胞类型

 import Foundation 
import UIKit
 enum CellType{ 

case input
case inputLong
case inputImage
case dropdown
case button

func getHeight() -> CGFloat{
switch self {
case .input, .dropdown, .button: return 80
case .inputLong: return 115
case .inputImage: return 65
}
}

func getClass() -> BaseCell.Type{
switch self {
case .input: return InputCell.self
case .inputLong: return InputLongCell.self
case .dropdown: return DropdownCell.self
case .button: return ButtonCell.self
case .inputImage: return InputImageCell.self
}
}
}

如上所述,我们将使用CellType来确定UI。 当前,您可以看到getHeight()函数,该函数将返回每种单元格类型的高度,以及getClass ()函数,该函数将返回给定情况下的单元格类型。

FormCellType

 import Foundation 
import UIKit
 enum FormCellType{ 

case name
case email
case username
case pass
case contactTypes
case work
case message
case send
  func getTitle() -> String{ 
switch self {
case .name: return "Name"
case .email: return "Email"
case .work: return "Work"
case .message: return "Message"
case .send: return "Send"
case .contactTypes: return "Pick Contact Type"
case .username: return "Username"
case .pass: return "Password"
}
}

func placeholder() -> String{
switch self {
case .name: return "Enter your name"
case .email: return "Enter your email address"
case .work: return "Enter your company place"
case .message: return "Write us a message (optional)"
case .username: return "Enter Username"
case .pass: return "Enter Password"
default: return ""
}
}

func image() -> String{
switch self {
case .username: return "form-username"
case .email: return "form-email"
case .pass: return "form-password"
default: return ""
}
}

func keyboardSecure() -> Bool{
switch self {
case .pass: return true
default: return false
}
}
  func keyboardType() -> UIKeyboardType{ 
switch self {
case .email: return .emailAddress
default: return .default
}
}

func pickerOptions()->[String]{
switch self {
case .contactTypes:
return ["Advertising on Site", "General Enquiries", "Feedback", "Account Enquiries"]
default: return []
}
}

func cellType() -> CellType{
switch self {
case .message: return .inputLong
case .send: return .button
case .username, .pass, .email: return .inputImage
case .contactTypes: return .dropdown
default: return .input
}
}
}

FormCellType是枚举类型,您需要在其中定义要在UITableView中使用的字段及其设置(例如标题,占位符,keyboardType等)。此外,请注意cellType()函数,在该函数中我们确定了CellType的CellType。案件。

UIViewController流

让我们看看如何结合到目前为止所学到的一切。 我将从创建一个主控制器开始。 创建主控制器的目的是避免编写重复代码,简化代码重用以及保持主类整洁有序。 在我们的例子中,所有需要多个单元的控制器都继承自BaseController 。 在这里,我们将存储UITableViewCellDelegateUITableViewCellDataSource方法。 让我示范一下这里发生了什么……

 import UIKit 
 class BaseController: UIViewController { 
  var cellTypes = [FormCellType]() 

override func viewDidLoad() {
super.viewDidLoad()

}

func currentCell(c: BaseCell, index: Int){

}

}
 extension BaseController: UITableViewDelegate{ 
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let c = cellTypes[indexPath.row]
return c.cellType().getHeight()
}
}
 extension BaseController: UITableViewDataSource{ 
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellTypes.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = cellTypes[indexPath.row]
let cellClass = c.cellType().getClass()
let cell = tableView.dequeueReusableCell(withIdentifier: cellClass.cellReuseIdentifier(), for: indexPath) as! BaseCell
cell.set(title: c.getTitle(), placeholder: c.placeholder(), image: c.image(), secureEntry: c.keyboardSecure(), keyboardType: c.keyboardType())
cell.type = c.cellType()
if cell.type == .dropdown{ cell.pickerOptions = c.pickerOptions() }
currentCell(c: cell, index: indexPath.row)
  return cell 
}
}

从顶部开始, cellTypes是一个存储FormCellType枚举值的数组。 我们将在主控制器中声明,但将在子视图控制器中进行填充。 您会看到将单元格的高度轻松传递给heightForRowAt()委托方法。 cellForRowAt()数据源方法也是如此,您只需要初始化BaseCell并从子单元格提供reuseIdentifier。 每当您的子级控制器中需要cellForRowAt()数据源方法时, currentCell()函数就会被覆盖。 就是用来设置数据源和委托方法。

现在,您所需要做的只是用所需的单元格填充子控制器中的cellTypes数组,它将立即填充UITableView。 在本教程中,我仅使用了2种不同形式的1个子控制器,但是您可以轻松使用x个子控制器,并且仅填写cellTypes数组。 看看我们的控制器有多短……

ViewController

 import UIKit 
 class ViewController: BaseController { 

//MARK: Private properties
@IBOutlet fileprivate weak var mainTableView: UITableView!
  override func viewDidLoad() { 
super.viewDidLoad()
cellTypes = [.name, .work, .contactTypes, .message, .send]
setupUI()
}
  override func currentCell(c: BaseCell, index: Int) { 
let type = cellTypes[index]
if type == .contactTypes{
let cell = c as! DropdownCell
cell.actionBlock = { (options) in
print(options)
}
}
}
}
 private extension ViewController{ 
func setupUI(){
for type in cellTypes{
mainTableView.registerNibForCellClass(type.cellType().getClass())
}
}

@IBAction func onRegisterButton(btn: UIButton){
cellTypes = [.email, .username, .pass, .send]
setupUI()
mainTableView.reloadData()
}
@IBAction func onContactUsButton(btn: UIButton){
cellTypes = [.name, .work, .contactTypes, .message, .send]
setupUI()
mainTableView.reloadData()
}
 } 

你有它。 您有一个组织良好的控制器,在单个UITableView中具有多个UITableViewCells支持。 🙂
为了将UITableViewCells注册到UITableView,我使用2个扩展…

UITableView + Extensions.swift

 import UIKit 
 extension UITableView { 

func registerNibForCellClass(_ cellClass: UITableViewCell.Type) {
let cellReuseIdentifier = cellClass.cellReuseIdentifier()
let nibCell = UINib(nibName: cellReuseIdentifier, bundle: nil)
register(nibCell, forCellReuseIdentifier: cellReuseIdentifier)
}
}

UITableViewCell + Extensions.swift

 import UIKit 
 extension UITableViewCell { 

class func cellReuseIdentifier() -> String {
return "\(self)"
}
}