#25–6 期末< 6>好味小姐罐頭訂購APP-TableView 的Cell裡裝一個Table View
附上完成圖:
我希望在同一個類別裡面的商品可以出現在一起,要做到這程度就要考慮到的技術有:
- 自動調整cell的高度 (因為你要在同一類別裡新增一個新的產品)
- cell裡面的值要變化,使用delegate來做變化
- TableView + TableView製作。
或是 TableView + CollectionView製作。
或是 StackView製作
在這裡我使用TableView + TableView製作,高度調整及cell值變化都是使用delegate。
其實還是有些bug要處理,還不夠完美,但還是把現狀發上來吧~
資料來源:
使用單例模式來確保應用程序中只有一個實例,並提供全局訪問點來共享數據~
class SharedData {
// 靜態常量,用於存儲 SharedData 的唯一實例
static let shared = SharedData()
// 存儲 TitleOrderData 的數組
var titleOrders: [TitleOrderData] = []
// 存儲 DetailOrderData 的數組
var detailOrders: [DetailOrderData] = []
// 存儲 CategoryName 的數組
var categoryTitles: [CategoryName] = []
// 用於緩存數據的字典
var cache: [String: [Record]] = [:]
// 私有的初始化方法,防止外部創建新的實例
private init() {}
}
struct TitleOrderData {
var title: String? // 訂單標題
var titleImageUrl: URL? // 標題圖片的 URL
var detailOrderData: [DetailOrderData] // 包含的詳細訂單數據
}
struct DetailOrderData {
var name: String? // 訂單項目的名稱
var detailPrice: String? // 詳細價格
var price: String? // 價格
var orderQty: String? // 訂單數量
var imageUrl: URL? // 項目圖片的 URL
var sectionIndex: Int? // 所在的 section 索引
var rowIndex: Int? // 所在的 row 索引
}
TableView + TableView:
他麻煩點有兩個:
第一個是因為你有兩個TableView,Xcode不知道要顯示哪一個,沒有設定內容還是不會顯示出來~
第二個是numberOfSections & numberOfRowsInSection
究竟這兩的數量要怎麼確認。
關於第一個,因為有兩個TableView,我們可以把外面的TableView拉一個Outlet來做辨別。
關於資料:
numberOfSections
func numberOfSections(in tableView: UITableView) -> Int {
if tableView == mainTableView {
// 外面的TableView 的 section 數量來自 titleOrders
return SharedData.shared.titleOrders.count
} else {
// 裡面的TableView 只有一個 section
return 1
}
}
numberOfRowsInSection
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == mainTableView {
// 主 TableView 每個 section 只有一個 row,因為每個 cell 會包含一個子 TableView
return 1
} else {
// 子 TableView 的 row 數量來自 detailOrderData
let sectionIndex = tableView.tag
guard sectionIndex < SharedData.shared.titleOrders.count else {
print("無效的 section 索引: \(sectionIndex)")
return 0
}
return SharedData.shared.titleOrders[sectionIndex].detailOrderData.count
}
}
cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == mainTableView {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "OrderOfTitleViewTableViewCell", for: indexPath) as? OrderOfTitleViewTableViewCell else {
return UITableViewCell()
}
let titleOrder = SharedData.shared.titleOrders[indexPath.section]
if let imageUrl = titleOrder.titleImageUrl {
cell.titleImage.kf.setImage(with: imageUrl)
}
cell.titleLabel.text = titleOrder.title
cell.changeHeightDelegate = self
cell.subTableView.tag = indexPath.section
cell.subTableView.reloadData()
return cell
} else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "OrderOfSubViewTableViewCell", for: indexPath) as? OrderOfSubViewTableViewCell else {
return UITableViewCell()
}
// 確保 tableView.tag 有效
guard tableView.tag < SharedData.shared.titleOrders.count else {
print("無效的 tableView.tag: \(tableView.tag)")
return UITableViewCell()
}
let detailOrderData = SharedData.shared.titleOrders[tableView.tag].detailOrderData
// 確保 indexPath.row 有效
guard indexPath.row < detailOrderData.count else {
print("Section of \(tableView.tag) 的無效行索引: \(indexPath.row)")
return UITableViewCell()
}
let detailOrder = detailOrderData[indexPath.row]
if let imageUrl = detailOrder.imageUrl {
cell.productImageView.kf.setImage(with: imageUrl)
}
cell.nameLabel.text = detailOrder.name
cell.packageLabel.text = detailOrder.detailPrice
cell.priceLabel.text = "\(detailOrder.price ?? "")"
cell.countLabel.text = detailOrder.orderQty
cell.detailData = DetailOrderData(
name: detailOrder.name,
detailPrice: detailOrder.detailPrice,
price: detailOrder.price,
orderQty: detailOrder.orderQty,
imageUrl: detailOrder.imageUrl,
sectionIndex: tableView.tag,
rowIndex: indexPath.row
)
cell.changeHeightDelegate = self
cell.alertDelegate = self // 設置 alertDelegate
cell.changeQtyDelegate = self
return cell
}
}
willDisplay
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
tableView.invalidateIntrinsicContentSize()
tableView.layoutIfNeeded()
updateTotalPrice()
}
關於自動調整高度:
因為兩個TableView都要自動調高,所以記得要在這兩個TableView加上
var changeHeightDelegate: ChangeHeightDelegate?
並且在override func awakeFromNib() {} 加上.layoutIfNeeded()
protocol ChangeHeightDelegate {
func heightChanged (index: Int)
}
extension OrderViewController: ChangeHeightDelegate {
func heightChanged (index: Int) {
mainTableView.performBatchUpdates(nil)
}
}
extension UITableView {
public override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return contentSize
}
public override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
}
最後記得在
cellForRowAt裡面加上cell.changeHeightDelegate = self
func viewWillAppear()、 viewDidAppear()、
tableview func的willDisplay裡面
加上tableView.layoutIfNeeded()
大概就這樣吧~☕☕☕