iOS TableView,不带Storyboard的UITableView
今天,我们将在不使用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)
}
}
}

