当前位置: > > > Swift - 可编辑表格样例(可直接编辑单元格中内容、移动删除单元格)

Swift - 可编辑表格样例(可直接编辑单元格中内容、移动删除单元格)

(本文代码已升级至Swift3)

本文演示如何制作一个可以编辑单元格内容的表格(UITableView)。

1,效果图
(1)默认状态下,表格不可编辑,当点击单元格的时候会弹出提示框显示选中的内容。
                 

(2)点击导航栏右侧编辑按钮,表格进入可以编辑状态

(3)这时我们可以删除表格项。

(4)也可以拖动调整单元格的顺序。

(5)然后就是本文的重点,在编辑状态下。直接点击单元格,即可在当前页面下直接编辑修改单元格中的内容。

2,单元格编辑功能讲解
(1)通过自定义 UITableViewCell,在其内部添加一个 textField 来实现可编辑的cell。
(2)通过改变 userInteractionEnabled 属性,可以让 textField 在可编辑与只读两个状态间切换。
(3)在编辑状态下要加大 textField 的左边距,因为左侧会出现个删除按钮图标。
(4)同时在编辑 cell 内容的时候,由于键盘会弹出挡到后面的表格。所以我们在键盘出现的时候,要通过改变 edgeInsets 的办法在底部加大 tableviewcontentview 大小。
而结束编辑隐藏键盘时,需要还原 tableviewcontentview 的尺寸。

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()
    }
}
源码下载hangge_1175.zip
评论3
  • 3楼
    2017-02-09 19:49
    yuesf

    // 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发送通知,接收通知 

    方法2:直接在cell中获取所在的VC
    先取得VC对象,然后再调用获取到的VC的present方法。如何获取参考我的这篇文章:Swift - 通过UIView对象找到其所在的UIViewController

  • 2楼
    2016-12-14 09:28
    小学生

    把textfield换成text view,出现键盘时,怎么不能自动向上移动呢?

    站长回复

    如果用textView的话,你还要在它的编辑响应事件中,通过tableView.scrollToRow方法将当前单元格滚动到可视区域。

  • 1楼
    2016-05-26 10:37
    study

    //监听键盘弹出通知
    NSNotificationCenter.defaultCenter()
    .addObserver(self,selector: #selector(keyboardWillShow(_:)),
    name: UIKeyboardWillShowNotification, object: nil)
    //监听键盘隐藏通知
    NSNotificationCenter.defaultCenter()
    .addObserver(self,selector: #selector(keyboardWillHide(_:)),
    name: UIKeyboardWillHideNotification, object: nil)
    keyboardWillShow()\keyboardWillHide()这两个方法没有调用是什么原因?大神帮忙看看

    站长回复

    原来代码通知那块写的有问题,现已修复。你再试试。