🩵 OrderListView
✔️ Properties
- orderList: 주문 목록을 나타내는 SpabucksOrderItem 배열
✔️ UI Properties
- orderListTable: 주문 목록을 표시하는 UITableView
- countLabel: 주문 항목의 총 개수를 표시하는 UILabel
- priceTitleLabel: "총 주문 가격"이라는 내용을 표시하는 UILabel
- priceLabel: 총 주문 가격을 표시하는 UILabel
- cancelButton, paymentButton, callEmployeeButton: 주문 취소, 결제 및 직원 호출과 관련된 작업을 수행하는 ColorButton 인스턴스
✔️ Life Cycle
- init(frame:): 뷰 초기화 + setUI() 메서드를 호출 = UI 설정
- setUI(): 주문 수 및 주문 가격 정보, 테이블 뷰 및 결제 버튼과 관련된 스택 뷰를 포함하여 전반적인 UI 설정
✔️ Data Setting
- setOrderItem(_:): 주문 목록에 메뉴 아이템을 추가하거나 중복된 아이템이면 수량을 증가시키는 메서드
- updateOrderListTable(): 주테이블 뷰를 업데이트하고 주문 정보를 갱신하는 메서드
✔️ MenuDataDelegate
- didSelectMenuItem(_:): 메뉴 항목이 선택될 때 호출되는 메서드
✔️ Extensions
- createOrderPriceInfo(): 주문 개수, 가격 제목 및 총 가격을 표시하는 스택 뷰 생성
- createPaymentBtn(): 주문 관리 및 결제에 관련된 버튼을 표시하는 스택 뷰 생성
- createTableView(): 주문 목록 테이블 구성
✔️ UITableViewDataSource, UITableViewDelegate
- setTotalOrderInfo(): 주문의 총 개수 및 총 가격을 계산하여 화면에 표시
- tableView(_:numberOfRowsInSection:): 테이블 뷰의 행 수를 반환
- tableView(_:cellForRowAt:): 지정된 행에 대한 셀을 구성하고 반환
- updateOrderCount(at:delta:): 특정 행의 주문 수를 업데이트하고 해당하는 셀 리로드
- deleteOrder(at:): 지정된 인덱스 경로의 주문을 삭제하고 총 주문 정보를 업데이트. 마지막 항목이 삭제되면 전체 주문 목록 삭제
🩵 OrderListTableViewCell
✔️ Properties
- identifier: 셀의 재사용을 위한 식별자로 사용되는 문자열 상수
- onMinusButton, onPlusButton, onDeleteButton: 각각 마이너스(-), 플러스(+), 삭제(X) 버튼이 탭됐을 때 실행될 클로저를 저장하는 프로퍼티
✔️ UI Properties
- itemImageView: 주문된 항목의 이미지를 표시하는 UIImageView.
- itemNameLabel: 주문된 항목의 이름을 표시하는 UILabel.
- itemPriceLabel: 주문된 항목의 가격을 표시하는 UILabel.
- quantityLabel: 주문된 항목의 수량을 표시하는 UILabel.
- minusButton, plusButton, deleteButton: 수량을 감소시키는 마이너스(-) 버튼, 수량을 증가시키는 플러스(+) 버튼, 그리고 항목을 삭제하는 삭제(X) 버튼입니다.
✔️ Life Cycle & init
- init(style:reuseIdentifier:): 셀의 초기화를 담당하는 이니셜라이저. setUI() 메서드를 호출하여 UI 설정
- setUI(): 셀의 사용자 인터페이스를 설정하는 메서드. 이미지 뷰, 레이블, 버튼 등의 요소를 추가 + Auto Layout
- prepareForReuse(): 셀이 재사용될 때 호출. 이미지, 레이블 등의 속성을 초기화
✔️ Button Action Method
- tapMinusButton(): 마이너스(-) 버튼이 탭됐을 때 실행되는 메서드. 등록된 클로저를 호출
- tapPlusButton(): 플러스(+) 버튼이 탭됐을 때 실행되는 메서드. 등록된 클로저를 호출
- tapDeleteButton(): 삭제(X) 버튼이 탭됐을 때 실행되는 메서드. 등록된 클로저를 호출
✔️ Auto Layout Setting
- NSLayoutConstraint: UI 요소들 간의 상대적인 위치와 크기를 설정
❗메모리 누수 방지를 위한 weak 참조 사용
extension OrderListView: UITableViewDataSource, UITableViewDelegate {
func setTotalOrderInfo() {
let orderListCount = orderList.count
var totalPrice: Double = 0
var totalCount: Int = 0
for i in 0 ..< orderListCount {
totalPrice += orderList[i].menuItem.price * Double(orderList[i].orderCount)
totalCount += orderList[i].orderCount
}
countLabel.text = "\(totalCount) 개"
priceLabel.text = "\(totalPrice.formattedString())원"
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return orderList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: OrderListTableViewCell.identifier, for: indexPath) as! OrderListTableViewCell
cell.selectionStyle = .none
cell.onMinusButton = { [weak self] in
self?.updateOrderCount(at: indexPath, delta: -1)
}
cell.onPlusButton = { [weak self] in
self?.updateOrderCount(at: indexPath, delta: 1)
}
cell.onDeleteButton = { [weak self] in
self?.deleteOrder(at: indexPath)
}
cell.itemImageView.image = UIImage(named: orderList[indexPath.row].menuItem.imageName)
cell.itemNameLabel.text = orderList[indexPath.row].menuItem.name
cell.itemPriceLabel.text = "\((self.orderList[indexPath.row].menuItem.price * Double(self.orderList[indexPath.row].orderCount)).formattedString()) 원"
cell.quantityLabel.text = String(self.orderList[indexPath.row].orderCount)
return cell
}
private func updateOrderCount(at indexPath: IndexPath, delta: Int) {
guard indexPath.row < self.orderList.count else { return }
let newCount = self.orderList[indexPath.row].orderCount + delta
if newCount > 0 {
self.orderList[indexPath.row].orderCount = newCount
self.orderListTable.reloadRows(at: [indexPath], with: .automatic)
self.setTotalOrderInfo()
}
}
private func deleteOrder(at indexPath: IndexPath) {
if indexPath.row < self.orderList.count {
self.orderList.remove(at: indexPath.row)
self.orderListTable.deleteRows(at: [indexPath], with: .automatic)
self.setTotalOrderInfo()
} else {
self.orderList.removeAll()
self.orderListTable.reloadData()
self.setTotalOrderInfo()
}
}
}
❗주문목록 중복 체크를 위한 필터링
발생에러
Referencing operator function '==' on 'Equatable' requires that 'SpabucksOrderItem' conform to 'Equatable'
데이터 타입이 구조체라서 명시적으로 Equatable을 채택하지 않아서 발생한 문제 인 듯.
구조체 자체를 비교하려 했었음.
어차피 아이디 비교 형태를 바꾸려고 했기 때문에 문자열로 새로운 아이디를 만들어 동등성 검사를 진행.
해결되었음.
private func setOrderItem(_ item: SpabucksMenuItem) {
var duplicateCheck = false
var duplicateIndex: Int?
for i in 0 ..< orderList.count {
if(orderList[i].menuItem.name == item.name) {
duplicateIndex = orderList.indices.filter({"\(orderList[$0].menuItem.name)-\(orderList[$0].menuItem.id)" == "\(orderList[i].menuItem.name)-\(orderList[i].menuItem.id)"}).first
duplicateCheck = true
break
}
}
if duplicateCheck == true {
orderList[duplicateIndex!].orderCount += 1
orderListTable.reloadRows(at: [IndexPath(row: duplicateIndex!, section: 0)], with: .automatic)
setTotalOrderInfo()
} else {
let orderItemData = SpabucksOrderItem(menuItem: item)
orderList.append(orderItemData)
updateOrderListTable()
}
}
❗선택한 주문 상품이 0개
- 버튼을 사용하여 주문 상품 갯수를 0개 이하로 만들 수 있는 이슈가 발생.
주문목록에 들어있는 아이템 갯수를 1이하로 만들 수 없도록 조치.
private func updateOrderCount(at indexPath: IndexPath, delta: Int) {
var customIndexPath = IndexPath(row: self.orderList.count-1, section: 0)
if indexPath.row < orderList.count {
customIndexPath = indexPath
}
var orderItem = orderList[customIndexPath.row]
orderItem.orderCount += delta
if orderItem.orderCount > 0 {
orderList[customIndexPath.row] = orderItem
setTotalOrderInfo()
orderListTable.reloadRows(at: [customIndexPath], with: .none)
}
}
❗주문목록 Cell 삭제 이슈
발생에러
Index out of range
여러개의 cell을 담아놓고 하나씩 지울 때, 마지막 cell을 delete 할 시 에러 발생.
배열의 유효하지 않은 인덱스에 접근하려고 할 때 발생하는 에러임.
따라서 주문목록 카운트가 1이라면 0번 인덱스를 삭제하는 것으로 해결.
private func deleteOrder(at indexPath: IndexPath) {
if orderList.count == 1 {
orderList.removeAll()
orderListTable.deleteRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
} else {
orderList.remove(at: indexPath.row)
orderListTable.deleteRows(at: [indexPath], with: .automatic)
}
setTotalOrderInfo()
}
❗Delegate Pattern
메뉴리스트에서 주문목록으로 데이터를 보내기 위해서 사용
UIView 끼리의 작업이기 때문에 반드시 ViewController를 거쳐야만 했음.
MenuView에서 OrderView 인스턴스를 만들어서 사용하면 새로운 인스턴스가 생성되기 때문에
ViewController에서 공용의 인스턴스가 생성되어 있는 것을 사용, MenuView의 Delegate를 OrderView로 설정.
// ViewController
private func setDelegate() {
menuView.delegate = orderView
}
// MenuView
weak var delegate: MenuDataDelegate?
extension MenuView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedData = dataSource[indexPath.row] // 선택된 셀의 데이터
delegate?.didSelectMenuItem(selectedData)
collectionView.deselectItem(at: indexPath, animated: true)
}
}
// OrderListView
class OrderListView: UIView, MenuDataDelegate {
func didSelectMenuItem(_ item: SpabucksMenuItem) {
setOrderItem(item)
}
}
'내일배움캠프 iOS 2기' 카테고리의 다른 글
[트러블슈팅] 앱개발심화(개인) - 이미지 로딩 및 재사용 문제 해결 (2) | 2024.01.31 |
---|---|
[NBCAMP] 앱 개발 입문 : To do list (0) | 2023.12.20 |