文章目录
前言
对UITable View的学习,并使用两个TableView来制作外卖App的点单页面
一、UITableView整体布局
1.拖入两个Table View并对其进行约束(bottom的约束是对根视图)
2.对Cell定义identifier
定义对应的常量
let kCategoryCellID = "CategoryCellID" //分类
let kMenuCellID = "MenuCellID" //菜单
3.TableView的IBOutlet
@IBOutlet weak var categoryTableView: UITableView!
@IBOutlet weak var MenuTableview: UITableView!
4.data Source和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
}
}
对应的单元格布局
四、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)
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练习,灵活运用到实际开发中。