(作业)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

纯相位全息图优化算法综述

除此之外,还有许多用于减少斑点噪声的非迭代方法,例如:采用下采样掩膜的采样纯相位全息图(Sampled-Phase-only Hologram)方法、模式化相位掩膜(Patterned Phase-Only Hologram)方法、双相位方法(Double-Phase Method),以及使用非随机相位掩膜的方法(Random Phase-Free Method)等。迭代算法通常由一个对目标全息图的近似出发,经过一系列的重复操作不断优化这个近似全息图,直到该近似所得到的重建图像满足一定的误差要求;

ZhongKeWeiXing的博客 1428

SwiftUI创建TableView(1)

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

旭日的博客 1227

BGE-Reranker-v2-M3介绍

重排序模型介绍

吉米_王 5893

AP发票批量付款程序(WEB ADI)

CREATE OR REPLACE PACKAGE MEW_AP_BATCH_PAYMENTS_PKG IS   PROCEDURE MAIN(P_BATCH_ID          NUMBER                 ,P_ORG_ID            NUMBER                 ,P_BANK_ACCOUNT_NAME VARCHAR2

储藏室 5051

Swift TableView的使用

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

长沙火山 1742

学习笔记( 11 ) - 解决:finalshell连接虚拟机(ubuntu)超时,主机ping不通虚拟机,主机没有VMnet8网卡, Connection refused: connect

解决:finalshell连接虚拟机(ubuntu)超时,主机ping不通虚拟机,主机没有VMnet8网卡, Connection refused: connect

烟溪彭于晏的博客 2万+

iOS tableview的delegate和datasource执行顺序

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

Tom_woo_y的博客 1029

还在用 RaiDrive/Rclone 挂载网盘?教您使用Windows 原生 WebDAV 丝滑直连 AList/Openlist,让你的网盘秒变本地硬盘!

网盘文件分散管理?😩 别再依赖第三方工具了!本文教你如何在 Windows 下,利用原生 WebDAV 功能,将 Openlist 网盘直接映射为本地硬盘!💻 操作简单,告别繁琐,让你的云端文件像本地文件一样丝滑!🚀 轻松实现高效管理,快来体验吧!

XiaoqiangClub的博客 2974

Archery通过OpenResty代理转发后登录一直提示Forbidden

测试几遍,连账号密码都没用验证,因为不管输什么都是提示Forbidden。找到配置文件settings.py,下面命令可到配置文件路径。回到页面刷新,随便输入个账号密码,验证失败证明请求成功了。查看Preview,提示CSRF验证失败,请求被中断。域名:dba.hkeasyspeed.com。打开开发者工具查看,网络里面可以看到报403。添加一条配置,把你配置域名添加进去。可以看到页面可以正常访问到。进入Archery容器。重启Archery容器。正常输入账号密码,OK。

万物皆可学 3147

UITableview delegate dataSource调用探究

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

abc0432136的博客 376

MATLAB基础应用精讲-【数模应用】PPO算法(附python代码实现)(二)

表示为状态转移概率和动作选择概率的乘积,因为状态和动作决定轨迹。

qq_36130719的博客 413

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

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

Linux2060的博客 264

人工蜂群优化算法(ABC)详解:原理、实现与应用

人工蜂群算法(Artificial Bee Colony, ABC)是Karaboga于2005年提出的一种群体智能优化算法,其灵感来源于蜜蜂群体在自然界中高效的采蜜行为。该算法通过模拟雇佣蜂、观察蜂和侦察蜂三种角色的分工协作来寻找最优解。在ABC算法中,每个潜在的解决方案被视为一个“食物源”,其质量由适应度函数评估。雇佣蜂负责在已知优质食物源附近进行局部开采;观察蜂根据雇佣蜂分享的信息,以一定概率选择并跟进优质食物源;侦察蜂则随机探索新区域,防止种群陷入局部最优。

qq_42212808的博客 393

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

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

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

1874

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

UIViewController中使用dataSource与delegate

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

chuichuanqin0793的博客 235

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

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、付费专栏及课程。

余额充值