apple/iOS, UIKit, Documentation

[iOS] UICollectionView에 대해서 알아보기 5편 (북마크 구현 및 모델 관리)

lgvv 2022. 9. 1. 00:46

UICollectionView에 대해서 알아보기 5편 (북마크 구현 및 모델 관리)

 

이번에는 bookmark 기능을 구현해 볼 예정이다.

이전에도 북마크를 구현했었고, 모델을 잘 만들어서 관리하고 있었다. 

그러니까 기록용의 의미가 더 큼!

AppleCollectionView.zip
2.67MB

UI

 

북마크 구현

 

 

우선 모델을 변경했다.

서버에서 내려오는 데이터는 id, name, avatar, job, age뿐이지만, isBookmark를 추가해서 북마크 여부를 나타내는 프로퍼티를 만들었다.

Equtable을 채택해서 멤버의 구조체의 5가지 정보가 다 같으면 같은 객체로 판단하게 했다.

여기 MemberAPI가 Member에 해당하는 조합을 일정하게 주는게 아니라, id, name, avatar, job, age를 랜덤하게 조합해서 주는것 같았다.

id는 고유값이 아니라 배열의 index 정보라서 늘 업데이트 되어서 결국은 같은 객체를 얻는게 불가능해서 이는 리액터킷을 적용할 때, 다른 예제를 갖고오도록 하겠다.

//
//  RestaurantList.swift
//  AppleCollectionView
//
//  Created by Hamlit Jason on 2022/08/23.
//

import UIKit

struct Member: Codable, Hashable, Equatable {
    
    let id: Int
    let name: String
    let avatar: String
    let job: String
    let age: Int
    
    var isBookmark: Bool {
        isBookmark(member: Member(id: id, name: name, avatar: avatar, job: job, age: age))
    }
    
    /// 아이디 값이 같으면 같은 객체로 판정
    static func ==(lhs: Member, rhs: Member) -> Bool {
        return lhs.id == rhs.id &&
        lhs.name == rhs.name &&
        lhs.avatar == rhs.avatar &&
        lhs.job == rhs.job &&
        lhs.age == rhs.age
    }
}

extension Member {
    fileprivate func isBookmark(member: Member) -> Bool {
        
        UserDefaultManager.bookmark.contains(member)
        ? true
        : false
    }
}

 

 

셀은 아래처럼 구성했다.

 

configureCell(with: Member)에서 Member 객체를 받아서 프로퍼티로 따로 저장하고 있다. 

이 정보를 이용해 bookmark를 관리!

//
//  MemberCell.swift
//  AppleCollectionView
//
//  Created by Hamlit Jason on 2022/08/23.
//

import UIKit
import SnapKit
import Nuke
import RxSwift
import RxCocoa

class MemberCell: UICollectionViewCell {
    let disposeBag = DisposeBag()
    
    var avatarImage = UIImageView()
    var nameLabel = UILabel()
    var jobLabel = UILabel()
    var bookmarkButton = UIButton()
    
    var member: Member?
    
    override init(frame: CGRect) {
        super.init(frame: .zero)
        
        setupAvatarImage()
        setupNameLabel()
        setupJobLabel()
        setupBookmarkButton()
        
        bind()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    /// 셀 세팅
    func configureCell(with item: Member) {
        member = item
        
        if let url = URL(string: item.avatar) {
            let options = ImageLoadingOptions(failureImageTransition: .fadeIn(duration: 0.5))
            Nuke.loadImage(
                with: url,
                options: options,
                into: avatarImage
            )
        }
        nameLabel.text = item.name
        jobLabel.text = item.job
        
        if item.isBookmark {
            bookmarkButton.setImage(UIImage(systemName: "heart.fill"), for: .normal)
        } else {
            bookmarkButton.setImage(UIImage(systemName: "heart"), for: .normal)
        }
    }
    
    func bind() {
        bookmarkButton.rx.tap
            .withUnretained(self)
            .bind { owner, _ in
                owner.updateBookmark(with: owner.member!)
            }
            .disposed(by: disposeBag)
    }
    
    /// 북마크 업데이트
    private func updateBookmark(with member: Member) {
        
        // 1. 로컬데이터 정보 열어보기 - Set타입
        var value = UserDefaultManager.bookmark
        
        // 2. value값 업데이트 - Set이라 이미 있으면 반영안돼
        if value.contains(member) {
            // 2-1. 이미 북마크 했던 값이라면,
            
            // 2-1-1. 북마크 안된 상태의 UI반영
            bookmarkButton.setImage(UIImage(systemName: "heart"), for: .normal)
            // 2-1-2. value값 변경 - 기존의 값 삭제
            value.remove(member)
        } else {
            // 2-2. 새로 북마크한 값이라면,
            
            // 2-2-1. 북마크한 상태로 UI변경
            bookmarkButton.setImage(UIImage(systemName: "heart.fill"), for: .normal)
            // 2-2-2. value값 변경 - 값 추가
            value.insert(member)
        }
        
        // 3. 로컬에 새로운 값 저장
        UserDefaultManager.bookmark = value
        print("값을 확인해 볼까요? \(UserDefaultManager.bookmark.count)")
    }
}

extension MemberCell {
    private func setupAvatarImage() {
        addSubview(avatarImage)
        avatarImage.layer.cornerRadius = 12
        avatarImage.backgroundColor = .gray
        
        avatarImage.snp.makeConstraints {
            $0.top.leading.equalToSuperview().inset(12)
            $0.width.height.equalTo(80)
        }
    }
    
    private func setupNameLabel() {
        addSubview(nameLabel)
        
        nameLabel.adjustsFontSizeToFitWidth = true
        nameLabel.snp.makeConstraints {
            $0.top.equalTo(avatarImage)
            $0.leading.equalTo(avatarImage.snp.trailing).offset(10)
            $0.trailing.equalToSuperview().inset(10)
        }
    }
    
    private func setupJobLabel() {
        addSubview(jobLabel)
        jobLabel.font = .systemFont(ofSize: 16, weight: .medium)
        jobLabel.textColor = .purple
        jobLabel.numberOfLines = 0
        jobLabel.textAlignment = .left
        
        jobLabel.snp.makeConstraints {
            $0.top.equalTo(nameLabel.snp.bottom)
            $0.leading.equalTo(nameLabel)
            $0.trailing.equalToSuperview().inset(10)
        }
    }
    
    private func setupBookmarkButton() {
        addSubview(bookmarkButton)
        
        bookmarkButton.setImage(UIImage(systemName: "heart"), for: .normal)
        bookmarkButton.snp.makeConstraints {
            $0.centerY.equalToSuperview()
            $0.trailing.equalToSuperview().inset(25)
        }
    }
}