(作业)TableView、Delegate、DataSource

iOS基础 - UITableView的数据源(dataSource)和代理(delegate) UITableView的数据源(dataSource)和代理(delegate) UITableView需要一个数据源(dataSource)来显示数据,UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等。没有设置数据源的UITableView只是个空壳。凡是遵守UITableViewDataSource协议的OC对象,都可以是UITableView的数据源。 通常都 阅读详情

来来来,继续作业系列的博客,老规矩了,直接上要求
这里写图片描述

这次作业涉及到iOS开发中最常用的一个UI组件UITableView,App Store中的几乎所有工具型和应用型App都使用了UITableView,而初学UITableView又不是那么容易,因为它涉及到很多代理和数据源的问题,但用熟练了,会觉得它非常地方便。
废话不多说了,先来完成要求吧,在这个过程中,博主会讲解一些细节方面的东西。

首先是需要用到作业3中的类,点击这里,看看细节。我这里就不多说了,直接把全部代码贴出来吧。各位最好新建一个文件来放这个类,不然会使ViewController显得非常臃肿。
Person.swift

import Foundation

//性别的枚举
enum Gender: Int {
    case male    //男性
    case female  //女性
    case unknow  //未知

    //重载>操作符,方便后面排序使用
    static func >(lhs: Gender, rhs: Gender) -> Bool {
        return lhs.rawValue < rhs.rawValue
    }
}

//公寓的枚举
enum Department {
    case one, two, three
}

//学校协议
protocol SchoolProtocol {
    var department: Department { get set }
    func lendBook()
}

//人类
class Person: CustomStringConvertible  {
    var firstName: String  //姓
    var lastName: String  //名
    var age: Int  //年龄
    var gender: Gender  //性别

    var fullName: String {  //全名
        get {
            return firstName + lastName
        }
    }

    //构造方法
    init(firstName: String, lastName: String, age: Int, gender: Gender) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
        self.gender = gender
    }

    convenience init(firstName: String, age: Int, gender: Gender) {
        self.init(firstName: firstName, lastName: "", age: age, gender: gender)
    }

    convenience init(firstName: String) {
        self.init(firstName: firstName, age: 0, gender: Gender.unknow)
    }

    required convenience init() {
        self.init(firstName: "")
    }

    //重载==
    static func ==(lhs: Person, rhs: Person) -> Bool {
        return lhs.fullName == rhs.fullName && lhs.age == rhs.age && lhs.gender == rhs.gender
    }

    //重载!=
    static func !=(lhs: Person, rhs: Person) -> Bool {
        return !(lhs == rhs)
    }

    //实现CustomStringConvertible协议中的计算属性,可以使用print直接输出对象内容
    var description: String {
        return "fullName: \(self.fullName), age: \(self.age), gender: \(self.gender)"
    }

    //输出Person XXX is running
    func run() {
        print("Person \(self.fullName) is running")
    }
}

//教师类
class Teacher: Person, SchoolProtocol {
    var title: String  //标题
    var department: Department  //公寓

    //构造方法
    init(title: String, firstName: String, lastName: String, age: Int, gender: Gender, department: Department) {
        self.title = title
        self.department = department
        super.init(firstName: firstName, lastName: lastName, age: age, gender: gender)
    }

    init(title: String, department: Department) {
        self.title = title
        self.department = department
        super.init(firstName: "", lastName: "", age: 0, gender: .unknow)
    }

    convenience required init() {
        self.init(title: "", department: Department.one)
    }

    //重写父类的计算属性
    override var description: String {
        return "title: \(self.title), fullName: \(self.fullName), age: \(self.age), gender: \(self.gender), department: \(self.department)"
    }

    //重载父类run方法
    override func run() {
        print("Teacher \(self.fullName) is running")
    }

    //遵循协议的方法
    func lendBook() {
        print("Teacher \(self.fullName) lend a book")
    }
}

//学生类
class Student: Person, SchoolProtocol {
    var stuNo: Int  //学号
    var department: Department  //公寓

    //构造方法
    init(stuNo: Int, firstName: String, lastName: String, age: Int, gender: Gender, department: Department) {
        self.stuNo = stuNo
        self.department = department
        super.init(firstName: firstName, lastName: lastName, age: age, gender: gender)
    }

    convenience init(stuNo: Int, firstName: String, lastName: String, age: Int, gender: Gender) {
        self.init(stuNo: stuNo, firstName: firstName, lastName: lastName, age: age, gender: gender, department: .one)
    }

    init(stuNo: Int, department: Department) {
        self.stuNo = stuNo
        self.department = department
        super.init(firstName: "", lastName: "", age: 0, gender: Gender.unknow)
    }

    required convenience init() {
        self.init(stuNo: 0, department: .one)
    }

    //重写父类的计算属性
    override var description: String {
        return "stuNo: \(self.stuNo), fullName: \(self.fullName), age: \(self.age), gender: \(self.gender), department: \(self.department)"
    }

    //重载父类run方法
    override func run() {
        print("Student \(self.fullName) is running")
    }

    //遵循协议的方法
    func lendBook() {
        print("Teacher \(self.fullName) lend a book")
    }
}

接下来就开始进入主题

为了方便我们对界面的布局,我们使用UINavigationController来放置按钮,所以需要在AppDelegate.swift的application(_:didFinishLaunchingWithOptions:)方法中添加下面这句代码

self.window?.rootViewController = UINavigationController(rootViewController: ViewController())

这句代码的作用就是将UIWindow的根视图控制器设置为UINavigationController并将该导航栏控制器的根视图控制器设置为ViewController。这样我们的ViewController就放置在导航栏控制器中了。

然后我们需要在ViewController中做如下几个属性的声明

//学生数组
var students = [Student]()
//教师数组
var teachers = [Teacher]()
//定义表头数组
var tableTitle = ["Teacher", "Student"]
//定义一个表视图
var table: UITableView!
//右边按钮
var rightItem: UIBarButtonItem!
//弹出框
var alert: UIAlertController!

这些在下面的代码中会使用到,大致的作用看注释应该差不多能理解了。

然后我们需要创建数据,并按要求排序

//生成3个Teacher对象
        for i in 1...3 {
            let temp = Teacher(title: "教授", firstName: "张", lastName: "\(i)", age: 21, gender: .female, department: .one)
            teachers.append(temp)
        }
        //生成4个Student对象
        for i in 1..<5 {
            let temp = Student(stuNo: 2015110100 + i, firstName: "李", lastName: "\(i)", age: 19, gender: .male, department: .two)
            students.append(temp)
        }

        //按全名排序
        teachers.sort { return $0.fullName < $1.fullName }
        students.sort { return $0.fullName < $1.fullName }

在准备好数据之后,我们就需要创建表视图了

//创建表视图,并设置代理和数据源
table = UITableView(frame: self.view.bounds)
table.delegate = self
table.dataSource = self
self.view.addSubview(table)

然后,我们在导航栏控制器上添加两个按钮,左边添加一个“添加”按钮,用于添加学生;右边添加“编辑”按钮,用于对表视图进行编辑操作。

//导航栏控制器右边的按钮
        rightItem = UIBarButtonItem(title: "编辑", style: .plain, target: self, action: #selector(edit))
        self.navigationItem.rightBarButtonItem = rightItem

        //导航栏控制器左边的按钮
        let leftItem = UIBarButtonItem(title: "添加", style: .plain, target: self, action: #selector(addStudent))
        self.navigationItem.leftBarButtonItem = leftItem

我们添加的“编辑”按钮在开始时声明,是因为我们需要更改它的title属性,当我们进入编辑状态时,需要将它改为“完成”;当它退出编辑状态时,需要将其改为“编辑”。

/// 编辑表视图
    @objc func edit() {
        if table.isEditing {
            rightItem.title = "编辑"
            table.isEditing = false
        } else {
            rightItem.title = "完成"
            table.isEditing = true
        }
    }

“编辑”按钮的功能做完之后,就需要设置“添加”按钮的功能了。按要求需要输入相应的学生信息,我们这里就使用UIAlertController来弹出提示框,让用户输入相应的信息。

/// 添加学生提示框
    @objc func addStudent() {

        alert = UIAlertController(title: "hh", message: "ss", preferredStyle: .alert)
        alert.addTextField { (textField) in
            textField.placeholder = "学生学号"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生姓"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生名"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生性别"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生年龄"
        }
        let OKBtn = UIAlertAction(title: "确定", style: .default) { (alert) in
            self.add()
        }
        let cancelBtn = UIAlertAction(title: "取消", style: .cancel, handler: nil)
        alert.addAction(OKBtn)
        alert.addAction(cancelBtn)
        self.present(alert, animated: true, completion: nil)

    }

    /// 添加学生
    func add() {
        let no = Int(alert.textFields![0].text!)
        let firstName = alert.textFields![1].text!
        let lastName = alert.textFields![2].text!
        let gender: Gender
        switch alert.textFields![3].text! {
        case "男":
            gender = .male
        case "女":
            gender = .female
        default:
            gender = .unknow
        }
        let age = Int(alert.textFields![4].text!)
        let student = Student(stuNo: no!, firstName: firstName, lastName: lastName, age: age!, gender: gender)
        students.append(student)

        table.reloadData()
    }

我们先初始化一个alert型的UIAlertController,除了alert,常用的还有actionSheet,读者可以自行百度区别。然后向alert里面添加了5个UITextField用于用户输入学生信息,然后添加两个按钮,一个“确定”,一个“取消”,功能就不说了。注意UIAlertController上的按钮是UIAlertAction,而不是UIButton。在初始化UIAlertController时可以添加事件在按钮上,我们在“确定”按钮上添加了一个函数,用于将用户填的信息加入到表视图中。最后调用UIViewController的present方法将提示框弹出即可。
在add()方法中,我们将UITextField中的内容一一取出并做相关的处理,即可创建一个学生的实例,然后添加到学生数组中。这时我们只更新的数据,而界面(也就是表视图)并没有更新,所以我们需要让表视图重新加载一次数据。(博主并没有做输入的验证,各位读者可以自行扩展)。

在做完这些基础的操作之后,我们的数据和功能就已经准备完成了,接下来就要进行表视图的操作了。在最开始初始化表视图时,我们指定了其代理和数据源,这是系统定义个两个协议,通过这两个协议,我们可以很方便地对表视图进行操作和调整。所以我们需要在类的声明部分遵循UITableViewDelegate协议和UITableViewDataSource协议。截图如下:
这里写图片描述
UITableViewDelegate协议没有必须要实现的方法,而UITableViewDataSource中有两个方法必须要实现。
第一个是指定表视图中单元格行数的方法

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            return teachers.count
        } else {
            return students.count
        }
    }

因为我们这里有两种不同的数据信息需要展示,所以博主做了两个section(部分),分别返回两个section的行数即可。

第二个是指定表视图中的单元格的方法

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let identifier = tableTitle[indexPath.section]

        var cell = tableView.dequeueReusableCell(withIdentifier: identifier)

        if cell == nil {
            let style: UITableViewCellStyle = (identifier == "Teacher") ? .subtitle : .default
            cell = UITableViewCell(style: style, reuseIdentifier: identifier)
            cell?.accessoryType = .disclosureIndicator
        }

        switch identifier {
        case "Teacher":
            cell?.textLabel?.text = teachers[indexPath.row].fullName
            cell?.detailTextLabel?.text = teachers[indexPath.row].title
        case "Student":
            cell?.textLabel?.text = students[indexPath.row].fullName
        default:
            break
        }

        return cell!
    }

在这个方法中,我们先初始化了一个identifier常量,这个常量用于单元格的重用。在表视图中,往往有很多的单元格,但如果我们每滚动到一个单元格的位置时都要将其初始化的话,这样就会是界面卡顿,并且浪费很多内存,所以我们需要优化一下表视图,而系统刚好就提供了这种优化的方法,那就是注册单元格的重用。根据不同的identifier可以重用不同的单元格,在表视图滚动时,只需要更改单元格展示的数据即可,这样就可以大大的提高效率。
重用的机制是将已经初始化的单元格放入队列中,待需要时将其取出,更新数据。所以我们要使用dequeueReusableCell(withIdentifier:)方法从队列中取出单元格。但我们第一次进入界面时,队列中并没有单元格,所以我们取出的cell会是空的,我们就可以在这里初始化cell,并设置其样式。当cell取出后,就可以将数据展示在上面了,最后返回cell即可。

因为我们这里有两个部分的数据,所以我们需要指定section数

func numberOfSections(in tableView: UITableView) -> Int {
        return tableTitle.count
    }

然后为了用户方便,我们可以指定每一个section的头,来提示一些信息

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return tableTitle[section]
    }

做完这一些操作之后,我们就可以看到界面上展示的效果了。运行程序看看
初始界面
这里写图片描述
点击“添加”按钮
这里写图片描述
点击“编辑”按钮
这里写图片描述

到这里我们发现,我们点击“添加”按钮后输入信息,并点击“确定”,新添加的学生信息可以添加到表视图中,但我们点击编辑按钮后,却删除不了单元格,那是因为我们还缺少一些必要的设置。

首先我们需要使用代理设置每一个单于格的编辑类型,因为我们已经做了添加按钮,所以我们直接指定每一个单元格都是删除

func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        return .delete
    }

我们可以更改一下滑动出来的删除按钮显示的文字

func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
        return "删除"
    }

在做完这些之后,我们还是不能删除单元格,那是因为还需要一个方法来真正的删除数据

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCellEditingStyle.delete {
            if indexPath.section == 0 {
                teachers.remove(at: indexPath.row)
            } else {
                students.remove(at: indexPath.row)
            }

            tableView.deleteRows(at: [indexPath], with: .left)
        }
    }

在这个方法中,我们需要先判断编辑的类型,当为delete时,先删除section对应的数组中的数据,然后再删除表视图中的cell。删除cell使用deleteRows(at:with:)方法可以指定删除时的动画效果。
到这里,我们就可以删除单元格了。这时我们有了添加和删除的功能,但我们需要实现点击单元格给出选择反馈和移动cell。

首先实现点击单于给出反馈,我们做成点击某个cell后,弹出一个提示框,提示框中显示选中的是学生还是教师,并显示其名字。

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let category = tableTitle[indexPath.section]

        let name: String
        if indexPath.section == 0 {
            name = teachers[indexPath.row].fullName
        } else {
            name = students[indexPath.row].fullName
        }

        let message = "you selected \(category), name: \(name)"

        let alert = UIAlertController(title: "系统提示", message: message, preferredStyle: .alert)
        let OKBtn = UIAlertAction(title: "确定", style: .default, handler: nil)
        alert.addAction(OKBtn)
        self.present(alert, animated: true, completion: nil)
    }

做完这个之后,我们最后就需要实现单元格的移动了

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        if sourceIndexPath.section != destinationIndexPath.section {
            tableView.reloadData()
        } else {
            if sourceIndexPath.section == 0 {
                teachers.insert(teachers.remove(at: sourceIndexPath.row), at: destinationIndexPath.row)
            } else {
                students.insert(students.remove(at: sourceIndexPath.row), at: destinationIndexPath.row)
            }
        }
    }

在这个方法中,我们限制了单元格只能在自己的section里面移动,一旦跨section移动,那就会回到原来的位置。我们只要实现了这个方法,就可以将单元格移动了,但相应的,我们需要更改数据数组中相应元素的位置,这样才不会引起错误。

到这里,我们就基本完成了作业的要求,运行看一下效果。
这里写图片描述

添加学生信息
这里写图片描述
这里写图片描述

删除单元格
这里写图片描述
这里写图片描述
在Swift4.0之后,我们实现了删除功能之后,可以不需要点击“编辑”按钮进入编辑状态再删除单元格了,我们可以在cell上向左滑动,就会有删除按钮出现
这里写图片描述
这里写图片描述

最后是移动单于格了。
我们先进入编辑状态,然后点击右边的三根横线的按钮,就可以拖动单元格了。
这里写图片描述

到这里,本次作业完成,各位读者如果需要完善功能,可以在代码中进行改进,下面附上这次作业的ViewController的所有代码。
ViewConroller.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    //学生数组
    var students = [Student]()
    //教师数组
    var teachers = [Teacher]()
    //定义表头数组
    var tableTitle = ["Teacher", "Student"]
    //定义一个表视图
    var table: UITableView!
    //右边按钮
    var rightItem: UIBarButtonItem!
    //弹出框
    var alert: UIAlertController!

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

        self.title = "table"
        self.view.backgroundColor = UIColor.white

        //生成3个Teacher对象
        for i in 1...3 {
            let temp = Teacher(title: "教授", firstName: "张", lastName: "\(i)", age: 21, gender: .female, department: .one)
            teachers.append(temp)
        }
        //生成4个Student对象
        for i in 1..<5 {
            let temp = Student(stuNo: 2015110100 + i, firstName: "李", lastName: "\(i)", age: 19, gender: .male, department: .two)
            students.append(temp)
        }

        //按全名排序
        teachers.sort { return $0.fullName < $1.fullName }
        students.sort { return $0.fullName < $1.fullName }

        //创建表视图,并设置代理和数据源
        table = UITableView(frame: self.view.bounds)
        table.delegate = self
        table.dataSource = self
        self.view.addSubview(table)

        //导航栏控制器右边的按钮
        rightItem = UIBarButtonItem(title: "编辑", style: .plain, target: self, action: #selector(edit))
        self.navigationItem.rightBarButtonItem = rightItem

        //导航栏控制器左边的按钮
        let leftItem = UIBarButtonItem(title: "添加", style: .plain, target: self, action: #selector(addStudent))
        self.navigationItem.leftBarButtonItem = leftItem

    }

    /// 添加学生提示框
    @objc func addStudent() {

        alert = UIAlertController(title: "hh", message: "ss", preferredStyle: .alert)
        alert.addTextField { (textField) in
            textField.placeholder = "学生学号"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生姓"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生名"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生性别"
        }
        alert.addTextField { (textField) in
            textField.placeholder = "学生年龄"
        }

        let OKBtn = UIAlertAction(title: "确定", style: .default) { (alert) in
            self.add()
        }
        let cancelBtn = UIAlertAction(title: "取消", style: .cancel, handler: nil)
        alert.addAction(OKBtn)
        alert.addAction(cancelBtn)
        self.present(alert, animated: true, completion: nil)

    }

    /// 添加学生
    func add() {
        let no = Int(alert.textFields![0].text!)
        let firstName = alert.textFields![1].text!
        let lastName = alert.textFields![2].text!
        let gender: Gender
        switch alert.textFields![3].text! {
        case "男":
            gender = .male
        case "女":
            gender = .female
        default:
            gender = .unknow
        }
        let age = Int(alert.textFields![4].text!)
        let student = Student(stuNo: no!, firstName: firstName, lastName: lastName, age: age!, gender: gender)
        students.append(student)

        table.reloadData()
    }

    /// 编辑表视图
    @objc func edit() {
        if table.isEditing {
            rightItem.title = "编辑"
            table.isEditing = false
        } else {
            rightItem.title = "完成"
            table.isEditing = true
        }
    }

    // MARK: delegate
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        return .delete
    }

    func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
        return "删除"
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let category = tableTitle[indexPath.section]

        let name: String
        if indexPath.section == 0 {
            name = teachers[indexPath.row].fullName
        } else {
            name = students[indexPath.row].fullName
        }

        let message = "you selected \(category), name: \(name)"

        let alert = UIAlertController(title: "系统提示", message: message, preferredStyle: .alert)
        let OKBtn = UIAlertAction(title: "确定", style: .default, handler: nil)
        alert.addAction(OKBtn)
        self.present(alert, animated: true, completion: nil)
    }


    // MARK: data source
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCellEditingStyle.delete {
            if indexPath.section == 0 {
                teachers.remove(at: indexPath.row)
            } else {
                students.remove(at: indexPath.row)
            }

            tableView.deleteRows(at: [indexPath], with: .left)
        }
    }

    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        if sourceIndexPath.section != destinationIndexPath.section {
            tableView.reloadData()
        } else {
            if sourceIndexPath.section == 0 {
                teachers.insert(teachers.remove(at: sourceIndexPath.row), at: destinationIndexPath.row)
            } else {
                students.insert(students.remove(at: sourceIndexPath.row), at: destinationIndexPath.row)
            }
        }
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return tableTitle.count
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return tableTitle[section]
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 0 {
            return teachers.count
        } else {
            return students.count
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let identifier = tableTitle[indexPath.section]

        var cell = tableView.dequeueReusableCell(withIdentifier: identifier)

        if cell == nil {
            let style: UITableViewCellStyle = (identifier == "Teacher") ? .subtitle : .default
            cell = UITableViewCell(style: style, reuseIdentifier: identifier)
            cell?.accessoryType = .disclosureIndicator
        }

        switch identifier {
        case "Teacher":
            cell?.textLabel?.text = teachers[indexPath.row].fullName
            cell?.detailTextLabel?.text = teachers[indexPath.row].title
        case "Student":
            cell?.textLabel?.text = students[indexPath.row].fullName
        default:
            break
        }

        return cell!
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}
使用Swift实现TableView的展示 在ViewController.swift文件中,我们需要设置TableView的数据源和代理,以便处理TableView的数据和交互事件。接下来,我们需要实现UITableViewDataSource协议中的方法,以提供TableView所需的数据。在Interface Builder中,选择TableView并在属性检查器中设置"Identifier"属性为"Cell",这将作为单元格的重用标识符。方法用于配置每个单元格的内容,这里我们将每行的文本设置为"Row X",其中X为行号。 阅读详情

相关推荐

如何在 Swiftui 中构建具有多种单元格类型的 Tableview?

定义数据模型是创建具有多种单元格类型的 TableView 的第一步。必须识别您希望在 TableView 中显示的多种数据类型,并且必须为每个类别创建关联的数据模型。为了便于说明,假设您要显示可以是文本或图像的对象列表。

iCloudEnd的博客 646

1.Swift基础控件:TableView列表

在 iOS 开发中,UITableView 是一个常用的界面组件,用于显示列表型数据。UITableView 可以展示大量数据,并支持滚动、分组、选择等功能,是开发 iOS 应用中常见的组件之一。分组显示:UITableView 可以按照分组的方式显示数据,每个分组可以有一个 header 和一个 footer。可定制的单元格:UITableView 中的每个单元格可以自定义样式,包括文本、图像、按钮等。滚动和重用机制:UITableView 支持滚动功能,并且具有重用机制,可以重复使用单元格,提高性能。

长沙火山 1791

告别里程焦虑:手把手教你用MATLAB/Simulink仿真电动车AMT换挡规律(附代码)

本文详细介绍了如何使用MATLAB/Simulink仿真电动车AMT换挡规律,帮助解决里程焦虑问题。通过搭建完整的仿真系统,包括电机MAP图处理、Stateflow逻辑实现和动力性与经济性换挡曲线生成,优化电动车换挡策略以提升续航与性能。附带的代码示例和实用技巧使开发者能够快速上手并验证仿真结果。

weixin_26757925的博客 306

SwiftUI创建TableView(1)

swiftUI是苹果2019年新推出的一款UI框架,势必是未来苹果主推的一种UI搭建方式,并且其确实可以用更少的代码实现APP界面的构建,声明式的UI实现方式使得视图层级显而易见。UItableview是UIKit中常见的一种视图,在传统的使用方式中,我们需要配置代理,配置cell等繁琐的流程,但是现在我们有了swiftUI,简单几行代码就能实现tableview。下面我来介绍一下swiftUI实现tableview的三种方式:1、固定行数方式,2、绑定数组的可变行数方式。

旭日的博客 1227

【初阶】手把手教你轻松上手Denodo | 连接数据库并创建基本视图 (二)

在本教程的第二部分,我们将涵盖:连接到数据库,与创建基本视图。

Denodo的博客 1106

搞定安卓11+抓包难题:用Kitsune Mask、magic_overlayfs和LSPosed三件套绕过SSL Pinning(保姆级教程)

本文详细解析了安卓11+系统中抓包面临的核心挑战,包括SSL Pinning和Root检测机制,并提供了使用Kitsune Mask、magic_overlayfs和LSPosed三件套的完整解决方案。通过动态分区技术解析和工具链配置,帮助开发者绕过安全限制,实现高效抓包分析。

weixin_42665255的博客 442

Swift TableView的使用

创建一个列表,包含列表的头部和尾部,SectionHeader和SectionFooter。

长沙火山 1742

Oracle ERP EBS R12 GL总帐业务方案 系统操作

本文档是Oracle ERP系统的总账管理模块使用手册,主要包含以下内容:1.系统架构设计,详细介绍多组织架构、会计科目设置等核心概念;2.总账日常操作流程,包括凭证录入、审核、过账等全流程管理;3.特殊业务处理,涵盖预算控制、内部往来、合并报表等专项功能;4.期末处理,说明月结、年结的操作步骤;5.系统设置指南,提供会计科目表、权限控制等配置说明;6.附录包含各业务模块的分录示例和系统操作图标说明。本手册适用于财务人员学习和使用Oracle ERP总账系统,重点讲解系统功能要点和实操步骤。

anpeng2019的博客 1888

iOS tableview的delegate和datasource执行顺序

前端时间去面试的时候遇到这道题,结果只知道一个大概调用顺序,于是写了一个demo测试了一下,执行这些方法的顺序如下:1.//有多少列 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView2.//cell 页眉高度 -(CGFloat)tableView:(UITableView *)tableView heightFor

Tom_woo_y的博客 1029

比Synergy更轻量!Barrier鼠标共享工具实测:5分钟实现Win/Mac/Linux三设备无缝切换

本文实测了开源免费的Barrier鼠标共享工具,详细指导用户如何在5分钟内于Windows、macOS和Linux三台设备间实现一套键鼠的无缝切换。文章对比了其与Synergy的优势,提供了分平台安装、核心服务端-客户端配置、防火墙设置等完整教程,并分享了解决连接问题的关键技巧,是提升多设备办公效率的实用指南。

weixin_29304021的博客 598

openEuler 20.03 LTS面向RK3399移植系列(3)—openEuler 20.03 LTS面向Firefly RK3399的第一个版本(Linux kernel 4.4)

任务动机:完成openEuler面向RK3399开发板的移植。 任务描述:按照固件烧写准备,固件获取,固件打包和烧写的步骤实现openEuler面向RK3399开发板的移植,整理过程和相关命令记录到文档。 1. 制作openEuler的rootfs.img 1.1 开发环境 树莓派--openEuler系统 openEuler 20.03 LTS 的内测版本镜像,下载。 1.2 具体操作 1.2.1 ssh远程连接树莓派 确定树莓派IP ssh连接 1.2.2 制作rootfs.img

火种源码的机器人技术专栏 2369

UITableview delegate dataSource调用探究

UITableview是大家常用的UIKit组件之一,使用中我们最常遇到的就是对delegate和dataSource这两个委托的使用。我们大多数人可能知道当reloadData这个方法被调用时,delegate和dataSource就会被回调,但是其中具体的细节,可能很多人不会去探究。我最近有兴趣来探讨这个问题是因为我最近遇到过dataSource中有的方法被调用,但是有的方法没有被调...

abc0432136的博客 376

机器学习与深度学习基础概念

本篇主要介绍机器学习主要概念,包括不限于标签、特征、样本、训练、模型、回归模型、分类模型、泛化、过拟合、预测、平稳性、训练集、验证集、测试集。

专注大数据与人工智能技术分享,欢迎私信加群互相学习! 3万+

tableView 数据源(dataSource)和代理(delegate)

UITableView的数据源(dataSource)和代理(delegate) UITableView需要一个数据源(dataSource)来显示数据,UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等。没有设置数据源的UITableView只是个空壳。凡是遵守UITableViewDataSource协议的OC对象,都可以是UITableView的数...

Linux2060的博客 264

1553B总线介绍及其硬件方案设计

军用1553B总线 1.简介 简单介绍可参考(https://blog.youkuaiyun.com/ScilogyHunter/article/details/108444231)博主写的很详细。 1553B总线是MIL-STD-1553总线的简称,其中B就是BUS,MIL-STD-1553B总线是飞机内部时分制命令/响应式多路复用数据总线。1553B数据总线标准是20世纪70年代由美国公布的一种串行多路数据总线标准。1553B总线能挂31个远置终端,1553B总线采用指令/响应型通信协议,它有三种终端类型: BC:

小幽余生不加糖 1万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

[课堂实践与项目]UITableView DataSource,Delegate方法的使用

今天就tableView的DataSource和Delegate 两个代理方法进行介绍和项目支持下的作用实现。首先来看看 一,UITableViewDataSource 的所有代理方法。 //__________________________________________________________________________________________________

1874

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

UIViewController中使用dataSource与delegate

苹果的示例代码: @implementationAPLMainTableViewController -(void)viewDidLoad{ [superviewDidLoad]; _resultsTableController=[[APLResultsTableC...

chuichuanqin0793的博客 235

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

tableview中的delegate、DataSource原理

// // ViewController.m //#import "ViewController.h"@interface ViewController () // 4.遵守代理@end@implementation ViewController- (void)viewDidLoad { UITableV

西瓜的博客 1504

ios实现两个tableview联动

两个tableview的联动,滑动左侧tableview,右侧tableview跟着滑动 其实实现起来比较简单,只是需要搞清楚他们之间的区别和联系,还有就是调用一个 - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

Mr_Big_Kong的博客 1885

iOS tableview的常用delegate和dataSource执行顺序

在这次项目中遇到了一个特别奇葩的问题:表视图创建的cell在7以上的系统能正常运行显示,在模拟器上就不能正常实现......为解决这个问题,纠结了好久...... 对在7系统上不显示的猜测: 用masory设置约束不对,导致cell不显示; 对这个猜测也弄了好久,对一个个控件的约束都好好检查了一遍,发现实在是没有错的...后来迫不得已,自己不用masory,直接用frame约束,发现还是不显...

weixin_30271335的博客 129

故事版xib --view上添加tableview

以前我们实现过纯代码版view上添加tableview http://blog.youkuaiyun.com/lee727n/article/details/72582677 下面我们来看一下,如何通过故事版和xib实现 效果如下 获取数据以及实现方式和前面一片微博一样,不在重复,具体参考: http://blog.youkuaiyun.com/lee727n/article/details/72584807这里具

lee727n的博客 2657

【我们都爱Paul Hegarty】斯坦福IOS8公开课个人笔记37 TableView Delegate

上一话介绍了tableView的datasource,本话来介绍另一个重要的部分delegate。 当我们点击一个cell的时候,如何跳转到另外一个mvc中呢? 像增加其他segue一样,点击cell按住control键,右键连线到另一个mvc上,然后松手,选择需要的segue类型。 如果你的cell上还有其他按钮,比如detail disclosure,你也可以选择它的segue:

Cwift的专栏 1496

UI学习: TableView初识

告诉 tableView:如果没有可复用的 cell,就用这个类(UITableViewCell)创建一个,标识叫 CellID。cell 滑出屏幕 –> 进入复用池(备用)–> 当需要新 cell 时 –> 优先从池子拿。因为已经注册了,系统知道如何去创建, 反之, 如果没有在 viewDidLoad 中写。cell的注册, 系统就不知道如何去创建, 程序就会崩溃。的意思是向 tableView 要一个。如果池子里面有, 就返回一个旧的 cell。Cell 的详细内容, 下文会详细讲解。

for_ever_love__的博客 239

打造轻量级 tableViewController 之抽离 DataSource/Delegate

前言 UITableView/UICollectionView 是我们开发中使用最为频繁的两个控件。关于其使用的实践网上已经有很多优秀的总结了,所以我不打算再啰嗦了。今天要讨论的问题基于 objc.io 的一遍文章 Lighter View Controllers,此文讲述如何通过抽取频繁出现的配置类型的代码到专门的一个 DataSource/Delegate 里面来为 Cont

iiiiiiiiiihero的专栏 1888

TableView封装心得 去掉TableView中委托里麻烦的重复代码。

TableView封装心得 去掉TableView中委托里麻烦的重复代码。手写继承NSObject类 此类实现TableView的DataSource和Delegate的协议。自己写个协议,在C中去进行委托传值。整体思路,就是把TableView的委托以拉链的形式,在管理类中实现。

.мνρ謎︱ing.瀡緣坣 1365

IOS Xcode5 IB tableView 简单使用

在Xcode5上使用IB界面做tableView的简单的使用。

闪亮的的Bug写手 996
上一篇: (作业)使用autolayout和约束实现简单的界面布局
下一篇: Swift4.0 Codable踩坑之派生类数据的保存
麻豆_matou
博客等级 码龄10年 41粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值