iOS TableView,不带Storyboard的UITableView

时间:2020-02-23 14:45:55  来源:igfitidea点击:

今天,我们将在不使用Storyboard的情况下在我们的应用程序中实现iOS TableView UITableView类。
我们在这里使用Objective-C中的Storyboard实现了UITableView。
在本教程中,我们将使用Swift 3。

iOS TableView UITableView

与Swift 2.3相比,Swift 3中有许多主要代码更改。
对于来说,这只是我们将来所有iOS教程中都会使用的另一种语言。
但是,如果您尚未迁移到Swift 3,我将着重介绍本教程中的重大变化。

iOS TableView入门

首先,首先创建一个New Project,然后选择模板作为Single View Application。
在以下屏幕中,输入相关的应用程序和捆绑包标识符名称。
示例如下所示。

项目准备就绪后,选择Main.storyboard文件并将其删除(移至回收站)。

完成此操作后,让我们在模拟器上运行一个空项目,看看一切都很好。

观看右下角的日志中的错误将显示为:由于未捕获的异常'NSInvalidArgumentException',正在终止应用程序,原因:'在捆绑包NSBundle中找不到名为'Main'的故事板。

我们需要从项目配置中删除情节提要参考。

为此,转到" Info.plist",然后在"主要情节提要"文件基本名称字段中设置一个空值。

现在,我们无需编写故事板就可以编写应用程序代码了。
我们选择" ViewController.swift"并开始编码。

iOS UITableView示例代码

现在我们已经删除了Main.storyboard,ViewController.swift已取消链接。
我们需要找到一种方法使其成为应用程序的根视图控制器,而无需创建另一个故事板。

AppDelegate.swift文件可以帮助我们。
AppDelegate类可确保您的应用与系统和其他应用正确交互。

我们通过以下方式覆盖了函数application:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      
      window = UIWindow(frame: UIScreen.main.bounds)
      window!.backgroundColor = UIColor.white
      window!.rootViewController = ViewController()
      window!.makeKeyAndVisible()
      //Override point for customization after application launch.
      return true
  }

这使ViewController成为根视图控制器,并在我们的应用程序中启动第一个ViewController。
让我们跳到ViewController.swift文件,在该文件中将以编程方式显示和自定义UITableView。

首先,在ViewController.swift类中实现UITableViewDelegate和UITableViewDataSource协议,如下所示:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      
  }

}

以上三个函数需要强制执行,否则会出现编译时错误。

Swift 3注意:现在,函数参数始终包含第一个参数的标签。
本质上,函数名称的最后一部分已移动为第一个参数的名称。

例如:

//Swift 2.3
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
//Swift 3
override func numberOfSections(in tableView: UITableView) -> Int

除了Swift 3之外,由于它们是显而易见的,因此省略了函数中不必要的词。

回到代码,我们需要初始化并在视图中添加UITableView。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return tableData.count
  }
}

TableView的行是从tableData数组填充的。
TableView样式也可以设置为" UITableViewStyle.grouped"。
运行上述代码时的输出如下所示。

需要改进的地方:我们需要在视图和状态列之间添加填充。
同样,我们必须从空单元格中删除不必要的行分隔符。

要添加填充,请在viewDidLoad方法中添加以下行。

tableView.contentInset.top = 20

要删除空单元格和分隔线,我们需要包装TableView的高度,直到非空行数为止,并在底部添加页脚。

包括以上两个实现后的ViewController.swift代码如下。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      
      tableView.contentInset.top = 20
      let contentSize = self.tableView.contentSize
      let footer = UIView(frame: CGRect(x: self.tableView.frame.origin.x,
                                        y: self.tableView.frame.origin.y + contentSize.height,
                                        width: self.tableView.frame.size.width,
                                        height: self.tableView.frame.height - self.tableView.contentSize.height))
      
      self.tableView.tableFooterView = footer
      
      
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return tableData.count
  }
}

上面的ViewController实现的输出如下。

上面的输出中有一个滚动问题。
滚动时的TableView与状态列重叠,而不是在其下方。
让我们通过如下设置TableView的边界来解决此问题。

let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
tableView.frame = CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight)

将其添加到ViewController.swift文件中将产生以下代码并输出。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      
      let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
      let displayWidth: CGFloat = self.view.frame.width
      let displayHeight: CGFloat = self.view.frame.height
      
      tableView.contentInset.top = 20
      tableView.frame = CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight)
      let contentSize = self.tableView.contentSize
      let footer = UIView(frame: CGRect(x: self.tableView.frame.origin.x,
                                        y: self.tableView.frame.origin.y + contentSize.height,
                                        width: self.tableView.frame.size.width,
                                        height: self.tableView.frame.height - self.tableView.contentSize.height))
      
      self.tableView.tableFooterView = footer
      
      
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return tableData.count
  }
}

我们添加一项功能,以便选择任何行都会显示一个警报对话框。
我们需要覆盖函数didSelectRowAt来使每个都可选。

具有上述实现的ViewController.swift的代码如下。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      
      let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
      let displayWidth: CGFloat = self.view.frame.width
      let displayHeight: CGFloat = self.view.frame.height
      
      tableView.contentInset.top = 20
      tableView.frame = CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight)
      let contentSize = self.tableView.contentSize
      let footer = UIView(frame: CGRect(x: self.tableView.frame.origin.x,
                                        y: self.tableView.frame.origin.y + contentSize.height,
                                        width: self.tableView.frame.size.width,
                                        height: self.tableView.frame.height - self.tableView.contentSize.height))
      
      self.tableView.tableFooterView = footer
      
      
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return tableData.count
  }
  
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      tableView.deselectRow(at: indexPath, animated: true)
      let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
      showDialog(text: (currentCell.textLabel?.text)!)
  }
  
  func showDialog(text : String)
  {
      let alert = UIAlertController(title: "Alert", message: text, preferredStyle: UIAlertControllerStyle.alert)
      alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
      self.present(alert, animated: true, completion: nil)
  }
}

tableView.deselectRow(at:indexPath,animation:true)用于在不再按下时从选定行中删除突出显示。

具有多个节的UITableView

让我们自定义UITableView,使其包含多个部分。
我们需要重写下面显示的另外两个函数。

func numberOfSections(in tableView: UITableView) -> Int {
      
  }
  
  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      
  }

下面给出了具有多个部分的ViewController.swift。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  let data = [["0,0", "0,1", "0,2"], ["1,0", "1,1", "1,2"]]
  let headerTitles = ["Some Data 1", "Some Data 2"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      
      let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
      let displayWidth: CGFloat = self.view.frame.width
      let displayHeight: CGFloat = self.view.frame.height
      
      tableView.contentInset.top = 20
      tableView.frame = CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight)
      let contentSize = self.tableView.contentSize
      let footer = UIView(frame: CGRect(x: self.tableView.frame.origin.x,
                                        y: self.tableView.frame.origin.y + contentSize.height,
                                        width: self.tableView.frame.size.width,
                                        height: self.tableView.frame.height - self.tableView.contentSize.height))
      
      self.tableView.tableFooterView = footer
      
      
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      //cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      cell.textLabel?.text = "This is row \(data[indexPath.section][indexPath.row])"
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      //return tableData.count
      return data[section].count
  }
  
  func numberOfSections(in tableView: UITableView) -> Int {
      return data.count
  }
  
  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      if section < headerTitles.count {
          return headerTitles[section]
      }
      
      return nil
  }
  
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      tableView.deselectRow(at: indexPath, animated: true)
      let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
      showDialog(text: (currentCell.textLabel?.text)!)
  }
  
  func showDialog(text : String)
  {
      let alert = UIAlertController(title: "Alert", message: text, preferredStyle: UIAlertControllerStyle.alert)
      alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
      self.present(alert, animated: true, completion: nil)
  }
}

上面代码的输出如下。

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  var tableView = UITableView()
  var tableData = ["Beach", "Clubs", "Chill", "Dance"]
  let data = [["0,0", "0,1", "0,2"], ["1,0", "1,1", "1,2"]]
  let headerTitles = ["Some Data 1", "Some Data 2"]
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      tableView = UITableView(frame: self.view.bounds, style: UITableViewStyle.plain)
      tableView.dataSource = self
      tableView.delegate = self
      tableView.backgroundColor = UIColor.white
      
      tableView.register(UITableViewCell.self, forCellReuseIdentifier: "my")
      
      let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
      let displayWidth: CGFloat = self.view.frame.width
      let displayHeight: CGFloat = self.view.frame.height
      
      tableView.contentInset.top = 20
      tableView.frame = CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight)
      let contentSize = self.tableView.contentSize
      let footer = UIView(frame: CGRect(x: self.tableView.frame.origin.x,
                                        y: self.tableView.frame.origin.y + contentSize.height,
                                        width: self.tableView.frame.size.width,
                                        height: self.tableView.frame.height - self.tableView.contentSize.height))
      
      self.tableView.tableFooterView = footer
      
      
      view.addSubview(tableView)
      
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "my", for: indexPath)
      //cell.textLabel?.text = "This is row \(tableData[indexPath.row])"
      cell.textLabel?.text = "This is row \(data[indexPath.section][indexPath.row])"
      return cell
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      //return tableData.count
      return data[section].count
  }
  
  func numberOfSections(in tableView: UITableView) -> Int {
      return data.count
  }
  
  func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
      if section < headerTitles.count {
          return headerTitles[section]
      }
      
      return nil
  }
  
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      tableView.deselectRow(at: indexPath, animated: true)
      let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
      showDialog(text: (currentCell.textLabel?.text)!)
  }
  
  func showDialog(text : String)
  {
      let alert = UIAlertController(title: "Alert", message: text, preferredStyle: UIAlertControllerStyle.alert)
      alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
      self.present(alert, animated: true, completion: nil)
  }

  func scrollViewDidScroll(_ scrollView: UIScrollView) {
      let sectionHeight: CGFloat = 80
      if scrollView.contentOffset.y = sectionHeight {
          scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
      }
      
  }

}