Swift - 可编辑表格样例(可直接编辑单元格中内容、移动删除单元格)
作者:hangge | 2016-05-23 09:20
(本文代码已升级至Swift3)
1,效果图
(1)默认状态下,表格不可编辑,当点击单元格的时候会弹出提示框显示选中的内容。

(2)点击导航栏右侧编辑按钮,表格进入可以编辑状态
(3)这时我们可以删除表格项。
(4)也可以拖动调整单元格的顺序。
(5)然后就是本文的重点,在编辑状态下。直接点击单元格,即可在当前页面下直接编辑修改单元格中的内容。

2,单元格编辑功能讲解
(1)通过自定义 UITableViewCell,在其内部添加一个 textField 来实现可编辑的cell。
(2)通过改变 userInteractionEnabled 属性,可以让 textField 在可编辑与只读两个状态间切换。
(3)在编辑状态下要加大 textField 的左边距,因为左侧会出现个删除按钮图标。
(4)同时在编辑 cell 内容的时候,由于键盘会弹出挡到后面的表格。所以我们在键盘出现的时候,要通过改变 edgeInsets 的办法在底部加大 tableview 的 contentview 大小。
而结束编辑隐藏键盘时,需要还原 tableview 的 contentview 的尺寸。
3,程序代码
--- MyTableViewCell.swift(自定义单元格类)---
import UIKit
//表格数据实体类
class ListItem: NSObject {
var text: String
init(text: String) {
self.text = text
}
}
//单元格类
class MyTableViewCell: UITableViewCell, UITextFieldDelegate {
//单元格内部标签(可输入)
let label:UITextField
//单元格左边距
var leftMarginForLabel: CGFloat = 15.0
//单元格数据
var listItem:ListItem? {
didSet {
label.text = listItem!.text
}
}
//单元格是否可编辑
var labelEditable:Bool? {
didSet {
label.isUserInteractionEnabled = labelEditable!
//如果是可以编辑的话,要加大左边距(因为左边有个删除按钮)
leftMarginForLabel = labelEditable! ? 45.0 : 15.0
self.setNeedsLayout()
}
}
//初始化
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
//初始化文本标签
label = UITextField(frame: CGRect.null)
label.textColor = UIColor.black
label.font = UIFont.systemFont(ofSize: 16)
super.init(style: style, reuseIdentifier: reuseIdentifier)
//设置文本标签代理
label.delegate = self
label.contentVerticalAlignment = UIControlContentVerticalAlignment.center
//添加文本标签
addSubview(label)
}
//布局
override func layoutSubviews() {
super.layoutSubviews()
label.frame = CGRect(x: leftMarginForLabel, y: 0,
width: bounds.size.width - leftMarginForLabel,
height: bounds.size.height)
}
//键盘回车
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return false
}
//结束编辑
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if listItem != nil {
listItem?.text = textField.text!
}
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
--- ViewController.swift(主类)---
import UIKit
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource{
//表格
var tableView:UITableView?
//表格数据
var listItems = [ListItem]()
override func viewDidLoad() {
super.viewDidLoad()
//初始化数据
listItems = [ListItem(text: "这是条目1"), ListItem(text: "这是条目2"),
ListItem(text: "这是条目3"), ListItem(text: "这是条目4"),
ListItem(text: "这是条目5"), ListItem(text: "这是条目6"),
ListItem(text: "这是条目7"), ListItem(text: "这是条目8"),
ListItem(text: "这是条目9"), ListItem(text: "这是条目10"),
ListItem(text: "这是条目11"), ListItem(text: "这是条目12"),
ListItem(text: "这是条目13"), ListItem(text: "这是条目14"),
ListItem(text: "这是条目15"), ListItem(text: "这是条目16"),
ListItem(text: "这是条目17")]
//创建表视图
self.tableView = UITableView(frame:self.view.frame, style:.plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//创建一个重用的单元格
self.tableView!.register(MyTableViewCell.self, forCellReuseIdentifier: "tableCell")
self.view.addSubview(self.tableView!)
//监听键盘弹出通知
NotificationCenter.default
.addObserver(self,selector: #selector(keyboardWillShow(_:)),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
//监听键盘隐藏通知
NotificationCenter.default
.addObserver(self,selector: #selector(keyboardWillHide(_:)),
name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
//导航栏编辑按钮点击
@IBAction func editBarBtnClick(_ sender: UIBarButtonItem) {
//在正常状态和编辑状态之间切换
if(self.tableView!.isEditing == false){
self.tableView!.setEditing(true, animated:true)
sender.title = "保存"
}
else{
self.tableView!.setEditing(false, animated:true)
sender.title = "编辑"
}
//重新加载表数据(改变单元格输入框编辑/只读状态)
self.tableView?.reloadData()
}
//在本例中,有1个分区
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//返回表格行数
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listItems.count
}
//创建各单元显示内容(创建参数indexPath指定的单元)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell
{
let cell = tableView
.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)
as! MyTableViewCell
//设置单元格内容
let item = listItems[(indexPath as NSIndexPath).row]
cell.listItem = item
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
//内容标签是否可编辑
cell.labelEditable = tableView.isEditing
return cell
}
// UITableViewDelegate 方法,处理列表项的选中事件
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.tableView!.deselectRow(at: indexPath, animated: true)
let itemString = listItems[(indexPath as NSIndexPath).row].text
let alertController = UIAlertController(title: "提示!",
message: "你选中了【\(itemString)】",
preferredStyle: .alert)
let okAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
//是否有删除功能
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath)
-> UITableViewCellEditingStyle
{
if(self.tableView!.isEditing == false){
return UITableViewCellEditingStyle.none
}else{
return UITableViewCellEditingStyle.delete
}
}
//删除提示
func tableView(_ tableView: UITableView,
titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath)
-> String? {
return "确定删除?"
}
//编辑完毕(这里只有删除操作)
func tableView(_ tableView: UITableView,
commit editingStyle: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath) {
if(editingStyle == UITableViewCellEditingStyle.delete)
{
self.listItems.remove(at: (indexPath as NSIndexPath).row)
self.tableView!.reloadData()
print("你确认了删除按钮")
}
}
//在编辑状态,可以拖动设置cell位置
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
//移动cell事件
func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath,
to toIndexPath: IndexPath) {
if fromIndexPath != toIndexPath{
//获取移动行对应的值
let itemValue:ListItem = listItems[(fromIndexPath as NSIndexPath).row]
//删除移动的值
listItems.remove(at: (fromIndexPath as NSIndexPath).row)
//如果移动区域大于现有行数,直接在最后添加移动的值
if (toIndexPath as NSIndexPath).row > listItems.count{
listItems.append(itemValue)
}else{
//没有超过最大行数,则在目标位置添加刚才删除的值
listItems.insert(itemValue, at:(toIndexPath as NSIndexPath).row)
}
}
}
// 键盘显示
func keyboardWillShow(_ notification: Notification) {
let userInfo = (notification as NSNotification).userInfo!
//键盘尺寸
let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey]
as! NSValue).cgRectValue
var contentInsets:UIEdgeInsets
//判断是横屏还是竖屏
let statusBarOrientation = UIApplication.shared.statusBarOrientation
if UIInterfaceOrientationIsPortrait(statusBarOrientation) {
contentInsets = UIEdgeInsetsMake(64.0, 0.0, (keyboardSize.height), 0.0);
} else {
contentInsets = UIEdgeInsetsMake(64.0, 0.0, (keyboardSize.width), 0.0);
}
//tableview的contentview的底部大小
self.tableView!.contentInset = contentInsets;
self.tableView!.scrollIndicatorInsets = contentInsets;
}
// 键盘隐藏
func keyboardWillHide(_ notification: Notification) {
//还原tableview的contentview大小
let contentInsets:UIEdgeInsets = UIEdgeInsetsMake(64.0, 0.0, 0, 0.0);
self.tableView!.contentInset = contentInsets
self.tableView!.scrollIndicatorInsets = contentInsets
}
//页面移除时
override func viewDidDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
//取消键盘监听通知
NotificationCenter.default.removeObserver(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
源码下载:
全部评论(3)
// UITableViewDelegate 方法,处理列表项的选中事件
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.tableView!.deselectRow(at: indexPath, animated: true)
let itemString = listItems[(indexPath as NSIndexPath).row].text
let alertController = UIAlertController(title: "提示!",
message: "你选中了【\(itemString)】",
preferredStyle: .alert)
let okAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
请问 如上代码是在选中行的时候, 我的cell是自定义的, 点击cell中的label 才做这个操作, 请问如何做这个操作. 因为cell 中没有 self.present(alertController, animated: true, completion: nil) 这个方法
站长回复:present方法是VC(视图控制器)中才有的,直接在cell类中自然调不到。这里有两种方法解决。
方法1:使用通知。
cell中点击后发送一个通知,VC接收这个通知然后执行present方法。通知的用法参考我的这篇文章:Swift - 使用NotificationCenter发送通知,接收通知
先取得VC对象,然后再调用获取到的VC的present方法。如何获取参考我的这篇文章:Swift - 通过UIView对象找到其所在的UIViewController
把textfield换成text view,出现键盘时,怎么不能自动向上移动呢?
站长回复:如果用textView的话,你还要在它的编辑响应事件中,通过tableView.scrollToRow方法将当前单元格滚动到可视区域。
//监听键盘弹出通知
NSNotificationCenter.defaultCenter()
.addObserver(self,selector: #selector(keyboardWillShow(_:)),
name: UIKeyboardWillShowNotification, object: nil)
//监听键盘隐藏通知
NSNotificationCenter.defaultCenter()
.addObserver(self,selector: #selector(keyboardWillHide(_:)),
name: UIKeyboardWillHideNotification, object: nil)
keyboardWillShow()\keyboardWillHide()这两个方法没有调用是什么原因?大神帮忙看看
站长回复:原来代码通知那块写的有问题,现已修复。你再试试。