☑️ 오늘 한 작업
- ScrollView 적용
- Pull to Refresh로 RemoteProduct 가져오기
- UI 수정
- 전체적인 디자인 / brand 추가
- 테이블 뷰 image, brand 추가
- 추가 구현 기능들
- AlertAction & 화면 전환
- 테이블 뷰 셀 버튼으로 삭제
- 중복된 상품은 코어데이터에 다시 추가되지 않도록 구현
어제부터 잡고 있던 ScrollView 공부를 더 해보다가 위시리스트 앱에 적용했다.
최종 구현단계까지 마무리하니 계속 수정하고 싶은 부분들이 생겨 자잘한 기능들을 많이 추가하게 됐다. 과제를 더 일찍 시작할 걸 후회되는 시간이었습니다..🥲
Pull to Refresh 구현
ScrollView 적용하기
스크롤 뷰의 핵심은 높이가 정해져 있어야 된다는 점!!⭐️
ContentView 안에 있는 요소들의 높이를 꼭 정하고 Bottom constraints까지 정해줘야 하는 게 핵심이다.
RefreshControl()
// MARK: - Pull to Refresh
func setRefreshControl() {
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(refreshFire), for: .valueChanged)
self.scrollView.refreshControl = refreshControl
}
@objc func refreshFire() {
fetchRemoteProduct()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.scrollView.refreshControl?.endRefreshing()
}
}
refresh가 발생하면 fetchRemoteProduct() 메서드가 호출된다. 그런 다음 0.5초 후에 스크롤 뷰의 리프레시 컨트롤이 종료된다.
따라서 스크롤 뷰를 당겨서 새로 고침하면 새로운 원격 제품을 가져와서 UI가 업데이트 되도록 구현했다.
UI 수정
버튼 및 UI를 조금 더 신경 써서 수정했다.
브랜드 라벨도 추가했고 네비게이션 상단에 위시리스트 확인 버튼도 하나 더 추가했다.
struct RemoteProduct: Decodable {
let id: Int
let title: String
let description: String
let price: Double
let thumbnail: URL
let brand: String
}
브랜드를 표시하기 위해 데이터 구조도 수정이 필요했다.
기존 방식대로 데이터 받아올 때 brand도 추가해주기!
위시리스트의 셀 디자인도 수정했다!
이미지를 볼 수 있도록 추가했고 버튼으로 삭제해보는 기능도 도전해보고 싶어서 삭제 버튼도 추가했다.
추가 구현 기능
AlertAction & 화면 전환
/ MARK: - 위시 리스트 담기 Btn
@IBAction func tappedSaveProductButton(_ sender: UIButton) {
guard let product = currentProduct else { return }
print("위시리스트 담는 상품: \(product)")
let alert = UIAlertController(title: "WISH LIST", message: "상품을 위시리스트에 추가하시겠습니까?", preferredStyle: .alert)
let addAction = UIAlertAction(title: "예", style: .default) { [weak self] _ in
CoreDataManager.saveWishProduct(product: product) { success in
if success {
print("상품이 위시 리스트에 추가되었습니다.")
guard let nextVC = self?.storyboard?.instantiateViewController(withIdentifier: "WishListViewController") else { return }
self?.modalPresentationStyle = .fullScreen
self?.present(nextVC, animated: true)
} else {
print("상품을 위시 리스트에 추가하는 데 실패했습니다.")
}
}
}
let cancelAction = UIAlertAction(title: "아니오", style: .default)
alert.addAction(cancelAction)
alert.addAction(addAction)
self.present(alert, animated: true)
}
위시리스트에 담을 때 얼럿으로 한 번 더 확인할 수 있도록 수정했다.
또한 위시리스트에 추가되었을 때 모달 형식으로 상품이 추가된 위시리스트를 확인할 수 있다.
테이블 뷰 셀 버튼으로 삭제
기존에 있던 스와이프로 삭제 외 버튼으로 삭제하는 기능을 구현해보자.
var deleteHandler: (() -> Void)?
@IBAction func tappedDeleteButton(_ sender: UIButton) {
deleteHandler?()
}
버튼으로 셀을 삭제하기 위해서는 핸들러 사용이 필요하다.
버튼을 눌렀을 때 핸들러를 호출해서 사용하자.
// MARK: - cell 버튼 삭제
cell.deleteHandler = { [weak self] in
guard let self = self else { return }
// 사용자에게 확인 메시지를 표시하는 알림 창 생성
let alert = UIAlertController(title: "WISH LIST", message: "상품을 위시리스트에서 삭제하시겠습니까?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "예", style: .default) { _ in
let selectedProduct = self.wishList[indexPath.row]
let productId = selectedProduct.id
CoreDataManager.deleteProduct(withId: productId) { success in
if success {
print("상품 삭제 성공")
self.loadWishList()
} else {
print("상품 삭제 실패")
}
}
}
let cancelAction = UIAlertAction(title: "아니오", style: .default)
alert.addAction(cancelAction)
alert.addAction(deleteAction)
self.present(alert, animated: true)
}
핸들러를 다음과 같이 사용해서 버튼으로 셀을 삭제하도록 구현했다.
CoreData 중복 저장 방지
// MARK: - SAVE: CoreData에 상품 저장
static func saveWishProduct(product: RemoteProduct, completion: @escaping (Bool) -> Void) {
guard let context = CoreDataManager.context else {
completion(false)
return
}
let fetchRequest = NSFetchRequest<Product>(entityName: "Product")
fetchRequest.predicate = NSPredicate(format: "id == %lld", product.id)
do {
let products = try context.fetch(fetchRequest)
if let existingProduct = products.first {
print("기존 데이터와 동일")
} else {
// 기존 데이터가 없으면 새로 추가
let wishProduct = Product(context: context)
wishProduct.id = Int64(product.id)
wishProduct.title = product.title
wishProduct.price = product.price
wishProduct.brand = product.brand
}
try context.save()
completion(true)
} catch {
print("Error saving or updating product: \(error.localizedDescription)")
completion(false)
}
}
중복된 상품은 기존 id와 비교하여 코어데이터에 다시 추가되지 않도록 구현했다.
📺 최종 구현 화면
아이 뿌듯해라 👩🏻💻
아래 깃허브 링크 첨부합니다!
https://github.com/yyujnn/WishList
'TIL✏️' 카테고리의 다른 글
[iOS] 책 검색 & 저장 App (1) - Kakao REST API 사용해서 책 검색하기 (4) | 2024.05.07 |
---|---|
[iOS] UICollectionView: Header(헤더) 사용하기 (1) | 2024.04.23 |
[iOS] WishList App - CoreData 사용하기 (2) | 2024.04.17 |
[iOS] WishList App - URLSession 으로 API 연결하기 (2) | 2024.04.16 |
[iOS] 키오스크 앱 프로젝트 - 주문 내역 화면 (1) | 2024.04.03 |