UITableView--实现左右TableView滑动联动


前言

对UITable View的学习,并使用两个TableView来制作外卖App的点单页面


一、UITableView整体布局

1.拖入两个Table View并对其进行约束(bottom的约束是对根视图)

UITableView整体布局

2.对Cell定义identifier

identifier
定义对应的常量

let kCategoryCellID = "CategoryCellID" //分类
let kMenuCellID = "MenuCellID"        //菜单

3.TableView的IBOutlet

@IBOutlet weak var categoryTableView: UITableView!
@IBOutlet weak var MenuTableview: UITableView!

4.data Source和delegate

dataSource和delegate

二、分类和菜单的数据

1.Menu

class Menu{
    var menuImageName:String
    var menuName:String
    var price:Double
    
    init(menuImageName:String,menuName:String,price :Double){
        self.menuImageName = menuImageName
        self.menuName = menuName
        self.price = price
    }
}

2.分类为一维数组,菜单为二维数组

var categories:[String] = []
var menus:[[Menu]] = []
for i in 1...20{
    categories.append("分类\(i)")
}

for category in categories{
    var menusPerCategory: [Menu] = []
    for i in 1...3{
         let menu = Menu(menuImageName: "food", menuName: "\(category) - 外卖菜品\(i)", price: Double(i))
        menusPerCategory.append(menu)
    }
    menus.append(menusPerCategory)
}

三、配置tableView的section和row以及cell的UI

代码如下(示例):

//判断tableView的位置
func numberOfSections(in tableView: UITableView) -> Int {
    tableView == categoryTableView ? 1 : categories.count
}

//每段rows中的数量
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    tableView == categoryTableView ? categories.count : menus[section].count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if tableView == categoryTableView{
        //强制转换为CategoryCell
        let cell = tableView.dequeueReusableCell(withIdentifier: kCategoryCellID,for: indexPath) as! CategoryCell
        cell.categoryLabel.text = categories[indexPath.row]
        return cell
    }else{
        let cell = tableView.dequeueReusableCell(withIdentifier: kMenuCellID,for: indexPath) as! MenuCell
        cell.menu = menus[indexPath.section][indexPath.row]
        return cell
    }
}

对应的单元格布局

cell的UI布局


四、tableViewCell里的内容

1.MenuCell

import UIKit

class MenuCell: UITableViewCell {

    @IBOutlet weak var menuImageView: UIImageView!
    @IBOutlet weak var menuNameLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
    
    //在外部对menu的属性赋值,即触发Menu中didSet方法
    var menu:Menu?{
        didSet{
            //可选形
            guard let menu = menu else {
                return
            }
            menuImageView.image = UIImage(named: menu.menuImageName)
            menuNameLabel.text = menu.menuName
            priceLabel.text = \(menu.price)" //double的price转换为string
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

}

2.CategoryCell

设置cell被选中后的样式

import UIKit

class CategoryCell: UITableViewCell {

    @IBOutlet weak var categoryLabel: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    //选中cell后的样式
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
        contentView.backgroundColor = selected ? .systemBackground : .clear
        categoryLabel.font = selected ? .boldSystemFont(ofSize: 15) : .systemFont(ofSize: 15)
        categoryLabel.textColor = selected ? .label : .secondaryLabel
    }

}

初始被选中cell

//默认分类1为选中状态
categoryTableView.selectRow(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .none)

五、tableView的sectionHeader+xib

1.xib

xib不同于storyboard,是用于制作UIView
设置Header View和Footer View的背景色(要设置为Default)应在其中添加background View(UIView)
Section Header

import UIKit

class CategoryHeader: UITableViewHeaderFooterView {

    @IBOutlet weak var categoryNameLabel: UILabel!//必须选到对应的Header
}

2.配置sectionHeader的内容

1.为xib中的Section Header注册

let kCategoryHeaderNibName = "CategoryHeader"
let kCategoryHeaderID = "CategoryHeaderID"

//对xib中cell属性注册
MenuTableview.register(UINib(nibName: kCategoryHeaderNibName, bundle: nil), forHeaderFooterViewReuseIdentifier: kCategoryHeaderID)

extension ViewController:UITableViewDelegate{
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        if tableView == categoryTableView{
            return nil
        }
        let categoryHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: kCategoryHeaderID) as! CategoryHeader
        categoryHeader.categoryNameLabel.text = categories[section]
        return categoryHeader
    }
    
    //设置sectionHeight且优先级最高
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        tableView == categoryTableView ? 0:30
    }

2.点击左侧tableViewCell使右边tableView联动

// MARK: 选择左边cell时右边的联动
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if tableView == categoryTableView{
        //右侧tableView自动滚动到对应的分类菜单
        menuTableView.scrollToRow(at: IndexPath(row: 0, section: indexPath.row), at: .top, animated: true)
        //左侧tableView将该cell滚动到顶部
        categoryTableView.scrollToRow(at: indexPath, at: .top, animated: true)
    }
}

3.右tableView向上滚动导致header出现时联动左tableView

//向上
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    if tableView == MenuTableview && !menuTableViewGoDown && (MenuTableview.isDragging || MenuTableview.isDecelerating){
        categoryTableView.selectRow(at: IndexPath(row: section, section: 0), animated: true, scrollPosition: .top)
    }
}

4.判断用户正在上滑还是下滑

//判断向上滑动还是向下滑动
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let tableView = scrollView  as! UITableView
    if tableView == MenuTableview{
        //判断是否向下移动
        menuTableViewGoDown = menuTableViewCurrentContentOffsetY < tableView.contentOffset.y
        menuTableViewCurrentContentOffsetY = tableView.contentOffset.y
    }
}

5.右tableView向下滚动导致header消失时联动左tableView

 //向下
    func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
        if tableView == MenuTableview && menuTableViewGoDown && (MenuTableview.isDragging || MenuTableview.isDecelerating){
            categoryTableView.selectRow(at: IndexPath(row: section + 1, section: 0), animated: true, scrollPosition: .top)
        }
    }
}

六、ViewController

import UIKit

let kCategoryCellID = "CategoryCellID"
let kMenuCellID = "MenuCellID"
let kCategoryHeaderNibName = "CategoryHeader"
let kCategoryHeaderID = "CategoryHeaderID"

class ViewController: UIViewController {

    @IBOutlet weak var categoryTableView: UITableView!
    @IBOutlet weak var menuTableView: UITableView!
    
    var categories: [String] = []
    var menus: [[Menu]] = []
    
    var menuTableViewGoDown = true
    
    var menuTableViewCurrentContentOffsetY: CGFloat = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //注册header
        menuTableView.register(UINib(nibName: kCategoryHeaderNibName, bundle: nil), forHeaderFooterViewReuseIdentifier: kCategoryHeaderID)
        
        //配置演示数据
        for i in 1...20{
            categories.append("分类\(i)")
        }
        for category in categories{
            var menusPerCategory: [Menu] = []
            for i in 1...3{
                let menu = Menu(menuImageName: "food", menuName: "\(category) - 外卖菜品\(i)", price: Double(i))
                menusPerCategory.append(menu)
            }
            menus.append(menusPerCategory)
        }
        
        //默认让第一个cell是选中状态
        categoryTableView.selectRow(at: IndexPath(row: 0, section: 0), animated: true, scrollPosition: .none)
    }
}


extension ViewController: UITableViewDataSource{
    func numberOfSections(in tableView: UITableView) -> Int {
        tableView == categoryTableView ? 1 : categories.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        tableView == categoryTableView ? categories.count : menus[section].count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if tableView == categoryTableView{
            let cell = tableView.dequeueReusableCell(withIdentifier: kCategoryCellID, for: indexPath) as! CategoryCell
            cell.categoryLabel.text = categories[indexPath.row]
            return cell
        }else{
            let cell = tableView.dequeueReusableCell(withIdentifier: kMenuCellID, for: indexPath) as! MenuCell
            cell.menu = menus[indexPath.section][indexPath.row]
            return cell
        }
    }
}

extension ViewController: UITableViewDelegate{
    // MARK: 配置右边header
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        if tableView == categoryTableView{
            return nil
        }
        
        let categoryHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: kCategoryHeaderID) as! CategoryHeader
        categoryHeader.categoryNameLabel.text = categories[section]
        return categoryHeader
    }
    //尽管上面返回nil,但仍旧会出现一个无title的header(bug),需指定高度为0才能完全消失
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        tableView == categoryTableView ? 0 : 30
    }
    
    // MARK: 选择左边cell时右边的联动
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if tableView == categoryTableView{
            //右侧tableView自动滚动到对应的分类菜单
            menuTableView.scrollToRow(at: IndexPath(row: 0, section: indexPath.row), at: .top, animated: true)
            //左侧tableView将该cell滚动到顶部
            categoryTableView.scrollToRow(at: indexPath, at: .top, animated: true)
        }
    }
    
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let tableView = scrollView as! UITableView
        if tableView == menuTableView{
            menuTableViewGoDown = menuTableViewCurrentContentOffsetY < tableView.contentOffset.y
            menuTableViewCurrentContentOffsetY = tableView.contentOffset.y
        }
    }
    
    func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
        if tableView == menuTableView && !menuTableViewGoDown && (menuTableView.isDragging || menuTableView.isDecelerating){
            categoryTableView.selectRow(at: IndexPath(row: section, section: 0), animated: true, scrollPosition: .top)
        }
    }
    func tableView(_ tableView: UITableView, didEndDisplayingHeaderView view: UIView, forSection section: Int) {
        if tableView == menuTableView && menuTableViewGoDown && (menuTableView.isDragging || menuTableView.isDecelerating){
            categoryTableView.selectRow(at: IndexPath(row: section + 1, section: 0), animated: true, scrollPosition: .top)
        }
    }
}


总结

本次是对TableView的学习,对cell的注册以及dataSource和delegate方法有了新的了解;初次接触到Section Header,对其的初始化以及应用上要多加练习,也可以在footer Header上运用相似的Section Header方向进行练习;最后加强TableView练习,灵活运用到实际开发中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值